Comment puis-je définir des valeurs par défaut dans ActiveRecord?

417

Comment puis-je définir la valeur par défaut dans ActiveRecord?

Je vois un article de Pratik qui décrit un morceau de code laid et compliqué: http://m.onkey.org/2007/7/24/how-to-set-default-values-in-your-model

class Item < ActiveRecord::Base  
  def initialize_with_defaults(attrs = nil, &block)
    initialize_without_defaults(attrs) do
      setter = lambda { |key, value| self.send("#{key.to_s}=", value) unless
        !attrs.nil? && attrs.keys.map(&:to_s).include?(key.to_s) }
      setter.call('scheduler_type', 'hotseat')
      yield self if block_given?
    end
  end
  alias_method_chain :initialize, :defaults
end

J'ai vu les exemples suivants googler autour:

  def initialize 
    super
    self.status = ACTIVE unless self.status
  end

et

  def after_initialize 
    return unless new_record?
    self.status = ACTIVE
  end

J'ai également vu des gens le mettre dans leur migration, mais je préfère le voir défini dans le code du modèle.

Existe-t-il un moyen canonique de définir la valeur par défaut des champs dans le modèle ActiveRecord?

Ryw
la source
On dirait que vous avez répondu à la question vous-même, dans deux variantes différentes :)
Adam Byrtek
19
Notez que l'idiome Ruby "standard" pour 'self.status = ACTIVE sauf si self.status' est 'self.status || = ACTIVE'
Mike Woodhouse
1
La réponse de Jeff Perrin est bien meilleure que celle actuellement marquée comme acceptée. default_scope est une solution inacceptable pour définir des valeurs par défaut, car il a l'énorme effet secondaire de changer également le comportement des requêtes.
lawrence
voir aussi stackoverflow.com/questions/3975161/…
Viktor Trón
2
étant donné tous les votes positifs à cette question, je dirais que Ruby a besoin d'une méthode setDefaultValue pour ActiveRecord
spartikus

Réponses:

557

Il existe plusieurs problèmes avec chacune des méthodes disponibles, mais je pense que la définition d'un after_initializerappel est la voie à suivre pour les raisons suivantes:

  1. default_scopeinitialisera les valeurs des nouveaux modèles, mais cela deviendra alors l'étendue sur laquelle vous trouverez le modèle. Si vous voulez simplement initialiser certains nombres à 0, ce n'est pas ce que vous voulez.
  2. La définition des valeurs par défaut dans votre migration fonctionne également une partie du temps ... Comme cela a déjà été mentionné, cela ne fonctionnera pas lorsque vous appelez simplement Model.new.
  3. Le remplacement initializepeut fonctionner, mais n'oubliez pas d'appeler super!
  4. Utiliser un plugin comme Phusion est un peu ridicule. C'est ruby, avons-nous vraiment besoin d'un plugin juste pour initialiser certaines valeurs par défaut?
  5. Le remplacement after_initialize est obsolète à partir de Rails 3. Lorsque je remplace after_initializedans rails 3.0.3, j'obtiens l'avertissement suivant dans la console:

AVERTISSEMENT DEPRECATION: Base # after_initialize a été dépréciée, veuillez utiliser la méthode Base.after_initialize: à la place. (appelé depuis / Users / me / myapp / app / models / my_model: 15)

Par conséquent, je dirais écrire un after_initializerappel, qui vous permet de définir des attributs par défaut en plus de vous permettre de définir des valeurs par défaut sur des associations telles que:

  class Person < ActiveRecord::Base
    has_one :address
    after_initialize :init

    def init
      self.number  ||= 0.0           #will set the default value only if it's nil
      self.address ||= build_address #let's you set a default association
    end
  end    

Maintenant, vous n'avez qu'un seul endroit où chercher pour l'initialisation de vos modèles. J'utilise cette méthode jusqu'à ce que quelqu'un en trouve une meilleure.

