我如何在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回调是正确的方法,原因如下:

default_scope will initialize values for new models, but then that will become the scope on which you find the model. If you just want to initialize some numbers to 0 then this is not what you want. Defining defaults in your migration also works part of the time... As has already been mentioned this will not work when you just call Model.new. Overriding initialize can work, but don't forget to call super! Using a plugin like phusion's is getting a bit ridiculous. This is ruby, do we really need a plugin just to initialize some default values? Overriding after_initialize is deprecated as of Rails 3. When I override after_initialize in rails 3.0.3 I get the following warning in the console:

弃用警告:Base#after_initialize已弃用,请使用Base。After_initialize:方法代替。(调用from /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    

现在您只有一个地方可以寻找模型的初始化。我一直在用这个方法,直到有人提出更好的方法。

警告:

For boolean fields do: self.bool_field = true if self.bool_field.nil? See Paul Russell's comment on this answer for more details If you're only selecting a subset of columns for a model (ie; using select in a query like Person.select(:firstname, :lastname).all) you will get a MissingAttributeError if your init method accesses a column that hasn't been included in the select clause. You can guard against this case like so: self.number ||= 0.0 if self.has_attribute? :number and for a boolean column... self.bool_field = true if (self.has_attribute? :bool_value) && self.bool_field.nil? Also note that the syntax is different prior to Rails 3.2 (see Cliff Darling's comment below)

其他回答

在开发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?

但是第一种方法更简洁,也更精确。

这是所有。

我希望这对你们有帮助

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

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

恕我直言,这种方法与AR的原则是一致的:约定高于配置,DRY,表定义驱动模型,而不是相反。

注意,默认值仍然在应用程序(Ruby)代码中,尽管不在模型中,但在迁移中。

在rails 3中使用default_scope

接口文档

ActiveRecord模糊了在数据库(模式)中定义的默认值和在应用程序(模型)中完成的默认值之间的区别。在初始化过程中,它解析数据库模式并记录其中指定的任何默认值。稍后,在创建对象时,它将分配那些模式指定的默认值,而不涉及数据库。

讨论

我使用属性默认的宝石

从文档中可以看到: 运行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"