¿Cómo puedo establecer valores predeterminados en ActiveRecord?


¿Cómo puedo establecer el valor predeterminado en ActiveRecord?

Veo un post de Pratik que describe un pedazo feo y complicado de código: http://m.onkey.org/2007/7/24/how-to-set-default-values-in-your-model

class Item < ActiveRecord::Base  
  def initialize_with_defaults(attrs = nil, &block)
    initialize_without_defaults(attrs) do
      setter = lambda { |key, value| self.send("#{key.to_s}=", value) unless
        !attrs.nil? && attrs.keys.map(&:to_s).include?(key.to_s) }
      setter.call('scheduler_type', 'hotseat')
      yield self if block_given?
    end
  end
  alias_method_chain :initialize, :defaults
end

He visto los siguientes ejemplos buscando en Google:

  def initialize 
    super
    self.status = ACTIVE unless self.status
  end

Y

  def after_initialize 
    return unless new_record?
    self.status = ACTIVE
  end

También he visto a la gente ponerlo en su migración, pero prefiero verlo definido en el código del modelo.

¿Hay una forma canónica de establecer el valor predeterminado para los campos en Modelo ActiveRecord?

Author: meagar, 2008-11-30

25 answers

Hay varios problemas con cada uno de los métodos disponibles, pero creo que definir una devolución de llamada after_initialize es el camino a seguir por las siguientes razones:

  1. default_scope inicializará los valores para los nuevos modelos, pero luego se convertirá en el ámbito en el que encontrará el modelo. Si solo desea inicializar algunos números a 0, entonces esto es no lo que desea.
  2. Definir valores predeterminados en su migración también funciona parte del tiempo... Como ya se ha mencionado este no funcionará cuando solo llame a Model.nuevo.
  3. Sobrescribir initialize puede funcionar, pero no se olvide de llamar super!
  4. Usar un plugin como el de phusion se está volviendo un poco ridículo. Esto es ruby, ¿realmente necesitamos un plugin solo para inicializar algunos valores predeterminados?
  5. Overriding after_initialize está en desuso a partir de Rails 3. Cuando anulo after_initialize en rails 3.0.3 recibo la siguiente advertencia en la consola:

ADVERTENCIA DE OBSOLESCENCIA: Base # after_initialize ha sido obsoleto, por favor use Base.after_initialize :método en su lugar. (llamado desde/Users / me / myapp / app / models / my_model: 15)

Por lo tanto, diría que escriba una devolución de llamada after_initialize, que le permite atributos predeterminados además de que le permite establecer valores predeterminados en asociaciones como:

  class Person < ActiveRecord::Base
    has_one :address
    after_initialize :init

    def init
      self.number  ||= 0.0           #will set the default value only if it's nil
      self.address ||= build_address #let's you set a default association
    end
  end    

Ahora tienes solo un lugar para buscar la inicialización de tus modelos. Estoy usando este método hasta que alguien viene con un mejor una.

Advertencias:

  1. Para los campos booleanos haga:

    self.bool_field = true if self.bool_field.nil?

    Ver el comentario de Paul Russell sobre esta respuesta para más detalles

  2. Si solo está seleccionando un subconjunto de columnas para un modelo (ie; usando select en una consulta como Person.select(:firstname, :lastname).all) obtendrá un MissingAttributeError si su método init accede a una columna que no se ha incluido en la cláusula select. Usted puede protegerse contra este caso de la siguiente manera:

    self.number ||= 0.0 if self.has_attribute? :number

    Y para un booleano columna...

    self.bool_field = true if (self.has_attribute? :bool_value) && self.bool_field.nil?

    También tenga en cuenta que la sintaxis es diferente antes de Rails 3.2 (ver el comentario de Cliff Darling a continuación)

 528
Author: Jeff Perrin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-11 02:14:21

Ponemos los valores predeterminados en la base de datos a través de migraciones (especificando la opción :default en cada definición de columna) y dejamos que Active Record use estos valores para establecer el valor predeterminado para cada atributo.

En mi humilde opinión, este enfoque está alineado con los principios de AR : convención sobre configuración, SECO, la definición de tabla conduce el modelo, no al revés.

Tenga en cuenta que los valores predeterminados todavía están en el código de la aplicación (Ruby), aunque no en el modelo, sino en el migración(s).

 43
