112 votes

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

J'essaie de trouver la meilleure façon de définir des valeurs par défaut pour les objets dans Rails.

La meilleure solution à laquelle je puisse penser est de définir la valeur par défaut dans le fichier new dans le contrôleur.

Quelqu'un peut-il nous dire si c'est acceptable ou s'il y a une meilleure façon de procéder ?

1 votes

Quels sont ces objets, comment sont-ils consommés / utilisés ? Sont-ils utilisés lors du rendu des vues ou pour la logique du contrôleur ?

3 votes

Si vous parlez d'un objet ActiveRecord, je dois vous dire qu'il n'y a pas de solution sensée au problème des "valeurs par défaut". Il n'y a que des bidouillages insensés, et les auteurs de rails ne semblent pas penser que cette fonctionnalité en vaille la peine (incroyable comme seule la communauté des rails l'est ).

0 votes

Puisque la réponse acceptée et la plupart des réponses se concentrent sur ActiveRecords, nous supposons que la question originale portait sur AcitveRecords. Par conséquent, les doublons possibles de stackoverflow.com/questions/328525/

103voto

SFEley Points 4161

"Correct" est un mot dangereux en Ruby. Il y a généralement plus d'une façon de faire quelque chose. Si vous savez que vous allez toujours veulent cette valeur par défaut pour cette colonne sur cette table, les définir dans un fichier de migration de la base de données est la manière la 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, la même valeur par défaut sera définie dans tous les modèles qui l'utilisent dans n'importe quelle application Rails standard.

Cependant, si vous ne souhaitez que les valeurs par défaut soient définies dans des cas spécifiques - par exemple, s'il s'agit d'un modèle hérité qui partage une table avec d'autres - une autre façon élégante est de le faire directement dans votre code Rails lors de la création de l'objet modèle :

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() il fera toujours remonter l'attribut "Doe" jusqu'à Person.new() à moins que vous ne la remplaciez par quelque chose d'autre.

0 votes

La deuxième option ne fonctionnera pas : 3hv.co.uk/blog/2009/06/03/… Mais merci pour l'extrait de migration !

2 votes

Essayez-le. Il fonctionnera sur les nouveaux objets de modèle appelés avec la commande .new méthode de classe. La discussion de cet article de blog sur le fait qu'ActiveRecord appelle directement la méthode de la classe .allocate concernait des objets modèles chargés avec des données existantes de la base de données. ( Et c'est une très mauvaise idée de faire fonctionner ActiveRecord de cette manière, IMO. Mais ce n'est pas le sujet).

1 votes

@SFEley L'OP n'a rien dit à propos de new il est donc tout à fait raisonnable de mentionner cette bizarrerie ici.

58voto

J-_-L Points 4952

Basé sur la réponse de SFEley, voici une réponse mise à jour/corrigée pour les nouvelles versions de Rails :

class SetDefault < ActiveRecord::Migration
  def change
    change_column :table_name, :column_name, :type, default: "Your value"
  end
end

2 votes

Attention, cela ne fonctionne PAS sur Rails 4.2.x (non testé sur les versions ultérieures). as change_column peut être assez "structurant", l'opération inverse ne peut pas être déduite. Si vous n'êtes pas sûr, testez-le en lançant db:migrate y db:rollback juste après. Réponse acceptée pour le même résultat mais au moins c'est assumé !

1 votes

C'est la bonne réponse pour moi. Cela fonctionne avec rails 5.1 mais vous devrez faire up y down comme décrit ci-dessus par @GoBusto

23voto

Ian Purton Points 4648

Tout d'abord, il ne faut pas surcharger initialize(*args) car il n'est pas appelé dans tous les cas.

La meilleure solution consiste à intégrer les valeurs par défaut dans la migration :

add_column :accounts, :max_users, :integer, :default => 10

La deuxième solution consiste à intégrer des valeurs par défaut dans votre modèle, mais cela ne fonctionnera qu'avec des attributs initialement nuls. Vous pourriez avoir des problèmes, comme je l'ai fait avec boolean colonnes :

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 ||= pour empêcher Rails de surcharger les paramètres passés dans la méthode initialize.

12 votes

Note mineure -- vous voulez faire deux choses : .... 1) ne pas appeler votre méthode après_initialisation. Vous voulez after_initiation :your_method_name .... 2 utiliser self.max_users ||= 10

5 votes

Pour les booléens, il suffit de faire ceci : prop = true if prop.nil ?

2 votes

Ou after_initialize do au lieu de def after_initialize

16voto

Sebastiaan Pouyet Points 406

Vous pouvez également 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 API docs

1 votes

La réponse acceptée est plus complète mais j'aime bien cette réponse propre et documentée... Merci de votre compréhension !

7voto

Vlad Zloteanu Points 5730

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

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

E.G.

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'informations ici : http://api.rubyonrails.org/classes/ActiveRecord/Migration.html

2. Utiliser un rappel

E.G. before_validation_on_create

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

2 votes

Le problème de l'utilisation des valeurs par défaut dans la base de données n'est-il pas qu'elles ne sont pas définies lorsque l'objet est sauvegardé ? Si je veux construire un nouvel objet avec les valeurs par défaut, je dois les définir dans l'initialisateur.

0 votes

Les booléens ne peuvent pas prendre la valeur 1 ou 0 par défaut. Ils doivent prendre la valeur true ou false (voir la réponse de Silasj).

0 votes

@JamonHolmgren Merci pour la remarque, j'ai corrigé la réponse :)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X