rails-activerecord - ActiveRecord如何设置默认值?

  显示原文与译文双语对照的内容

如何在ActiveRecord中设置默认值?

我看到一个来自Pratik的post,它描述了一个丑陋的复杂代码块: 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

我在google上看到了以下例子:


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


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

我也看到人们把它放在迁移中,但是我宁愿在模型代码中看到它。

在ActiveRecord模型中设置字段默认值的标准方法?

时间:

我们将默认值在数据库中通过迁移( 通过在每个列定义上指定 :default 选项), 让活动记录使用这些值来设置默认为每个属性。

请原谅,这里方法与AR的原则一致: 约定优于配置,干,表定义驱动模型,而不是另一种方式。

注意默认值仍然在应用程序( ruby ) 代码中,但不在模型中,而是在 migration(s) 中。


after_initialize :defaults

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

我决定使用 after_initialize,但我不希望它被应用到只找到那些新建或者创建的对象的对象。 我认为对于这个明显的用例没有提供after_new回调几乎是令人震惊的,但我通过确认该对象是否已经被持久化来表明它不是新的。

看到了Brad的murray的回答,如果条件被移动到回调请求,这将更加干净:


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

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


after_initialize :some_method_goes_here, :if => :new_record?

如果你的init代码需要处理关联,这有一个non-trivial好处,下面的代码会触发一个微妙的n+1,如果你读取初始记录而不包括相关的关联。


class Account

 has_one :config
 after_initialize :init_config

 def init_config
 self.config ||= build_config
 end

end

我使用 attribute-defaults gem

从文档:运行 sudo gem install attribute-defaults 并将 require'attribute_defaults' 添加到你的应用程序。


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"

比建议的更清晰/更简洁的方法是覆盖访问器,如下所示:


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

看到"覆盖默认访问器" ActiveRecord::Base 文档并使用自我从stackoverflow更多。

after_initialize解决方案的问题是,你必须添加一个after_initialize每个对象从db,不管你是否访问该属性。 我建议使用lazy-loaded方法。

当然,属性方法( 获取) 本身就是方法本身,因此你可以覆盖它们并提供一个默认值。 就像这样:


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') 。 在这种情况下我认为你真的需要设置默认数据库中的约束,如果数据库支持它。

...