Author: Laurent Farcy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-11-30 13:45:52

Algunos casos simples se pueden manejar definiendo un valor predeterminado en el esquema de la base de datos, pero que no maneja un número de casos más complicados, incluidos los valores calculados y las claves de otros modelos. Para estos casos hago esto:

after_initialize :defaults

def defaults
   unless persisted?
    self.extras||={}
    self.other_stuff||="This stuff"
    self.assoc = [OtherModel.find_by_name('special')]
  end
end

He decidido usar after_initialize pero no quiero que se aplique a objetos que se encuentran solo aquellos nuevos o creados. Creo que es casi impactante que no se proporcione una devolución de llamada after_new para este caso de uso obvio, pero lo he hecho confirmando si el objeto ya está persistido indicando que no es nuevo.

Habiendo visto la respuesta de Brad Murray, esto es aún más limpio si la condición se mueve a la solicitud de devolución de llamada:

after_initialize :defaults, unless: :persisted?
              # ":if => :new_record?" is equivalent in this context

def defaults
  self.extras||={}
  self.other_stuff||="This stuff"
  self.assoc = [OtherModel.find_by_name('special')]
end
 38
Author: Joseph Lord,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-28 15:05:36

En Rails 5+, puede usar el método attribute dentro de sus modelos, por ejemplo.:

class Account < ApplicationRecord
  attribute :locale, :string, default: 'en'
end
 25
Author: Lucas Caton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-19 01:40:29

El patrón de devolución de llamada after_initialize se puede mejorar simplemente haciendo lo siguiente

after_initialize :some_method_goes_here, :if => :new_record?

Esto tiene un beneficio no trivial si su código init necesita tratar con asociaciones, ya que el siguiente código desencadena un sutil n+1 si lee el registro inicial sin incluir el asociado.

class Account

  has_one :config
  after_initialize :init_config

  def init_config
    self.config ||= build_config
  end

end
 17
Author: Brad Murray,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-14 05:37:50

Los chicos de Phusion tienen un buen plugin para esto.

 16
Author: Milan Novota,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-11-30 09:42:56

Una manera potencial aún mejor / más limpia que las respuestas propuestas es sobrescribir el accessor, así:

def status
  self['status'] || ACTIVE
end

Vea "Sobrescribir los accesos predeterminados" en la documentación ActiveRecord::Basey más de StackOverflow sobre el uso de self.

 8
Author: peterhurford,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:26:37

Utilizo el attribute-defaults gem

De la documentación: ejecute sudo gem install attribute-defaults y agregue require 'attribute_defaults' a su aplicación.

class Foo < ActiveRecord::Base
  attr_default :age, 18
  attr_default :last_seen do
    Time.now
  end
end

Foo.new()           # => age: 18, last_seen => "2014-10-17 09:44:27"
Foo.new(:age => 25) # => age: 25, last_seen => "2014-10-17 09:44:28"
 8
Author: aidan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-16 22:46:39

Preguntas similares, pero todas tienen un contexto ligeramente diferente: - ¿Cómo puedo crear un valor predeterminado para los atributos en el modelo de Rails activerecord?

Mejor Respuesta: Depende de Lo Que Quieras!

Si quieres que cada objeto comience con un valor: use after_initialize :init

¿Desea que el formulario new.html tenga un valor predeterminado al abrir la página? use https://stackoverflow.com/a/5127684/1536309

class Person < ActiveRecord::Base
  has_one :address
  after_initialize :init

  def init
    self.number  ||= 0.0           #will set the default value only if it's nil
    self.address ||= build_address #let's you set a default association
  end
  ...
end 

Si quieres cada objeto a tener un valor calculado a partir de la entrada del usuario: use before_save :default_values ¿Desea que el usuario ingrese X y luego Y = X+'foo'? uso:

class Task < ActiveRecord::Base
  before_save :default_values
  def default_values
    self.status ||= 'P'
  end
end
 6
Author: Blair Anderson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:47

¡Para esto están los constructores! Sobreescriba el método initialize del modelo.

Utilice el método after_initialize.

 4
Author: John Topley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-13 09:56:19

Chicos Sup, terminé haciendo lo siguiente:

def after_initialize 
 self.extras||={}
 self.other_stuff||="This stuff"
