¿Cómo establecer valores predeterminados en Rails?

Estoy tratando de encontrar la mejor manera de establecer valores predeterminados para los objetos en Rails.

Lo mejor que puedo pensar es establecer el valor predeterminado en el new método en el controlador.

¿Alguien tiene alguna entrada si esto es aceptable o si hay una mejor manera de hacerlo?

“Correcto” es una palabra peligrosa en Ruby. Generalmente hay más de una forma de hacer cualquier cosa. Si sabe que siempre querrá ese valor predeterminado para esa columna en esa tabla, configurarlos en un archivo de migración de base de datos es la manera más fácil:

 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 

Debido a que ActiveRecord detecta automáticamente sus propiedades de tabla y columna, esto hará que se establezca el mismo valor predeterminado en cualquier modelo que lo use en cualquier aplicación Rails estándar.

Sin embargo, si solo desea valores predeterminados establecidos en casos específicos, es decir, es un modelo heredado que comparte una tabla con algunos otros, entonces otra forma elegante es hacerlo directamente en su código Rails cuando se crea el objeto modelo:

 class GenericPerson < Person def initialize(attributes=nil) attr_with_defaults = {:last_name => "Doe"}.merge(attributes) super(attr_with_defaults) end end 

Entonces, cuando haces un GenericPerson.new() , siempre se Person.new() atributo “Doe” hasta Person.new() menos que lo Person.new() con otra cosa.

Basado en la respuesta de SFEley, aquí hay una versión actualizada / fija para las versiones más nuevas de Rails:

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

En primer lugar, no puede sobrecargar initialize(*args) ya que no se llama en todos los casos.

Su mejor opción es poner sus valores predeterminados en su migración:

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

El segundo mejor es colocar los valores predeterminados en su modelo, pero esto solo funcionará con los atributos que son inicialmente nulos. Puede tener problemas como lo hice con las columnas boolean :

 def after_initialize if new_record? max_users ||= 10 end end 

Necesitas el nuevo new_record? por lo que los valores predeterminados no anulan los valores cargados desde la base de datos.

Necesita ||= para evitar que Rails anule los parámetros pasados ​​al método de inicialización.

También puedes probar change_column_default en tus migraciones (probado en 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

Si se refiere a objetos ActiveRecord, tiene (más de) dos formas de hacerlo:

1. Use un: parámetro predeterminado en el DB

P.EJ

 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 

Más información aquí: http://api.rubyonrails.org/classes/ActiveRecord/Migration.html

2. Utiliza una callback

EG before_validation_on_create

Más información aquí: http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html#M002147

En Ruby on Rails v3.2.8, utilizando la callback de ActiveRecord after_initialize , puede llamar a un método en su modelo que asignará los valores predeterminados para un nuevo objeto.

after_initialize la callback se desencadena para cada objeto encontrado e instanciado por un buscador, con after_initialize que se activa después de que también se crean instancias de objetos nuevos ( consulte las Devolución de llamada de ActiveRecord ).

Entonces, IMO debería verse algo así como:

 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 para esta instancia a menos que la instancia contenga un attribute_whose_presence_has_been_validated haya Foo.bar = default_value anteriormente en guardar / actualizar. El default_value se usará junto con su vista para representar el formulario utilizando el valor default_value para el atributo de la bar .

En el mejor de los casos esto es hacky ...

EDITAR - use 'new_record?' para verificar si hay una instancia de una nueva llamada

En lugar de verificar un valor de atributo, use new_record? método incorporado con Rails. Entonces, el ejemplo anterior debería verse así:

 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 

Esto es mucho más limpio. Ah, la magia de Rails: es más inteligente que yo.

Para los campos booleanos en Rails 3.2.6 al menos, esto funcionará en su migración.

 def change add_column :users, :eula_accepted, :boolean, default: false end 

Poner un 1 o 0 para un valor predeterminado no funcionará aquí, ya que es un campo booleano. Debe ser un valor true o false .

Si solo está configurando valores predeterminados para ciertos atributos de un modelo respaldado por una base de datos, consideraría usar valores de columna predeterminados de sql. ¿Puede aclarar qué tipos de valores predeterminados está utilizando?

Hay una serie de enfoques para manejarlo, este complemento parece una opción interesante.

La sugerencia de reemplazar new / initialize es probablemente incompleta. Los raíles llamarán (con frecuencia) a los objetos ActiveRecord, y las llamadas a asignar no darán lugar a llamadas para inicializar.

Si está hablando de objetos ActiveRecord, eche un vistazo a sobreescribir after_initialize.

Estas publicaciones de blog (no las mías) son útiles:

Valores predeterminados No se llaman constructores por defecto

[Editar: SFEley señala que Rails realmente mira el valor predeterminado en la base de datos cuando crea una instancia de un nuevo objeto en la memoria, no me había dado cuenta de eso.]

Necesitaba establecer un valor predeterminado como si hubiera sido especificado como valor de columna predeterminado en DB. Entonces se comporta así

 a = Item.new a.published_at # => my default value a = Item.new(:published_at => nil) a.published_at # => nil 

Debido a que after_initialize callback se invoca después de configurar los atributos desde los argumentos, no había manera de saber si el atributo es nulo porque nunca se estableció o porque se estableció intencionalmente como nil. Así que tuve que penetrar un poco y vine con esta solución simple.

 class Item < ActiveRecord::Base def self.column_defaults super.merge('published_at' => Time.now) end end 

Funciona muy bien para mi (Rails 3.2.x)

Una forma potencialmente mejor / más limpia que las respuestas propuestas es sobreescribir el acceso, como este:

 def status self['name_of_var'] || 'desired_default_value' end 

Consulte “Sobrescribir accesos predeterminados” en la documentación de ActiveRecord :: Base y más desde StackOverflow al usar Self .

Respondí una pregunta similar aquí . Una forma limpia de hacerlo es usando Rails attr_accessor_with_default

 class SOF attr_accessor_with_default :is_awesome,true end sof = SOF.new sof.is_awesome => true 

ACTUALIZAR

attr_accessor_with_default ha quedado obsoleto en Rails 3.2 … puedes hacer esto en cambio con Ruby puro

 class SOF attr_writer :is_awesome def is_awesome @is_awesome ||= true end end sof = SOF.new sof.is_awesome #=> true 

Si está hablando de objetos ActiveRecord, utilizo la gem ‘attribute-defaults’.

Documentación y descarga: https://github.com/bsm/attribute-defaults

Podría usar la gem rails_default_value. p.ej:

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

https://github.com/keithrowell/rails_default_value

En caso de que esté tratando con un Modelo, puede usar la API de Attriutes en Rails 5+ http://api.rubyonrails.org/classes/ActiveRecord/Attributes/ClassMethods.html#method-i-attribute

simplemente agregue una migración con un nombre de columna apropiado y luego en el modelo configúrelo con:

 class StoreListing < ActiveRecord::Base attribute :country, :string, default: 'PT' end 

Puede anular el constructor para el modelo ActiveRecord.

Me gusta esto:

 def initialize(*args) super(*args) self.attribute_that_needs_default_value ||= default_value self.attribute_that_needs_another_default_value ||= another_default_value #ad nauseum end