Je veux créer une valeur par défaut pour un attribut en le définissant dans ActiveRecord. Par défaut, chaque fois que l'enregistrement est créé, je souhaite définir une valeur par défaut pour l'attribut :status
. J'ai essayé de faire ceci:
class Task < ActiveRecord::Base
def status=(status)
status = 'P'
write_attribute(:status, status)
end
end
Mais lors de la création, je récupère toujours cette erreur de la base de données:
ActiveRecord::StatementInvalid: Mysql::Error: Column 'status' cannot be null
Par conséquent, je suppose que la valeur n'a pas été appliquée à l'attribut.
Quelle serait la manière élégante de faire cela dans Rails?
Merci beaucoup.
Vous pouvez définir une option par défaut pour la colonne dans la migration.
....
add_column :status, :string, :default => "P"
....
OR
Vous pouvez utiliser un rappel, before_save
class Task < ActiveRecord::Base
before_save :default_values
def default_values
self.status ||= 'P' # note self.status = 'P' if self.status.nil? might be safer (per @frontendbeauty)
end
end
Étant donné que j'ai rencontré ce problème il y a peu de temps et que les options pour Rails 3.0 sont légèrement différentes, je vais fournir une autre réponse à cette question.
Dans Rails 3.0, vous voulez faire quelque chose comme ceci:
class MyModel < ActiveRecord::Base
after_initialize :default_values
private
def default_values
self.name ||= "default value"
end
end
Lorsque j'ai besoin de valeurs par défaut, c'est généralement pour les nouveaux enregistrements avant que la vue de la nouvelle action ne soit rendue. La méthode suivante définit les valeurs par défaut pour les nouveaux enregistrements uniquement afin qu'elles soient disponibles lors du rendu des formulaires. before_save
et before_create
trop tard et ne fonctionnera pas si vous voulez que les valeurs par défaut apparaissent dans les champs de saisie.
after_initialize do
if self.new_record?
# values will be available for new record forms.
self.status = 'P'
self.featured = true
end
end
Vous pouvez le faire sans écrire de code du tout :) Il vous suffit de définir la valeur par défaut de la colonne dans la base de données. Vous pouvez le faire dans vos migrations. Par exemple:
create_table :projects do |t|
t.string :status, :null => false, :default => 'P'
...
t.timestamps
end
J'espère que ça t'as aidé.
La solution dépend de quelques choses.
La valeur par défaut dépend-elle d'autres informations disponibles au moment de la création? Pouvez-vous effacer la base de données avec des conséquences minimes?
Si vous avez répondu oui à la première question, vous souhaitez utiliser la solution de Jim
Si vous avez répondu oui à la deuxième question, vous voulez utiliser la solution de Daniel
Si vous avez répondu non aux deux questions, vous feriez probablement mieux d'ajouter et d'exécuter une nouvelle migration.
class AddDefaultMigration < ActiveRecord::Migration
def self.up
change_column :tasks, :status, :string, :default => default_value, :null => false
end
end
: string peut être remplacé par tout type reconnu par ActiveRecord :: Migration.
Le processeur étant bon marché, la redéfinition de Task dans la solution de Jim ne causera pas beaucoup de problèmes. Surtout dans un environnement de production. Cette migration est une bonne façon de procéder car elle est chargée et appelée beaucoup moins souvent.
Je envisagerais d'utiliser les attr_defaults trouvés ici . Vos rêves les plus fous deviendront réalité.
Juste renforcer réponse de Jim
En utilisant présence on peut faire
class Task < ActiveRecord::Base
before_save :default_values
def default_values
self.status = status.presence || 'P'
end
end
Pour les types de colonne, Rails prend en charge la boîte de dialogue - comme la chaîne de cette question -, la meilleure approche consiste à définir la colonne par défaut dans la base de données, comme indiqué par Daniel Kristensen. Rails introspectera la base de données et initialisera l'objet en conséquence. De plus, cela sécurise votre base de données en ajoutant une ligne en dehors de votre application Rails et en oubliant d’initialiser cette colonne.
Pour les types de colonne, Rails ne prend pas en charge les opérations par défaut - par exemple. Les colonnes ENUM - Rails ne pourront pas introspecter la valeur par défaut de la colonne. Dans ce cas, vous pas voulez utiliser after_initialize (il est appelé chaque fois qu'un objet est chargé depuis la base de données, ainsi que chaque fois qu'un objet est créé à l'aide de .new), before_create (car il survient après validation), ou before_save (car cela se produit également lors de la mise à jour, ce qui n’est généralement pas ce que vous voulez).
Au lieu de cela, vous voulez définir l'attribut dans une before_validation sur: create, comme suit:
before_validation :set_status_because_Rails_cannot, on: :create
def set_status_because_Rails_cannot
self.status ||= 'P'
end
Selon moi, deux problèmes doivent être résolus lorsque vous avez besoin d'une valeur par défaut.
Voici ma solution:
# the reader providers a default if nil
# but this wont work when saved
def status
read_attribute(:status) || "P"
end
# so, define a before_validation callback
before_validation :set_defaults
protected
def set_defaults
# if a non-default status has been assigned, it will remain
# if no value has been assigned, the reader will return the default and assign it
# this keeps the default logic DRY
status = status
end
J'aimerais savoir pourquoi les gens pensent à cette approche.
J'ai trouvé un meilleur moyen de le faire maintenant:
def status=(value)
self[:status] = 'P'
end
Dans Ruby, un appel de méthode est autorisé à ne pas avoir de parenthèses. Par conséquent, je dois nommer la variable locale en quelque chose d'autre, sinon Ruby le reconnaîtra comme un appel de méthode.