end

Funciona como un encanto!

 4
Author: Tony,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-27 06:36:21

Lo primero es lo primero: No estoy en desacuerdo con la respuesta de Jeff. Tiene sentido cuando su aplicación es pequeña y su lógica simple. Estoy aquí tratando de dar una idea de cómo puede ser un problema al construir y mantener una aplicación más grande. No recomiendo usar este enfoque primero al construir algo pequeño, sino tenerlo en cuenta como un enfoque alternativo:


Una pregunta aquí es si este valor predeterminado en los registros es lógica de negocio. Si lo es, sería prudente ponerlo en el modelo OR. Dado que el campo que menciona ryw es active , esto suena como lógica de negocios. Por ejemplo, el usuario está activo.

¿Por qué tendría que ser cauteloso al poner las preocupaciones comerciales en un modelo OR?

  1. Rompe SRP. Cualquier clase heredada de ActiveRecord:: Base ya está haciendo un lote de diferentes cosas, la principal de ellas es la consistencia de datos (validaciones) y la persistencia (guardar). Poniendo la lógica de negocios, por pequeña que sea, en AR:: Base rompe SRP.

  2. Es más lento para probar. Si quiero probar cualquier forma de lógica que ocurra en mi modelo OR, mis pruebas tienen que inicializar Rails para poder ejecutarse. Esto no será demasiado de un problema en el comienzo de su aplicación, pero se acumulará hasta que sus pruebas unitarias tardan mucho tiempo en ejecutarse.

  3. Se romperá SRP aún más en la línea, y de maneras concretas. Digamos que nuestro negocio ahora requiere que enviemos correos electrónicos a los usuarios cuando el Elemento se active. Ahora estamos agregando envíe por correo electrónico la lógica al modelo Item OR, cuya responsabilidad principal es modelar un Elemento. No debe preocuparse por la lógica del correo electrónico. Este es un caso de efectos secundarios comerciales. Estos no pertenecen al modelo OR.

  4. Es difícil diversificar. He visto aplicaciones Rails maduras con cosas como un campo init_type: string respaldado por una base de datos, cuyo único propósito es controlar la lógica de inicialización. Esto está contaminando la base de datos para solucionar un problema estructural. Hay mejores maneras, yo creer.

La forma de PORO: Si bien esto es un poco más de código, le permite mantener sus Modelos OR y la Lógica de Negocio separados. El código aquí está simplificado, pero debería mostrar la idea:

class SellableItemFactory
  def self.new(attributes = {})
    record = Item.new(attributes)
    record.active = true if record.active.nil?
    record
  end
end

Entonces con esto en su lugar, la forma de crear un nuevo elemento sería

SellableItemFactory.new

Y mis pruebas ahora podrían simplemente verificar que ItemFactory se establece activo en Item si no tiene un valor. No se necesita inicialización de rieles, no se rompe SRP. Cuando la inicialización del elemento se convierte en más avanzado (por ejemplo, establecer un campo de estado, un tipo predeterminado, etc.) la ItemFactory puede tener esto añadido. Si terminamos con dos tipos de valores predeterminados, podemos crear un nuevo BusinesCaseItemFactory para hacer esto.

NOTA: También podría ser beneficioso usar la inyección de dependencias aquí para permitir que la fábrica construya muchas cosas activas, pero lo dejé fuera por simplicidad. Aquí está: yo.nuevo (klass = Elemento, atributos = {})

 4
Author: Houen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-27 09:40:57

Esto ha sido respondido durante mucho tiempo, pero necesito valores predeterminados con frecuencia y prefiero no ponerlos en la base de datos. Creo una preocupación DefaultValues:

module DefaultValues
  extend ActiveSupport::Concern

  class_methods do
    def defaults(attr, to: nil, on: :initialize)
      method_name = "set_default_#{attr}"
      send "after_#{on}", method_name.to_sym

      define_method(method_name) do
        if send(attr)
          send(attr)
        else
          value = to.is_a?(Proc) ? to.call : to
          send("#{attr}=", value)
        end
      end

      private method_name
    end
  end
end

Y luego usarlo en mis modelos como así:

class Widget < ApplicationRecord
  include DefaultValues

  defaults :category, to: 'uncategorized'
  defaults :token, to: -> { SecureRandom.uuid }
