ruby-on-rails - que - ruby on rails tutorial
¿Cómo establecer valores predeterminados en Rails? (15)
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 Ruby on Rails v3.2.8, utilizando la devolución de llamada 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 devolución de llamada 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 rieles. 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.
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.
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)
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
.
Podría usar la gema rails_default_value. p.ej:
class Foo < ActiveRecord::Base
# ...
default :bar => ''some default value''
# ...
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
Respondí una pregunta similar here . 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 gema ''attribute-defaults''.
Documentación y descarga: https://github.com/bsm/attribute-defaults
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 devolución de llamada
EG before_validation_on_create
Más información aquí: http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html#M002147
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 plugin parece una opción interesante.
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
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 al usar Self .