¿Cómo puedo establecer valores predeterminados en ActiveRecord?

417

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

Veo una publicación de Pratik que describe un fragmento de código feo y complicado: 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 personas ponerlo en su migración, pero prefiero verlo definido en el código del modelo.

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

ryw
fuente
Parece que respondiste la pregunta tú mismo, en dos variantes diferentes :)
Adam Byrtek
19
Tenga en cuenta que la expresión "estándar" de Ruby para 'self.status = ACTIVE a menos que self.status' sea 'self.status || = ACTIVE'
Mike Woodhouse el
1
La respuesta de Jeff Perrin es mucho mejor que la marcada actualmente como aceptada. default_scope es una solución inaceptable para establecer valores predeterminados, ya que tiene el GRAN EFECTO LATERAL de cambiar también el comportamiento de las consultas.
Lawrence
ver también stackoverflow.com/questions/3975161/…
Viktor Trón
2
dado todos los votos a favor de esta pregunta, diría que Ruby necesita un método setDefaultValue para ActiveRecord
spartikus

Respuestas:

557

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

  1. default_scopeinicializará valores para 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 no es lo que desea.
  2. La definición de valores predeterminados en su migración también funciona parte del tiempo ... Como ya se mencionó, esto no funcionará cuando llame a Model.new.
  3. La anulación initializepuede funcionar, ¡pero no olvides llamar super!
  4. Usar un complemento como el de phusion se está volviendo un poco ridículo. Esto es ruby, ¿realmente necesitamos un complemento solo para inicializar algunos valores predeterminados?
  5. La anulación after_initialize está en desuso a partir de Rails 3. Cuando anulo after_initializeen rails 3.0.3, aparece la siguiente advertencia en la consola:

ADVERTENCIA DE DEPRECACIÓN: Base # after_initialize ha quedado en desuso, utilice 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 after_initializedevolución de llamada, que le permite atributos predeterminados además de permitirle establecer valores predeterminados en asociaciones de esta manera:

  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 solo tiene un lugar para buscar la inicialización de sus modelos. Estoy usando este método hasta que a alguien se le ocurra uno mejor.

Advertencias:

  1. Para campos booleanos hacer:

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

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

  2. Si solo está seleccionando un subconjunto de columnas para un modelo (es decir, utilizando selectuna consulta como Person.select(:firstname, :lastname).all), obtendrá un mensaje MissingAttributeErrorsi su initmétodo accede a una columna que no se ha incluido en la selectcláusula. Puede protegerse contra este caso así:

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

    y para una columna booleana ...

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

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

Jeff Perrin
fuente
77
Esta definitivamente parece ser la mejor manera de lograr esto. Lo cual es realmente extraño y desafortunado. Un método sensato preferido para establecer valores predeterminados de atributos del modelo en la creación parece algo que Rails ya debería haber incorporado. La única otra forma (confiable), anular initialize, parece realmente complicada para algo que debe ser claro y bien definido. Pasé horas rastreando la documentación antes de buscar aquí porque asumí que esta funcionalidad ya estaba allí en algún lugar y simplemente no estaba al tanto.
seaneshbaugh
106
Una nota sobre esto: si tiene un campo booleano que desea predeterminar, no lo haga self.bool_field ||= true, ya que esto forzará el campo a verdadero incluso si lo inicializa explícitamente a falso. En cambio hazlo self.bool_field = true if self.bool_field.nil?.
Paul Russell
2
Con respecto al punto # 2, Model.new realmente funciona (¿solo para mí?) Junto con los valores predeterminados definidos en las migraciones, o más exactamente con los valores predeterminados para las columnas de la tabla. Pero reconozco que el método de Jeff basado en la devolución de llamada after_initialize es probablemente la mejor manera de hacerlo. Solo una pregunta: ¿funciona con objetos sucios pero no guardados? En su ejemplo, ¿Person.new.number_was devolverá 0.0?
Laurent Farcy
21
Tenga cuidado al usar este enfoque combinado con la selección de columnas específicas con registro activo. En este caso, solo los atributos especificados en la consulta se encontrarán en el objeto y el código de inicio arrojará un MissingAttributeError. Se puede añadir una verificación adicional como se muestra: self.number ||= 0.0 if self.has_attribute? :number Para booleanos: self.bool_field = true if (self.has_attribute? :bool_value) && self.bool_field.nil?. Este es Rails 3.2+: para usarlo antes, self.attributes.has_key?necesita una cadena en lugar de un símbolo.
Cliff Darling
66
Hacer esto con las asociaciones cargará ansiosamente esas asociaciones en la búsqueda. Comenzar initializecon return if !new_record?evitar problemas de rendimiento.
Kyle Macey
68

