我如何在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模型?
我强烈建议使用“default_value_for”gem: https://github.com/FooBarWidget/default_value_for
有一些棘手的场景需要重写initialize方法,这个gem就是这样做的。
例子:
你的db默认值是NULL,你的模型/ruby定义的默认值是“一些字符串”,但你实际上想要设置值为nil的原因:MyModel。新(my_attr: nil)
这里的大多数解决方案将无法将值设置为nil,而是将其设置为默认值。
好吧,所以你不用||=方法,而是切换到my_attr_changed?…
但是现在假设你的数据库默认值是"some string",你的模型/ruby定义的默认值是"some other string",但在某种情况下,你想将值设置为"some string"(数据库默认值):MyModel。新(my_attr:“some_string”)
这将导致my_attr_changed?为false是因为该值与db默认值匹配,而db默认值将触发ruby定义的默认代码,并将该值设置为“其他字符串”——同样,这不是您想要的。
由于这些原因,我不认为这可以通过一个after_initialize钩子正确地完成。
同样,我认为“default_value_for”gem采用了正确的方法:https://github.com/FooBarWidget/default_value_for
在开发Rails 6应用程序时,我也遇到过类似的挑战。
以下是我的解决方法:
我有一个用户表和一个角色表。Users表属于Roles表。我也有一个管理员和学生模型,从用户表继承。
然后需要在创建用户时为角色设置一个默认值,比如id = 1的管理员角色或id = 2的学生角色。
class User::Admin < User
before_save :default_values
def default_values
# set role_id to '1' except if role_id is not empty
return self.role_id = '1' unless role_id.nil?
end
end
这意味着在数据库中创建/保存admin用户之前,如果role_id不为空,则将其设置为默认值1。
return self.role_id = '1' unless role_id.nil?
等于:
return self.role_id = '1' unless self.role_id.nil?
和:
self.role_id = '1' if role_id.nil?
但是第一种方法更简洁,也更精确。
这是所有。
我希望这对你们有帮助
一些简单的情况可以通过在数据库模式中定义默认值来处理,但这不能处理许多棘手的情况,包括其他模型的计算值和键。对于这些情况,我这样做:
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