Mises en garde:

  1. Pour les champs booléens, faites:

    self.bool_field = true if self.bool_field.nil?

    Voir le commentaire de Paul Russell sur cette réponse pour plus de détails

  2. Si vous ne sélectionnez qu'un sous-ensemble de colonnes pour un modèle (c'est-à-dire, en l'utilisant selectdans une requête comme Person.select(:firstname, :lastname).all), vous obtiendrez un MissingAttributeErrorsi votre initméthode accède à une colonne qui n'a pas été incluse dans la selectclause. Vous pouvez vous prémunir contre ce cas comme ceci:

    self.number ||= 0.0 if self.has_attribute? :number

    et pour une colonne booléenne ...

    self.bool_field = true if (self.has_attribute? :bool_value) && self.bool_field.nil?

    Notez également que la syntaxe est différente avant Rails 3.2 (voir le commentaire de Cliff Darling ci-dessous)

Jeff Perrin
la source
7
Cela semble certainement être le meilleur moyen d'y parvenir. Ce qui est vraiment étrange et malheureux. Une méthode préférée pour établir les valeurs par défaut des attributs de modèle lors de la création semble être quelque chose que Rails devrait déjà avoir intégré. La seule autre manière (fiable), prioritaire initialize, semble vraiment compliquée pour quelque chose qui devrait être clair et bien défini. J'ai passé des heures à parcourir la documentation avant de chercher ici parce que je supposais que cette fonctionnalité était déjà là quelque part et je n'en avais tout simplement pas conscience.
seaneshbaugh
106
Une remarque à ce sujet - si vous avez un champ booléen que vous souhaitez par défaut, ne le faites pas self.bool_field ||= true, car cela forcera le champ à true même si vous l'initialisez explicitement à false. Au lieu de cela self.bool_field = true if self.bool_field.nil?.
Paul Russell
2
En ce qui concerne le point # 2, Model.new fonctionne réellement (uniquement pour moi?) Avec les valeurs par défaut définies dans les migrations, ou plus exactement avec les valeurs par défaut des colonnes de table. Mais je reconnais que la méthode de Jeff basée sur le rappel after_initialize est probablement la meilleure façon de le faire. Juste une question: cela fonctionne-t-il avec des objets sales mais non enregistrés? Dans votre exemple, Person.new.number_was renverra-t-il 0,0?
Laurent Farcy
21
Attention lorsque vous utilisez cette approche combinée à la sélection de colonnes spécifiques avec un enregistrement actif. Dans ce cas, seuls les attributs spécifiés dans la requête seront trouvés dans l'objet et le code init lancera a MissingAttributeError. Vous pouvez ajouter un contrôle supplémentaire comme indiqué: self.number ||= 0.0 if self.has_attribute? :number Pour booléens: self.bool_field = true if (self.has_attribute? :bool_value) && self.bool_field.nil?. Il s'agit de Rails 3.2+ - pour une utilisation antérieure, self.attributes.has_key?vous devez utiliser une chaîne au lieu d'un symbole.
Cliff Darling
6
Faire cela avec des associations chargera ces associations lors de la recherche. Commencez initializepar return if !new_record?pour éviter les problèmes de performances.
Kyle Macey
68

Rails 5+

Vous pouvez utiliser la méthode d' attribut dans vos modèles, par exemple:

class Account < ApplicationRecord
  attribute :locale, :string, default: 'en'
end

Vous pouvez également passer un lambda au default paramètre. Exemple:

attribute :uuid, UuidType.new, default: -> { SecureRandom.uuid }
Lucas Caton
la source
3
ahhhhh c'est le joyau que je cherchais! par défaut peut également prendre un proc, par exemple par défaut: -> {Time.current.to_date}
schpet
1
Assurez-vous de spécifier le type comme deuxième argument, sinon le type sera Valueet aucun transtypage ne sera effectué.
null
à mon grand plaisir, cela fonctionne également avec store_accessor, par exemple étant donné que store_accessor :my_jsonb_column, :localevous pouvez ensuite définirattribute :locale, :string, default: 'en'
Ryan Romanchuk
Oh c'est fantastique, j'avais besoin de valeurs par défaut pour montrer sous une forme et cela fonctionne très bien. Merci Lucas.
Paul Watson
On peut toujours les régler sur nil. S'ils ne peuvent pas être nilDB not null+ DB par défaut + github.com/sshaw/keep_defaults sont le chemin à parcourir à partir de mon expérience
sshaw
47

