我如何在ActiveRecord设置默认值?

我看到Pratik的一篇文章,描述了一段丑陋而复杂的代码: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

我在谷歌上看到了以下例子:

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

and

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

我也见过有人把它放在迁移中,但我更愿意看到它在模型代码中定义。

是否有一个规范的方法来设置默认值的字段在ActiveRecord模型?


当前回答

after_initialize解决方案的问题是,无论是否访问该属性,都必须向从DB中查找的每个对象添加after_initialize。我建议采用惰性加载方法。

属性方法(getter)当然是方法本身,因此您可以覆盖它们并提供默认值。喜欢的东西:

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

除非,就像有人指出的那样,你需要执行Foo.find_by_status('ACTIVE')。在这种情况下,我认为您确实需要在数据库约束中设置默认值,如果数据库支持它的话。

其他回答

尽管在大多数情况下这样设置默认值会令人困惑和尴尬,但您也可以使用:default_scope。点击这里查看squil的评论。

可以通过简单地执行以下操作来改进after_initialize回调模式

after_initialize :some_method_goes_here, :if => :new_record?

如果您的init代码需要处理关联,那么这就有一个非常重要的好处,因为如果您读取初始记录而不包括关联记录,那么下面的代码将触发一个微妙的n+1。

class Account

  has_one :config
  after_initialize :init_config

  def init_config
    self.config ||= build_config
  end

end

一个比建议的答案更好/更干净的潜在方法是覆盖访问器,像这样:

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

请参阅ActiveRecord::Base文档中的“覆盖默认访问器”和StackOverflow中关于使用self的更多信息。

class Item < ActiveRecord::Base
  def status
    self[:status] or ACTIVE
  end

  before_save{ self.status ||= ACTIVE }
end

After_initialize方法已弃用,请改用回调方法。

after_initialize :defaults

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

然而,在迁移中使用:default仍然是最干净的方式。