web-dev-qa-db-fra.com

Comment définir les valeurs par défaut dans Rails?

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?

104
biagidp

"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.

95
SFEley

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
56
J-_-L

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.

20
Ian Purton

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

change_column_default Rails de l'API

13
Sebastiaan Pouyet

Si vous faites référence à des objets ActiveRecord, vous avez (plus de) deux façons de procéder:

1. Utilisez un paramètre: default dans la base de données.

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

2. Utiliser un rappel

PAR EXEMPLE. before_validation_on_create

Plus d'infos ici: http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html#M002147

7
Vlad Zloteanu

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 ...

EDIT - utilisez 'new_record?' vérifier si instancier à partir d'un nouvel appel

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.

7
erroric

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.

5
Silasj

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
4
Paulo Fidalgo

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
1
pj.martorell

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.

1
paulthenerd

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 .

1
peterhurford

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.]

1
James Moore

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
0
Orlando

Vous pouvez utiliser la gemme Rails_default_value. par exemple:

class Foo < ActiveRecord::Base
  # ...
  default :bar => 'some default value'
  # ...
end

https://github.com/keithrowell/Rails_default_value

0
Keith Rowell

Si vous parlez d'objets ActiveRecord, j'utilise le joyau 'attribut-defaults'.

Documentation et téléchargement: https://github.com/bsm/attribute-defaults

0
aidan