ruby-on-rails sql - ¿Cómo creo un valor predeterminado para los atributos en el modelo de Rails activerecord?





query left (10)


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.

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.




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



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



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.

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



Consideraría usar attr_defaults que se encuentran here . Tus sueños más salvajes se harán realidad.




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 usar el método de llamada que ya se ha definido dentro de la biblioteca de gemas. El valor de retorno de ese método será una matriz. Entonces puede aplicar métodos de matriz para búsqueda en ese grupo de líneas

A continuación también es útil para poderosa traza. pry-stack_explorer





ruby-on-rails activerecord