J'essaie de trouver le meilleur moyen de définir les valeurs par défaut des objets dans Rails.
Le mieux que je puisse penser est de définir la valeur par défaut dans la méthode new
du contrôleur.
Est-ce que quelqu'un a des idées si cela est acceptable ou s'il y a une meilleure façon de le faire?
"Correct" est un mot dangereux en Ruby. Il y a généralement plus d'une façon de faire quoi que ce soit. Si vous savez que vous toujours voulez cette valeur par défaut pour cette colonne de la table, leur configuration dans un fichier de migration de base de données constitue le moyen le plus simple:
class SetDefault < ActiveRecord::Migration
def self.up
change_column :people, :last_name, :type, :default => "Doe"
end
def self.down
# You can't currently remove default values in Rails
raise ActiveRecord::IrreversibleMigration, "Can't remove the default"
end
end
Comme ActiveRecord découvre automatiquement les propriétés de votre table et de vos colonnes, le même paramètre par défaut sera défini dans tous les modèles l'utilisant dans n'importe quel standard Rails app.
Toutefois, si vous souhaitez uniquement définir les valeurs par défaut dans des cas spécifiques (par exemple, il s’agit d’un modèle hérité qui partage une table avec d’autres), une autre manière élégante consiste à le faire directement dans votre code Rails lorsque l'objet modèle est créé:
class GenericPerson < Person
def initialize(attributes=nil)
attr_with_defaults = {:last_name => "Doe"}.merge(attributes)
super(attr_with_defaults)
end
end
Ensuite, lorsque vous effectuez une GenericPerson.new()
, l'attribut "Doe" continue de couler jusqu'à Person.new()
, sauf si vous le remplacez par autre chose.
Sur la base de la réponse de SFEley, voici une version mise à jour/corrigée pour les nouveaux Rails versions:
class SetDefault < ActiveRecord::Migration
def change
change_column :table_name, :column_name, :type, default: "Your value"
end
end
Tout d’abord, vous ne pouvez pas surcharger initialize(*args)
car il n’est pas appelé dans tous les cas.
Votre meilleure option est de mettre vos valeurs par défaut dans votre migration:
add_column :accounts, :max_users, :integer, :default => 10
La deuxième solution consiste à définir les valeurs par défaut dans votre modèle, mais cela ne fonctionnera qu'avec des attributs initialement nuls. Vous pouvez avoir des problèmes, comme je l’ai fait avec les colonnes boolean
:
def after_initialize
if new_record?
max_users ||= 10
end
end
Vous avez besoin du new_record?
afin que les valeurs par défaut ne remplacent pas les valeurs chargées à partir de la base de données.
Vous avez besoin ||=
à arrêter Rails à partir des paramètres passés dans la méthode initialize.
Vous pouvez aussi essayer change_column_default
dans vos migrations (testé dans Rails 3.2.8):
class SetDefault < ActiveRecord::Migration
def up
# Set default value
change_column_default :people, :last_name, "Smith"
end
def down
# Remove default
change_column_default :people, :last_name, nil
end
end
Si vous faites référence à des objets ActiveRecord, vous avez (plus de) deux façons de procéder:
PAR EXEMPLE.
class AddSsl < ActiveRecord::Migration
def self.up
add_column :accounts, :ssl_enabled, :boolean, :default => true
end
def self.down
remove_column :accounts, :ssl_enabled
end
end
Plus d'infos ici: http://api.rubyonrails.org/classes/ActiveRecord/Migration.html
PAR EXEMPLE. before_validation_on_create
Plus d'infos ici: http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html#M002147
Dans Ruby sur Rails v3.2.8, en utilisant le after_initialize
Rappel ActiveRecord, vous pouvez appeler une méthode dans votre modèle qui affectera les valeurs par défaut pour un nouvel objet.
le rappel after_initialize est déclenché pour chaque objet trouvé et instancié par un Finder. after_initialize est également déclenché après l'instanciation de nouveaux objets ( voir ActiveRecord Callbacks ).
Donc, IMO ça devrait ressembler à quelque chose comme:
class Foo < ActiveRecord::Base
after_initialize :assign_defaults_on_new_Foo
...
attr_accessible :bar
...
private
def assign_defaults_on_new_Foo
# required to check an attribute for existence to weed out existing records
self.bar = default_value unless self.attribute_whose_presence_has_been_validated
end
end
Foo.bar = default_value
pour cette instance sauf si l’instance contient un attribute_whose_presence_has_been_validated
précédemment sur enregistrer/mettre à jour. Le default_value
sera alors utilisé conjointement avec votre vue pour restituer le formulaire à l’aide du default_value
pour l'attribut bar
.
Au mieux c'est hacky ...
Au lieu de vérifier une valeur d'attribut, utilisez le new_record?
méthode intégrée avec Rails. Ainsi, l'exemple ci-dessus devrait ressembler à:
class Foo < ActiveRecord::Base
after_initialize :assign_defaults_on_new_Foo, if: 'new_record?'
...
attr_accessible :bar
...
private
def assign_defaults_on_new_Foo
self.bar = default_value
end
end
C'est beaucoup plus propre. Ah, la magie de Rails - c'est plus intelligent que moi.
Pour les champs booléens dans Rails 3.2.6 au moins, cela fonctionnera dans votre migration.
def change
add_column :users, :eula_accepted, :boolean, default: false
end
Mettre un 1
ou 0
par défaut ne fonctionnera pas ici car il s'agit d'un champ booléen. Il doit s'agir d'une valeur true
ou false
.
Si vous avez affaire à un modèle, vous pouvez utiliser l'API Attriutes dans Rails 5+ http://api.rubyonrails.org/classes/ActiveRecord/Attributes/ClassMethods .html # method-i-attribut
ajoutez simplement une migration avec un nom de colonne approprié, puis définissez-la dans le modèle avec:
class StoreListing < ActiveRecord::Base
attribute :country, :string, default: 'PT'
end
Générez une migration et utilisez change_column_default
, est succinct et réversible:
class SetDefaultAgeInPeople < ActiveRecord::Migration[5.2]
def change
change_column_default :people, :age, { from: nil, to: 0 }
end
end
Si vous ne définissez que les valeurs par défaut pour certains attributs d'un modèle sauvegardé par une base de données, envisagez d'utiliser les valeurs de colonne SQL par défaut. Pouvez-vous préciser les types de valeurs par défaut que vous utilisez?
Il y a un certain nombre d'approches pour le gérer, ceci plugin ressemble à une option intéressante.
Un moyen potentiellement encore meilleur/plus propre que les réponses proposées est d’écraser l’accesseur, comme ceci:
def status
self['name_of_var'] || 'desired_default_value'
end
Voir "Remplacement des accesseurs par défaut" dans la documentation sur ActiveRecord :: Base et plus de StackOverflow sur l'utilisation de self .
Je devais définir une valeur par défaut comme si elle était spécifiée comme valeur de colonne par défaut dans la base de données. Alors ça se comporte comme ça
a = Item.new
a.published_at # => my default value
a = Item.new(:published_at => nil)
a.published_at # => nil
Comme callback after_initialize est appelé après la définition d'attributs à partir d'arguments, il était impossible de savoir si l'attribut est nil car il n'a jamais été défini ou s'il a été défini intentionnellement sur nil. Donc, j'ai dû fouiller un peu à l'intérieur et suis venu avec cette solution simple.
class Item < ActiveRecord::Base
def self.column_defaults
super.merge('published_at' => Time.now)
end
end
Ça marche bien pour moi. (Rails 3.2.x)
La suggestion de remplacer new/initialize est probablement incomplète. Rails appelera (fréquemment) allouer pour les objets ActiveRecord, et les appels pour allouer ne donneront pas lieu à des appels à initialiser.
Si vous parlez d'objets ActiveRecord, examinez la possibilité de remplacer after_initialize.
Ces articles de blog (pas les miens) sont utiles:
valeurs par défautconstructeurs par défaut non appelés
[Edit: SFEley souligne que Rails examine effectivement la valeur par défaut dans la base de données lorsqu'elle instancie un nouvel objet en mémoire - je ne m'en étais pas rendu compte.]
j'ai répondu à une question similaire ici .. une façon propre de le faire est d'utiliser Rails attr_accessor_with_default
class SOF
attr_accessor_with_default :is_awesome,true
end
sof = SOF.new
sof.is_awesome
=> true
[~ # ~] met à jour [~ # ~]
attr_accessor_with_default est obsolète dans Rails 3.2 .. vous pouvez le faire à la place avec pure Ruby
class SOF
attr_writer :is_awesome
def is_awesome
@is_awesome ||= true
end
end
sof = SOF.new
sof.is_awesome
#=> true
Vous pouvez utiliser la gemme Rails_default_value. par exemple:
class Foo < ActiveRecord::Base
# ...
default :bar => 'some default value'
# ...
end
Si vous parlez d'objets ActiveRecord, j'utilise le joyau 'attribut-defaults'.
Documentation et téléchargement: https://github.com/bsm/attribute-defaults