end
 3
Author: clem,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-14 21:35:41

También he visto a la gente ponerlo en su migración, pero prefiero verlo definido en el código del modelo.

¿Hay una forma canónica de establecer el valor predeterminado para los campos en Modelo ActiveRecord?

La forma canónica de Rails, antes de Rails 5, era en realidad establecerla en la migración, y simplemente mirar en el db/schema.rb para siempre que quiera ver qué valores predeterminados están siendo establecidos por la base de datos para cualquier modelo.

Contrario a lo que dice la respuesta de @Jeff Perrin (que es un poco old), el enfoque de migración incluso aplicará el predeterminado cuando se usa Model.new, debido a algunos Rails magic. Trabajo verificado en Rails 4.1.16.

Lo más simple es a menudo lo mejor. Menos deuda de conocimiento y posibles puntos de confusión en el código base. Y 'simplemente funciona'.

class AddStatusToItem < ActiveRecord::Migration
  def change
    add_column :items, :scheduler_type, :string, { null: false, default: "hotseat" }
  end
end

El null: false no permite valores NULOS en la BD, y, como un beneficio adicional, también actualiza todos los registros de BD preexistentes se establece con el valor predeterminado para este campo también. Puede excluir este parámetro en el migración si lo desea, pero me pareció muy útil!

La forma canónica en Rails 5 + es, como dijo @Lucas Caton:

class Item < ActiveRecord::Base
  attribute :scheduler_type, :string, default: 'hotseat'
end
 3
Author: Magne,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-22 14:46:34

El problema con las soluciones after_initialize es que debe agregar un after_initialize a cada objeto que busque en la base de datos, independientemente de si accede a este atributo o no. Sugiero un enfoque perezoso.

Los métodos de atributo (getters) son, por supuesto, métodos en sí mismos, por lo que puede sobrescribirlos y proporcionar un valor predeterminado. Algo como:

Class Foo < ActiveRecord::Base
  # has a DB column/field atttribute called 'status'
  def status
    (val = read_attribute(:status)).nil? ? 'ACTIVE' : val
  end
end

A menos que, como alguien señaló, necesitas hacer Foo.find_by_status ('ACTIVO'). En ese caso creo realmente necesitaría establecer las restricciones predeterminadas en su base de datos, si la base de datos lo admite.

 1
Author: Jeff Gran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-08 00:43:17

Me encontré con problemas con after_initialize dando ActiveModel::MissingAttributeError errores al hacer hallazgos complejos:

Eg:

@bottles = Bottle.includes(:supplier, :substance).where(search).order("suppliers.name ASC").paginate(:page => page_no)

"buscar" en el .where es hash de condiciones

Así que terminé haciéndolo sobrescribiendo initialize de esta manera:

def initialize
  super
  default_values
end

private
 def default_values
     self.date_received ||= Date.current
 end

La llamada super es necesaria para asegurarse de que el objeto se inicializa correctamente desde ActiveRecord::Base antes de hacer mi código personalizado, es decir: default_values

 1
Author: Sean,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-30 03:24:52
class Item < ActiveRecord::Base
  def status
    self[:status] or ACTIVE
  end

  before_save{ self.status ||= ACTIVE }
end
 1
Author: Mike Breen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-15 07:59:14

Recomiendo encarecidamente usar la gema" default_value_for": https://github.com/FooBarWidget/default_value_for

Hay algunos escenarios complicados que requieren reemplazar el método initialize, lo que hace esa gema.

Ejemplos:

El valor predeterminado de la base de datos es NULL, el valor predeterminado definido por model / ruby es "some string", pero en realidad desea establecer el valor en nil por cualquier razón: MyModel.new(my_attr: nil)

La mayoría de las soluciones aquí fallarán al establecer el valor a nil, y en su lugar establecer el valor predeterminado.

OK, entonces en lugar de tomar el enfoque ||=, cambias a my_attr_changed?...

PERO ahora imagine que en su db predeterminado es "una cadena", su modelo/ruby-definido por defecto es "otra cadena", pero por debajo de un determinado escenario, desea para establecer el valor a "cadena" (la base de datos predeterminada): MyModel.new(my_attr: 'some_string')

