Rails:如何在ActiveRecord中设置默认值?
Rails:如何在ActiveRecord中设置默认值?
如何在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
和
def after_initialize return unless new_record? self.status = ACTIVE end
我也看到有人把它放到迁移中,但我宁愿在模型代码中定义它。
有没有一种规范的方式来为ActiveRecord模型中的字段设置默认值?
Rails 5+
在你的模型中,可以使用attribute方法,例如:
class Account < ApplicationRecord attribute :locale, :string, default: 'en' end
你也可以把一个lambda传递给default
参数,例如:
attribute :uuid, :string, default: -> { SecureRandom.uuid }
第二个参数是类型,也可以是自定义类型类的实例,例如:
attribute :uuid, UuidType.new, default: -> { SecureRandom.uuid }
每种可用的方法都存在一些问题,但我相信定义一个after_initialize
回调是最好的方法,理由如下:
default_scope
将为新模型初始化值,但然后它将成为您查找模型的范围。如果您只想将某些数字初始化为0,那么这不是您想要的。- 在迁移中定义默认值有时也有效...正如已经提到的,当你只是调用Model.new时,这种方法是行不通的。
- 重写
initialize
可以起作用,但不要忘记调用super
! - 使用像Phusion这样的插件有点荒谬。这是Ruby,我们真的需要一个插件来初始化一些默认值吗?
- 自Rails 3之后,弃用了重写
after_initialize
。当我在Rails 3.0.3中重写after_initialize
时,控制台会提示以下警告:
DEPRECATION WARNING: Base#after_initialize已被弃用,请使用Base.after_initialize:方法。 (来自/Users/me/myapp/app/models/my_model:15)
因此,我建议编写一个after_initialize
回调函数,它使您能够在关联上设置默认值的同时,也可以为属性设置默认值,如下所示:
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
现在,您只需要查找一个地方就可以初始化您的模型。在有更好的方法出现之前,我一直使用这种方法。
警告:
-
对于布尔字段,请这样做:
self.bool_field = true if self.bool_field.nil?
请参阅保罗·拉塞尔(Paul Russell)对此回答的评论以获取更多详细信息。
-
如果您只为一个模型选择了子集列(即,在查询中使用
select
,例如Person.select(:firstname,:lastname).all
),如果您的init
方法访问没有包含在select
关键字中的列,您将获得MissingAttributeError
。您可以这样针对此情况进行保护:self.number ||= 0.0 if self.has_attribute? :number
对于布尔列,可以这样:
self.bool_field = true if (self.has_attribute? :bool_value) && self.bool_field.nil?
还要注意,在Rails 3.2之前,语法是不同的(请参阅下面Cliff Darling的评论)