Nous plaçons les valeurs par défaut dans la base de données lors des migrations (en spécifiant l' :defaultoption sur chaque définition de colonne) et laissons Active Record utiliser ces valeurs pour définir la valeur par défaut pour chaque attribut.

À mon humble avis, cette approche est alignée sur les principes de la réalité augmentée: convention sur la configuration, SEC, la définition de la table pilote le modèle, et non l'inverse.

Notez que les valeurs par défaut sont toujours dans le code d'application (Ruby), mais pas dans le modèle mais dans la ou les migrations.

Laurent Farcy
la source
3
Un autre problème est lorsque vous souhaitez une valeur par défaut pour une clé étrangère. Vous ne pouvez pas coder en dur une valeur d'ID dans le champ de clé étrangère car sur différents DB, l'ID peut être différent.
shmichael
2
un autre problème est que, de cette façon, vous ne pouvez pas initialiser les accesseurs non persistants (attributs qui ne sont pas des colonnes db).
Viktor Trón
2
un autre problème est que vous ne pouvez pas nécessairement voir toutes les valeurs par défaut au même endroit. ils peuvent être dispersés à travers différentes migrations.
declan
8
declan, il y a db / schema.rb
Benjamin Atkin
6
Je voudrais mentionner pour les futurs lecteurs: au moins d'après ce que j'ai lu, cela est contraire aux principes de la RA. La logique des modèles doit reposer sur les classes de modèles et les bases de données doivent être aussi ignorantes que possible. Les valeurs par défaut constituent pour moi une logique spécifique à propos d'un modèle.
darethas
40

Certains cas simples peuvent être traités en définissant une valeur par défaut dans le schéma de base de données, mais cela ne gère pas un certain nombre de cas plus délicats, y compris les valeurs calculées et les clés d'autres modèles. Pour ces cas, je fais ceci:

after_initialize :defaults

def defaults
   unless persisted?
    self.extras||={}
    self.other_stuff||="This stuff"
    self.assoc = [OtherModel.find_by_name('special')]
  end
end

J'ai décidé d'utiliser l'after_initialize mais je ne veux pas qu'il soit appliqué aux objets qui ne sont trouvés que ceux nouveaux ou créés. Je pense qu'il est presque choquant qu'un rappel after_new ne soit pas fourni pour ce cas d'utilisation évident, mais je l'ai fait en confirmant si l'objet est déjà persisté indiquant qu'il n'est pas nouveau.

Après avoir vu la réponse de Brad Murray, cela est encore plus propre si la condition est déplacée vers la demande de rappel:

after_initialize :defaults, unless: :persisted?
              # ":if => :new_record?" is equivalent in this context

def defaults
  self.extras||={}
  self.other_stuff||="This stuff"
  self.assoc = [OtherModel.find_by_name('special')]
end
Joseph Lord
la source
4
C'est un point vraiment important. Je dois imaginer que dans la plupart des cas, la définition de la valeur par défaut sur un enregistrement ne doit être effectuée qu'avant la persistance d'un nouvel enregistrement, pas lors du chargement d'un enregistrement persistant.
Russell Silva
Thx mec, tu as sauvé ma journée.
stephanfriedrich
2
Et alors :before_create?
Franklin Yu
Comment: before_create gère-t-il les appels séparés nouveaux et enregistrés? Je voudrais vérifier cela et vraiment comprendre avant de passer à cela.
Joseph Lord
17

Le modèle de rappel after_initialize peut être amélioré en procédant simplement comme suit

after_initialize :some_method_goes_here, :if => :new_record?

Cela présente un avantage non trivial si votre code init doit traiter avec des associations, car le code suivant déclenche un subtil n + 1 si vous lisez l'enregistrement initial sans inclure celui associé.

class Account

  has_one :config
  after_initialize :init_config

  def init_config
    self.config ||= build_config
  end

end
Brad Murray
la source
16

Les gars de Phusion ont un joli plugin pour ça.

Milan Novota
la source
Remarque, ce plugin permet aux :defaultvaleurs des migrations de schéma de `` fonctionner simplement '' avec Model.new.
jchook
Je peux faire en sorte que les :defaultvaleurs des migrations `` fonctionnent '' avec Model.new, contrairement à ce que Jeff a dit dans son article. Travail vérifié dans Rails 4.1.16.
Magne
8

J'utilise le attribute-defaultsbijou

À partir de la documentation: exécutez sudo gem install attribute-defaultset ajoutez require 'attribute_defaults'à votre application.

class Foo < ActiveRecord::Base
  attr_default :age, 18
  attr_default :last_seen do
    Time.now
  end
end

Foo.new()           # => age: 18, last_seen => "2014-10-17 09:44:27"
Foo.new(:age => 25) # => age: 25, last_seen => "2014-10-17 09:44:28"
aidan
la source
7

Des questions similaires, mais toutes ont un contexte légèrement différent: - Comment créer une valeur par défaut pour les attributs dans le modèle de Rails activerecord?

Meilleure réponse: dépend de ce que vous voulez!

Si vous voulez que chaque objet commence par une valeur: utilisezafter_initialize :init

Vous souhaitez que le new.htmlformulaire ait une valeur par défaut à l'ouverture de la page? utilisez https://stackoverflow.com/a/5127684/1536309

class Person < ActiveRecord::Base
  has_one :address
  after_initialize :init

  def init
    self.number  ||= 0.0           #will set the default value only if it's nil
    self.address ||= build_address #let's you set a default association
  end
  ...
end 

Si vous voulez que chaque objet ait une valeur calculée à partir de l'entrée utilisateur: utilisezbefore_save :default_values Vous voulez que l'utilisateur entreXet ensuiteY = X+'foo'? utilisation:

class Task < ActiveRecord::Base
  before_save :default_values
  def default_values
    self.status ||= 'P'
  end
end
Blair Anderson
la source
4

C'est à ça que servent les constructeurs! Remplacez la initializeméthode du modèle .

Utilisez la after_initializeméthode.

John Topley
la source
2
Normalement, vous auriez raison, mais vous ne devriez jamais remplacer l'initialisation dans un modèle ActiveRecord car il pourrait ne pas toujours être appelé. Vous devez utiliser la after_initializeméthode à la place.
Luke Redpath
L'utilisation d'un default_scope juste pour définir un défaut est CERTAINEMENT erronée. after_initialize est la bonne réponse.
joaomilho
4

Les gars Sup, j'ai fini par faire ce qui suit:

def after_initialize 
 self.extras||={}
 self.other_stuff||="This stuff"
end

Fonctionne comme un charme!

Tony
la source
3

Cela a été répondu depuis longtemps, mais j'ai souvent besoin de valeurs par défaut et je préfère ne pas les mettre dans la base de données. Je crée une DefaultValuespréoccupation:

module DefaultValues
  extend ActiveSupport::Concern

  class_methods do
    def defaults(attr, to: nil, on: :initialize)
      method_name = "set_default_#{attr}"
      send "after_#{on}", method_name.to_sym

      define_method(method_name) do
        if send(attr)
          send(attr)
        else
          value = to.is_a?(Proc) ? to.call : to
          send("#{attr}=", value)
        end
      end

      private method_name
    end
  end
end

Et puis utilisez-le dans mes modèles comme ceci:

class Widget < ApplicationRecord
  include DefaultValues

  defaults :category, to: 'uncategorized'
  defaults :token, to: -> { SecureRandom.uuid }
end
clem
la source
3

J'ai également vu des gens le mettre dans leur migration, mais je préfère le voir défini dans le code du modèle.

Existe-t-il un moyen canonique de définir la valeur par défaut des champs dans le modèle ActiveRecord?

La manière canonique de Rails, avant Rails 5, était en fait de le définir dans la migration, et de simplement chercher dans le db/schema.rbpour chaque fois que vous voulez voir quelles valeurs par défaut sont définies par la base de données pour n'importe quel modèle.

Contrairement à ce que dit @Jeff Perrin (qui est un peu ancien), l'approche de la migration appliquera même la valeur par défaut lors de l'utilisation Model.new, en raison de la magie de Rails. Travail vérifié dans Rails 4.1.16.

La chose la plus simple est souvent la meilleure. Moins de dette de connaissances et de points de confusion potentiels dans la base de code. Et cela «fonctionne tout simplement».

class AddStatusToItem < ActiveRecord::Migration
  def change
    add_column :items, :scheduler_type, :string, { null: false, default: "hotseat" }
  end
end

Ou, pour un changement de colonne sans en créer un nouveau, effectuez l'une des opérations suivantes:

class AddStatusToItem < ActiveRecord::Migration
  def change
    change_column_default :items, :scheduler_type, "hotseat"
  end
end

Ou peut-être encore mieux:

class AddStatusToItem < ActiveRecord::Migration
  def change
    change_column :items, :scheduler_type, :string, default: "hotseat"
  end
end

Consultez le guide RoR officiel pour les options de méthodes de changement de colonne.

Le null: falsen'autorise pas les valeurs NULL dans la base de données et, comme avantage supplémentaire, il est également mis à jour afin que tous les enregistrements de base de données préexistants qui étaient auparavant nuls soient également définis avec la valeur par défaut pour ce champ. Vous pouvez exclure ce paramètre dans la migration si vous le souhaitez, mais je l'ai trouvé très pratique!

La voie canonique dans Rails 5+ est, comme l'a dit @Lucas Caton:

class Item < ActiveRecord::Base
  attribute :scheduler_type, :string, default: 'hotseat'
end
Magne
la source
1

Le problème avec les solutions after_initialize est que vous devez ajouter un after_initialize à chaque objet que vous recherchez hors de la base de données, que vous accédiez ou non à cet attribut. Je suggère une approche paresseuse.

Les méthodes d'attribut (getters) sont bien sûr des méthodes elles-mêmes, vous pouvez donc les remplacer et fournir une valeur par défaut. Quelque chose comme:

Class Foo < ActiveRecord::Base
  # has a DB column/field atttribute called 'status'
  def status
    (val = read_attribute(:status)).nil? ? 'ACTIVE' : val
  end
end

À moins que, comme quelqu'un l'a souligné, vous devez faire Foo.find_by_status ('ACTIVE'). Dans ce cas, je pense que vous auriez vraiment besoin de définir la valeur par défaut dans vos contraintes de base de données, si la base de données le prend en charge.

Jeff Gran
la source
Cette solution et l'alternative proposée ne fonctionnent pas dans mon cas: j'ai une hiérarchie de classes STI où une seule classe a cet attribut, et la colonne correspondante qui sera utilisée dans les conditions de requête DB.
cmoran92
1

J'ai rencontré des problèmes pour after_initializedonner des ActiveModel::MissingAttributeErrorerreurs lors de recherches complexes:

par exemple:

@bottles = Bottle.includes(:supplier, :substance).where(search).order("suppliers.name ASC").paginate(:page => page_no)

"recherche" dans le .wherehachage de conditions

J'ai donc fini par le faire en remplaçant initialize de cette façon:

def initialize
  super
  default_values
end

private
 def default_values
     self.date_received ||= Date.current
 end

L' superappel est nécessaire pour s'assurer que l'objet s'initialise correctement ActiveRecord::Baseavant de faire mon code de personnalisation, ie: default_values

Sean
la source
Je l'aime. Je devais le faire def initialize(*); super; default_values; enddans Rails 5.2.0. De plus, la valeur par défaut est disponible, même dans le .attributeshachage.
spyle
1
class Item < ActiveRecord::Base
  def status
    self[:status] or ACTIVE
  end

  before_save{ self.status ||= ACTIVE }
end
Mike Breen
la source
2
Mmmhh ... semble ingénieux au début, mais après y avoir réfléchi un peu, je vois quelques problèmes. Tout d'abord, toutes les valeurs par défaut ne sont pas en un seul point, mais dispersées dans la classe (imaginez les rechercher ou les modifier). Deuxième et pire, vous ne pouvez pas mettre, plus tard, une valeur nulle (ou même fausse!).
paradoja
pourquoi auriez-vous besoin de définir une valeur nulle par défaut? vous obtenez cela hors de la boîte avec AR sans rien faire du tout. Quant à false lorsque vous utilisez une colonne booléenne, vous avez raison, ce n'est pas la meilleure approche.
Mike Breen
Je ne peux pas parler pour les autres habitudes de codage Je n'ai pas eu de problème parce que je ne disperse pas mes getters / setters autour d'un fichier de classe. De plus, tout éditeur de texte moderne devrait faciliter la navigation vers une méthode (shift-cmd-t dans textmate).
Mike Breen
@paradoja - Je reprends cela, je vois maintenant où ça tombe en panne en utilisant également null. Pas nécessairement en utilisant null comme valeur par défaut mais si vous vouliez réellement changer la valeur en null à un moment donné. Bonne prise @paradoja, merci.
Mike Breen
J'utilise cette méthode car elle fonctionne bien avec les attributs générés dynamiquement.
Dale Campbell
0

Bien que faire cela pour définir des valeurs par défaut soit déroutant et gênant dans la plupart des cas, vous pouvez également l'utiliser :default_scope. Découvrez le commentaire de Squil ici .

skalee
la source
0

La méthode after_initialize est obsolète, utilisez plutôt le rappel.

after_initialize :defaults

def defaults
  self.extras||={}
  self.other_stuff||="This stuff"
end

cependant, utiliser : default dans vos migrations est toujours le moyen le plus propre.

Greg
la source
4
Dans Rails 3: la after_initializeméthode n'est PAS obsolète . En fait, le rappel de style macro vous donne un exemple de SI obsolète . Détails: guides.rubyonrails.org/…
Zabba
0

J'ai trouvé que l'utilisation d'une méthode de validation fournit beaucoup de contrôle sur la définition des valeurs par défaut. Vous pouvez même définir des valeurs par défaut (ou échouer la validation) pour les mises à jour. Vous définissez même une valeur par défaut différente pour les insertions et les mises à jour si vous le souhaitez vraiment. Notez que la valeur par défaut ne sera pas définie tant que #valid? est appelé.

class MyModel
  validate :init_defaults

  private
  def init_defaults
    if new_record?
      self.some_int ||= 1
    elsif some_int.nil?
      errors.add(:some_int, "can't be blank on update")
    end
  end
end

Concernant la définition d'une méthode after_initialize, il pourrait y avoir des problèmes de performances car after_initialize est également appelé par chaque objet retourné par: find: http://guides.rubyonrails.org/active_record_validations_callbacks.html#after_initialize-and-after_find

Kelvin
la source
la validation ne se produit-elle qu'avant l'enregistrement? Et si vous vouliez afficher les valeurs par défaut avant d'enregistrer?
nurettin
@nurettin C'est un bon point et je peux voir pourquoi vous le voulez parfois, mais le PO n'a pas mentionné cela comme une exigence. Vous devez décider vous-même si vous voulez la surcharge de définition des valeurs par défaut sur chaque instance, même si elle n'est pas enregistrée. L'alternative est de garder un objet factice autour pour que l' newaction soit réutilisée.
Kelvin
0

Si la colonne se trouve être une colonne de type «état» et que votre modèle se prête à l'utilisation de machines à états, envisagez d'utiliser la gemme aasm , après quoi vous pouvez simplement faire

  aasm column: "status" do
    state :available, initial: true
    state :used
    # transitions
  end

Il initn'initialise toujours pas la valeur des enregistrements non enregistrés, mais c'est un peu plus propre que de rouler le vôtre avec ou autre chose, et vous récoltez les autres avantages d'un aasm tels que les étendues pour tous vos statuts.

Mauvaise Demande
la source
0

Je suggère fortement d'utiliser le joyau "default_value_for": https://github.com/FooBarWidget/default_value_for

Il existe des scénarios délicats qui nécessitent à peu près la priorité sur la méthode d'initialisation, ce que fait ce joyau.

Exemples:

Votre valeur par défaut db est NULL, votre valeur par défaut définie par le modèle / ruby ​​est "une chaîne", mais vous voulez réellement définir la valeur sur nil pour une raison quelconque:MyModel.new(my_attr: nil)

La plupart des solutions ici ne parviendront pas à définir la valeur à zéro et la définiront par défaut.

OK, alors au lieu de prendre l' ||=approche, vous passez à my_attr_changed?...

MAIS imaginez maintenant que votre valeur par défaut db est "une chaîne", votre valeur par défaut définie par le modèle / ruby ​​est "une autre chaîne", mais dans un certain scénario, vous souhaitez définir la valeur sur "une chaîne" (la valeur par défaut db):MyModel.new(my_attr: 'some_string')

Cela se traduira par my_attr_changed?être faux car la valeur correspond à la valeur par défaut db, qui à son tour déclenchera votre code par défaut défini en rubis et définira la valeur sur "une autre chaîne" - encore une fois, pas ce que vous vouliez.


Pour ces raisons, je ne pense pas que cela puisse être accompli correctement avec juste un hook after_initialize.

Encore une fois, je pense que le joyau "default_value_for" adopte la bonne approche: https://github.com/FooBarWidget/default_value_for

etipton
la source
0

Voici une solution que j'ai utilisée et qui m'a un peu surpris n'a pas encore été ajoutée.

Il y a deux parties. La première partie définit la valeur par défaut dans la migration réelle, et la deuxième partie ajoute une validation dans le modèle garantissant que la présence est vraie.

add_column :teams, :new_team_signature, :string, default: 'Welcome to the Team'

Vous verrez donc ici que la valeur par défaut est déjà définie. Maintenant, dans la validation, vous voulez vous assurer qu'il y a toujours une valeur pour la chaîne, alors faites simplement

 validates :new_team_signature, presence: true

Cela vous permet de définir la valeur par défaut pour vous. (pour moi, j'ai "Bienvenue dans l'équipe"), puis cela ira un peu plus loin et s'assurera qu'il y a toujours une valeur présente pour cet objet.

J'espère que cela pourra aider!

kdweber89
la source
0
# db/schema.rb
create_table :store_listings, force: true do |t|
  t.string :my_string, default: "original default"
end

StoreListing.new.my_string # => "original default"

# app/models/store_listing.rb
class StoreListing < ActiveRecord::Base
  attribute :my_string, :string, default: "new default"
end

StoreListing.new.my_string # => "new default"

class Product < ActiveRecord::Base
  attribute :my_default_proc, :datetime, default: -> { Time.now }
end

Product.new.my_default_proc # => 2015-05-30 11:04:48 -0600
sleep 1
Product.new.my_default_proc # => 2015-05-30 11:04:49 -0600
shilovk
la source
-2

utiliser default_scope dans les rails 3

api doc

ActiveRecord masque la différence entre la définition par défaut définie dans la base de données (schéma) et la définition par défaut effectuée dans l'application (modèle). Lors de l'initialisation, il analyse le schéma de la base de données et note toutes les valeurs par défaut qui y sont spécifiées. Plus tard, lors de la création d'objets, il attribue les valeurs par défaut spécifiées au schéma sans toucher à la base de données.

discussion

Viktor Trón
la source
si vous utilisez meta_where, default_scope peut ne pas fonctionner pour attribuer des valeurs par défaut aux nouveaux objets AR en raison d'un bogue.
Viktor Trón
ce problème meta_where a maintenant été corrigé [ metautonomous.lighthouseapp.com/projects/53011/tickets/…
Viktor Trón
3
ne pas utiliser default_scope. Ainsi, toutes vos requêtes ajouteront cette condition au champ que vous avez défini. Ce n'est presque JAMAIS ce que vous voulez.
brad
@brad, drôle tu parles, je suis totalement d'accord, c'est mal :). voir mon commentaire dans stackoverflow.com/questions/10680845/… .
Viktor Trón
-3

Depuis les docs api http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html Utilisez la before_validationméthode dans votre modèle, elle vous donne les options de création d'une initialisation spécifique pour créer et mettre à jour des appels, par exemple dans cet exemple (encore une fois le code est pris à partir de l'exemple api docs) le champ numéro est initialisé pour une carte de crédit. Vous pouvez facilement l'adapter pour définir les valeurs que vous souhaitez

class CreditCard < ActiveRecord::Base
  # Strip everything but digits, so the user can specify "555 234 34" or
  # "5552-3434" or both will mean "55523434"
  before_validation(:on => :create) do
    self.number = number.gsub(%r[^0-9]/, "") if attribute_present?("number")
  end
end

class Subscription < ActiveRecord::Base
  before_create :record_signup

  private
    def record_signup
      self.signed_up_on = Date.today
    end
end

class Firm < ActiveRecord::Base
  # Destroys the associated clients and people when the firm is destroyed
  before_destroy { |record| Person.destroy_all "firm_id = #{record.id}"   }
  before_destroy { |record| Client.destroy_all "client_of = #{record.id}" }
end

Surpris que son n'a pas été suggéré ici

jamesc
la source
before_validation ne définira pas les valeurs par défaut tant que l'objet n'est pas prêt à persister. Si le processus doit lire les valeurs par défaut avant de persister, les valeurs ne seront pas prêtes.
mmell
De toute façon, vous ne définissez jamais de valeurs par défaut lors de la vérification des validations. Ce n'est même pas un Hack. Faites-le lors de l'initialisation
Sachin