Esto dará como resultado que my_attr_changed? sea false porque el valor coincide con el valor predeterminado de la base de datos, lo que a su vez disparará su código predeterminado definido por ruby y establezca el valor en "some other string" again de nuevo, no es lo que desea.


Por esas razones, no creo que esto se pueda lograr correctamente con solo un hook after_initialize.

Nuevamente, creo que la gema" default_value_for " está tomando el enfoque correcto: https://github.com/FooBarWidget/default_value_for

 1
Author: etipton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-20 04:35:02

Aunque hacer eso para establecer valores predeterminados es confuso y incómodo en la mayoría de los casos, también puede usar :default_scope. Echa un vistazo comentario de squil aquí.

 0
Author: skalee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-21 12:57:32

El método After_initialize está obsoleto, use la devolución de llamada en su lugar.

after_initialize :defaults

def defaults
  self.extras||={}
  self.other_stuff||="This stuff"
end

Sin embargo, usar : default en tus migraciones sigue siendo la forma más limpia.

 0
Author: Greg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-10-17 01:59:13

He encontrado que el uso de un método de validación proporciona mucho control sobre la configuración de valores predeterminados. Incluso puede establecer valores predeterminados (o validación de error) para las actualizaciones. Incluso puede establecer un valor predeterminado diferente para insertos vs actualizaciones si realmente lo desea. Tenga en cuenta que el valor predeterminado no se establecerá hasta # valid? se llama.

class MyModel
  validate :init_defaults

  private
  def init_defaults
    if new_record?
      self.some_int ||= 1
    elsif some_int.nil?
      errors.add(:some_int, "can't be blank on update")
    end
  end
end

Con respecto a la definición de un método after_initialize, podría haber problemas de rendimiento porque after_initialize también es llamado por cada objeto devuelto por: find : http://guides.rubyonrails.org/active_record_validations_callbacks.html#after_initialize-and-after_find

 0
Author: Kelvin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-30 21:24:23

Si la columna resulta ser una columna de tipo 'status', y su modelo se presta al uso de máquinas de estado, considere usar la gema aasm, después de lo cual simplemente puede hacer

  aasm column: "status" do
    state :available, initial: true
    state :used
    # transitions
  end

Todavía no inicializa el valor para los registros no guardados, pero es un poco más limpio que rodar el tuyo con init o lo que sea, y cosechas los otros beneficios de aasm, como los ámbitos para todos tus estados.

 0
Author: Bad Request,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-11 22:30:44

Https://github.com/keithrowell/rails_default_value

class Task < ActiveRecord::Base
  default :status => 'active'
end
 0
Author: Keith Rowell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-24 12:39:31

Usar default_scope en rails 3

Api doc

ActiveRecord oculta la diferencia entre el valor predeterminado definido en la base de datos (esquema) y el valor predeterminado realizado en la aplicación (modelo). Durante la inicialización, analiza el esquema de la base de datos y toma nota de los valores predeterminados especificados allí. Más tarde, al crear objetos, asigna esos valores predeterminados especificados en el esquema sin tocar la base de datos.

Discusión

 -1
Author: Viktor Trón,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:45

Desde la api docs http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html Utilice el método before_validation en su modelo, que le da las opciones de crear inicialización específica para crear y actualizar llamadas por ejemplo, en este ejemplo (de nuevo código tomado del ejemplo de api docs) el campo de número se inicializa para una tarjeta de crédito. Puede adaptar esto fácilmente para establecer los valores que desee

class CreditCard < ActiveRecord::Base
  # Strip everything but digits, so the user can specify "555 234 34" or
  # "5552-3434" or both will mean "55523434"
  before_validation(:on => :create) do
    self.number = number.gsub(%r[^0-9]/, "") if attribute_present?("number")
  end
end

class Subscription < ActiveRecord::Base
  before_create :record_signup

  private
    def record_signup
      self.signed_up_on = Date.today
    end
end

class Firm < ActiveRecord::Base
  # Destroys the associated clients and people when the firm is destroyed
  before_destroy { |record| Person.destroy_all "firm_id = #{record.id}"   }
  before_destroy { |record| Client.destroy_all "client_of = #{record.id}" }
end

Sorprendido de que su no se ha sugerido aquí

 -2
Author: jamesc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-31 18:43:21