ruby-on-rails - left - rails sql query
¿Cómo creo un valor predeterminado para los atributos en el modelo de Rails activerecord? (11)
Consideraría usar attr_defaults que se encuentran here . Tus sueños más salvajes se harán realidad.
Esta pregunta ya tiene una respuesta aquí:
Quiero crear un valor predeterminado para un atributo definiéndolo en ActiveRecord. De forma predeterminada, cada vez que se crea el registro, quiero tener un valor predeterminado para atributo :status
. Traté de hacer esto:
class Task < ActiveRecord::Base
def status=(status)
status = ''P''
write_attribute(:status, status)
end
end
Pero al momento de la creación, aún recupero este error de la base de datos:
ActiveRecord::StatementInvalid: Mysql::Error: Column ''status'' cannot be null
Por lo tanto, supongo que el valor no se aplicó al atributo.
¿Cuál sería la manera más elegante de hacer esto en Rails?
Muchas gracias.
Cuando necesito valores predeterminados, generalmente es para nuevos registros antes de que se muestre la vista de la nueva acción. El siguiente método establecerá los valores predeterminados para solo los nuevos registros para que estén disponibles cuando se procesen los formularios. before_save
y before_create
son demasiado tarde y no funcionarán si desea que los valores predeterminados aparezcan en los campos de entrada .
after_initialize do
if self.new_record?
# values will be available for new record forms.
self.status = ''P''
self.featured = true
end
end
Debido a que me encontré con este problema hace poco tiempo y las opciones para Rails 3.0 son un poco diferentes, daré otra respuesta a esta pregunta.
En Rails 3.0 quieres hacer algo como esto:
class MyModel < ActiveRecord::Base
after_initialize :default_values
private
def default_values
self.name ||= "default value"
end
end
En su ejemplo aquí, está asignando ''P'' a una variable local ''estado''. Pruebe self.status = ''P''
para este rey de las cosas. Aunque sería mejor utilizar after_initialize
como se menciona en otras respuestas.
Encontré una mejor manera de hacerlo ahora:
def status=(value)
self[:status] = ''P''
end
En Ruby, una llamada a método no tiene paréntesis, por lo tanto, debería nombrar la variable local en otra cosa, de lo contrario, Ruby lo reconocerá como una llamada a método.
La solución depende de algunas cosas.
¿El valor predeterminado depende de otra información disponible en el momento de la creación? ¿Se puede borrar la base de datos con consecuencias mínimas?
Si respondió la primera pregunta sí, entonces quiere usar la solución de Jim
Si respondió la segunda pregunta sí, entonces quiere usar la solución de Daniel
Si contestó no a ambas preguntas, probablemente sea mejor que agregue y ejecute una nueva migración.
class AddDefaultMigration < ActiveRecord::Migration
def self.up
change_column :tasks, :status, :string, :default => default_value, :null => false
end
end
: la cadena se puede reemplazar con cualquier tipo que ActiveRecord :: Migration reconozca.
La CPU es barata, por lo que la redefinición de Tarea en la solución de Jim no causará muchos problemas. Especialmente en un entorno de producción. Esta migración es la forma correcta de hacerlo, ya que se carga y se invoca con mucha menos frecuencia.
Para los tipos de columnas que Rails admite, como la cadena en esta pregunta, el mejor enfoque es establecer el valor predeterminado de columna en la base de datos como lo indica Daniel Kristensen. Rails realizará una introspección en el DB e inicializará el objeto en consecuencia. Además, eso hace que su DB esté segura de que alguien agregue una fila fuera de su aplicación Rails y se olvide de inicializar esa columna.
Para los tipos de columna, Rails no es compatible, p. Ej. Columnas ENUM. Rails no podrá realizar una introspección de la columna predeterminada. Para estos casos, no desea utilizar after_initialize (se llama cada vez que se carga un objeto desde el DB así como también cada vez que se crea un objeto usando .new), before_create (porque ocurre después de la validación) o before_save (porque ocurre al actualizar también, que generalmente no es lo que quieres).
Por el contrario, desea establecer el atributo en una validez previa en: crear, así:
before_validation :set_status_because_rails_cannot, on: :create
def set_status_because_rails_cannot
self.status ||= ''P''
end
Puede establecer una opción predeterminada para la columna en la migración
....
add_column :status, :string, :default => "P"
....
O
Puede usar una devolución de llamada, before_save
class Task < ActiveRecord::Base
before_save :default_values
def default_values
self.status ||= ''P'' # note self.status = ''P'' if self.status.nil? might be safer (per @frontendbeauty)
end
end
Puede hacerlo sin escribir ningún código :) Solo necesita establecer el valor predeterminado para la columna en la base de datos. Puedes hacer esto en tus migraciones. Por ejemplo:
create_table :projects do |t|
t.string :status, :null => false, :default => ''P''
...
t.timestamps
end
Espero que ayude.
Según lo veo, hay dos problemas que deben abordarse cuando se necesita un valor predeterminado.
- Necesita el valor presente cuando se inicializa un nuevo objeto. El uso de after_initialize no es adecuado porque, como se dijo, se invocará durante las llamadas a #find, lo que dará lugar a un golpe de rendimiento.
- Debe conservar el valor predeterminado cuando se guarde
Aquí está mi solución:
# the reader providers a default if nil
# but this wont work when saved
def status
read_attribute(:status) || "P"
end
# so, define a before_validation callback
before_validation :set_defaults
protected
def set_defaults
# if a non-default status has been assigned, it will remain
# if no value has been assigned, the reader will return the default and assign it
# this keeps the default logic DRY
status = status
end
Me encantaría saber por qué la gente piensa en este enfoque.
Solo fortaleciendo la respuesta de Jim
Usando presence uno puede hacer
class Task < ActiveRecord::Base
before_save :default_values
def default_values
self.status = status.presence || ''P''
end
end