Rieles 5+

Puede usar el método de atributo dentro de sus modelos, por ejemplo:

class Account < ApplicationRecord
  attribute :locale, :string, default: 'en'
end

También puede pasar una lambda al defaultparámetro. Ejemplo:

attribute :uuid, UuidType.new, default: -> { SecureRandom.uuid }
Lucas Caton
fuente
3
ahhhhh esta es la joya que estaba buscando! default también puede tomar un proceso, por ejemplo, default: -> {Time.current.to_date}
schpet
1
Asegúrese de especificar el tipo como segundo argumento; de lo contrario, el tipo será Valuey no se realizará ninguna conversión de texto.
nulo
para mi deleite, esto también funciona con store_accessor, por ejemplo, dado store_accessor :my_jsonb_column, :localeque luego puede definirattribute :locale, :string, default: 'en'
Ryan Romanchuk
Oh, eso es fantástico, necesitaba valores predeterminados para mostrar en una forma y esto funciona muy bien. Gracias lucas
Paul Watson
Todavía se puede establecer esto en nil. Si no pueden ser nilDB not null+ DB default + github.com/sshaw/keep_defaults son el camino a seguir desde mi experiencia
sshaw
47

Ponemos los valores predeterminados en la base de datos a través de migraciones (especificando la :defaultopción 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: la convención sobre la configuración, DRY, la definición de la tabla impulsa 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 las migraciones.

Laurent Farcy
fuente
3
Otro problema es cuando desea un valor predeterminado para una clave externa. No puede codificar un valor de ID en el campo de clave externa porque en diferentes bases de datos la ID puede ser diferente.
shmichael
2
Otro problema más es que de esta manera no puede inicializar los accesos no persistentes (atributos que no son columnas db).
Viktor Trón
2
Otro problema es que no necesariamente puede ver todos los valores predeterminados en un solo lugar. pueden estar dispersos a través de diferentes migraciones.
Declan
8
declan, hay db / schema.rb
Benjamin Atkin
66
Me gustaría mencionar para futuros lectores: al menos por lo que he leído, esto es contrario a los principios de AR. La lógica de los modelos debe descansar dentro de las clases de modelos, y las bases de datos deben ser lo más ignorantes posible. Los valores predeterminados para mí constituyen una lógica específica sobre un modelo.
darethas
40

Algunos casos simples pueden manejarse definiendo un valor predeterminado en el esquema de la base de datos, pero eso no maneja una cantidad 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

Decidí usar after_initialize pero no quiero que se aplique a los 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 hice confirmando si el objeto ya persiste, lo que indica que no es nuevo.

Habiendo visto la respuesta de Brad Murray, esto es aún más claro si la condición se traslada 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
Joseph Lord
fuente
44
Este es un punto realmente importante. Tengo que imaginar que, en la mayoría de los casos, establecer el valor predeterminado en un registro solo se debe hacer antes de conservar un nuevo registro, no al cargar un registro persistente.
Russell Silva
Gracias amigo, me salvaste el día.
stephanfriedrich
2
¿Qué tal :before_create?
Franklin Yu el
¿Cómo: before_create maneja llamadas nuevas y guardadas por separado? Me gustaría comprobar eso y realmente entenderlo antes de cambiarlo.
Joseph Lord
17

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 de inicio necesita tratar con asociaciones, ya que el siguiente código activa 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
Brad Murray
fuente
16

Los chicos de Phusion tienen un buen complemento para esto.

Milan Novota
fuente
Tenga en cuenta que este complemento permite que los :defaultvalores en las migraciones de esquemas 'solo funcionen' Model.new.
jchook
Puedo hacer que los :defaultvalores de las migraciones 'solo funcionen' Model.new, al contrario de lo que Jeff dijo en su publicación. Trabajo verificado en Rails 4.1.16.
Magne
8

Yo uso la attribute-defaultsgema

De la documentación: ejecute sudo gem install attribute-defaultsy 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"
aidan
fuente
7

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

La mejor respuesta: ¡ Depende de lo que quieras!

Si desea que cada objeto comience con un valor: useafter_initialize :init

¿Desea que el new.htmlformulario 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 desea que cada objeto tenga un valor calculado a partir de la entrada del usuario: usebefore_save :default_values ¿Desea que el usuario ingreseXy luegoY = X+'foo'? utilizar:

class Task < ActiveRecord::Base
  before_save :default_values
  def default_values
    self.status ||= 'P'
  end
end
Blair Anderson
fuente
4

¡Para eso están los constructores! Anular el initializemétodo del modelo .

Usa el after_initializemétodo.

John Topley
fuente
2
Normalmente sería correcto, pero nunca debe anular la inicialización en un modelo ActiveRecord, ya que no siempre se puede llamar. Deberías usar el after_initializemétodo en su lugar.
Luke Redpath el
Usar un default_scope solo para establecer un valor predeterminado es CIERTAMENTE incorrecto. after_initialize es la respuesta correcta.
joaomilho
4

Sup, chicos, terminé haciendo lo siguiente:

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

¡Funciona de maravilla!

Tony
fuente
3

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

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 así:

class Widget < ApplicationRecord
  include DefaultValues

  defaults :category, to: 'uncategorized'
  defaults :token, to: -> { SecureRandom.uuid }
end
ayunarse
fuente
3

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

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

La forma canónica de Rails, antes de Rails 5, era en realidad configurarlo en la migración, y solo mirar en el db/schema.rb cuando quiera ver qué valores predeterminados establece la base de datos para cualquier modelo.

Al contrario de lo que dice la respuesta de @Jeff Perrin (que es un poco viejo), el enfoque de migración incluso aplicará el valor predeterminado cuando se usa Model.new , debido a la magia de Rails. 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 la base de código. Y 'simplemente funciona'.

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

O, para el cambio de columna sin crear uno nuevo, haga lo siguiente:

class AddStatusToItem < ActiveRecord::Migration
  def change
    change_column_default :items, :scheduler_type, "hotseat"
  end
end

O tal vez incluso mejor:

class AddStatusToItem < ActiveRecord::Migration
  def change
    change_column :items, :scheduler_type, :string, default: "hotseat"
  end
end

Consulte la guía oficial de RoR para ver las opciones en los métodos de cambio de columna.

No null: falsepermite valores NULL en la base de datos y, como beneficio adicional, también se actualiza para que todos los registros de bases de datos preexistentes que antes eran nulos se establezcan con el valor predeterminado para este campo también. Si lo desea, puede excluir este parámetro en la migración, ¡pero lo encontré 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
Magne
fuente
1

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

Los métodos de atributo (getters) son, por supuesto, métodos en sí mismos, por lo que puede anularlos 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ó, debe hacer Foo.find_by_status ('ACTIVE'). En ese caso, creo que realmente necesitaría establecer el valor predeterminado en las restricciones de su base de datos, si el DB lo admite.

Jeff Gran
fuente
Esta solución y la alternativa propuesta no funcionan en mi caso: tengo una jerarquía de clases STI donde solo una clase tiene ese atributo y la columna correspondiente que se usará en condiciones de consulta de base de datos.
cmoran92
1

Me encontré con problemas al after_initializedar ActiveModel::MissingAttributeErrorerrores al hacer hallazgos complejos:

p.ej:

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

"buscar" en el .wherehash de condiciones

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

def initialize
  super
  default_values
end

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

La superllamada es necesaria para asegurarse de que el objeto se inicialice correctamente desdeActiveRecord::Base antes de hacer mi código personalizado, es decir: valores_predeterminados

Sean
fuente
Me gusta. Tenía que hacer def initialize(*); super; default_values; enden Rails 5.2.0. Además, el valor predeterminado está disponible, incluso en el .attributeshash.
espía
1
class Item < ActiveRecord::Base
  def status
    self[:status] or ACTIVE
  end

  before_save{ self.status ||= ACTIVE }
end
Mike Breen
fuente
2
Mmmhh ... parece ingenioso al principio, pero después de pensar un poco, veo algunos problemas. Primero, todos los valores predeterminados no están en un solo punto, sino que están dispersos por la clase (imagine buscarlos o cambiarlos). Segundo y peor, no puedes poner, más adelante, un valor nulo (¡o incluso uno falso!).
paradoja
¿Por qué necesitaría establecer un valor nulo como predeterminado? lo sacas de la caja con AR sin hacer nada en absoluto. En cuanto a falso cuando se usa una columna booleana, entonces tiene razón, este no es el mejor enfoque.
Mike Breen
No puedo hablar por otros hábitos de codificación No he tenido un problema porque no disperso mis captadores / establecedores alrededor de un archivo de clase. Además, cualquier editor de texto moderno debería facilitar la navegación a un método (shift-cmd-t en textmate).
Mike Breen
@paradoja - Retiro eso, ahora veo dónde se descompone con el uso de nulo también. No necesariamente usa nulo como predeterminado, pero si realmente desea cambiar el valor a nulo en algún momento. Buena captura @paradoja, gracias.
Mike Breen
Utilizo este método ya que funciona bien con atributos generados dinámicamente.
Dale Campbell
0

Aunque hacer eso para establecer los valores predeterminados es confuso e incómodo en la mayoría de los casos, también puede usarlo :default_scope. Mira el comentario de squil aquí .

skalee
fuente
0

El método after_initialize está en desuso, 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 sus migraciones sigue siendo la forma más limpia.

Greg
fuente
44
En Rails 3: el after_initializemétodo NO está en desuso . De hecho, la devolución de llamada de estilo macro que le da un ejemplo de IS está en desuso . Detalles: guides.rubyonrails.org/…
Zabba
0

Descubrí que usar un método de validación proporciona mucho control sobre la configuración predeterminada. Incluso puede establecer valores predeterminados (o fallar la validación) para las actualizaciones. Incluso puede establecer un valor predeterminado diferente para inserciones 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

Kelvin
fuente
¿la validación solo ocurre antes de guardar? ¿Qué sucede si desea mostrar los valores predeterminados antes de guardar?
nurettin
@nurettin Ese es un buen punto y puedo ver por qué querrías eso a veces, pero el OP no mencionó esto como un requisito. Debe decidir por sí mismo si desea la sobrecarga de establecer los valores predeterminados en cada instancia, incluso si no está guardada. La alternativa es mantener un objeto ficticio alrededor para que la newacción se reutilice.
Kelvin
0

Si la columna es una columna de tipo 'estado' y su modelo se presta para el uso de máquinas de estado, considere usar la gema de aasmo , 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 de los registros no guardados, pero es un poco más limpio que rodar el tuyo inito lo que sea, y cosechas los otros beneficios del asasm, como los ámbitos para todos tus estados.

Solicitud incorrecta
fuente
0

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

Hay algunos escenarios difíciles que requieren anular el método de inicialización, lo que hace esa gema.

Ejemplos:

Su valor predeterminado de db es NULL, su valor predeterminado definido por modelo / ruby ​​es "alguna cadena", 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í no podrán establecer el valor en nulo, y en su lugar lo establecerán en el valor predeterminado.

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

PERO ahora imagine que su valor predeterminado de db es "alguna cadena", su modelo / ruby ​​definido por defecto es "alguna otra cadena", pero en un determinado escenario, desea establecer el valor en "alguna cadena" (el valor predeterminado de db):MyModel.new(my_attr: 'some_string')

Esto dará como resultado my_attr_changed?ser falsa ya que el valor coincide con el valor por defecto db, que a su vez se disparará su código predeterminado rubí definido y establecer el valor a "alguna otra cadena" - de nuevo, no lo que usted desea.


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

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

Etipton
fuente
0

Aquí hay una solución que he usado y me sorprendió un poco que aún no se haya agregado.

Hay dos partes en esto. La primera parte es establecer el valor predeterminado en la migración real, y la segunda parte es agregar una validación en el modelo para garantizar que la presencia sea verdadera.

add_column :teams, :new_team_signature, :string, default: 'Welcome to the Team'

Entonces verá aquí que el valor predeterminado ya está configurado. Ahora, en la validación, desea asegurarse de que siempre haya un valor para la cadena, así que solo haga

 validates :new_team_signature, presence: true

Lo que esto hará es establecer el valor predeterminado para usted. (para mí tengo "Bienvenido al equipo"), y luego irá un paso más allá y garantizará que siempre haya un valor presente para ese objeto.

¡Espero que ayude!

kdweber89
fuente
0
# db/schema.rb
create_table :store_listings, force: true do |t|
  t.string :my_string, default: "original default"
end

StoreListing.new.my_string # => "original default"

# app/models/store_listing.rb
class StoreListing < ActiveRecord::Base
  attribute :my_string, :string, default: "new default"
end

StoreListing.new.my_string # => "new default"

class Product < ActiveRecord::Base
  attribute :my_default_proc, :datetime, default: -> { Time.now }
end

Product.new.my_default_proc # => 2015-05-30 11:04:48 -0600
sleep 1
Product.new.my_default_proc # => 2015-05-30 11:04:49 -0600
shilovk
fuente
-2

use default_scope en rails 3

doc api

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 anota cualquier valor predeterminado especificado allí. Más tarde, al crear objetos, asigna los valores predeterminados especificados en el esquema sin tocar la base de datos.

discusión

Viktor Trón
fuente
si usa meta_where, default_scope puede no funcionar para asignar valores predeterminados a nuevos objetos AR debido a un error.
Viktor Trón
este problema de meta_where ahora se ha solucionado [ metautonomous.lighthouseapp.com/projects/53011/tickets/…
Viktor Trón
3
NO lo use default_scope. Esto hará que todas sus consultas agreguen esta condición al campo que ha establecido. Casi NUNCA es lo que quieres.
Brad
@brad, gracioso que menciones, estoy totalmente de acuerdo, es malo :). vea mi comentario en stackoverflow.com/questions/10680845/… .
Viktor Trón
-3

De los documentos de la api http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html Use el before_validationmétodo en su modelo, le brinda las opciones de crear una inicialización específica para crear y actualizar llamadas, por ejemplo, en este ejemplo (nuevamente 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 el suyo no haya sido sugerido aquí

jamesc
fuente
before_validation no establecerá los valores predeterminados hasta que el objeto esté listo para persistir. Si el proceso necesita leer los valores predeterminados antes de persistir, los valores no estarán listos.
mmell
Nunca establece valores predeterminados durante la verificación de validaciones de todos modos. Ni siquiera es un Hack. Hágalo durante la inicialización
Sachin