我如何在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给出ActiveModel::MissingAttributeError错误时做复杂的发现:

eg:

@bottles = Bottle.includes(:supplier, :substance).where(search).order("suppliers.name ASC").paginate(:page => page_no)

where中的"search"是条件哈希值

所以我用这种方式重写了初始化:

def initialize
  super
  default_values
end

private
 def default_values
     self.date_received ||= Date.current
 end

super调用是必要的,以确保在执行自定义代码(即:default_values)之前从ActiveRecord::Base正确初始化对象

其他回答

这就是构造函数的作用!重写模型的initialize方法。

使用after_initialize方法。

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

我使用属性默认的宝石

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

Rails 5 +

你可以在你的模型中使用属性方法,例如:

class Account < ApplicationRecord
  attribute :locale, :string, default: 'en'
end

您还可以将lambda传递给默认参数。例子:

attribute :uuid, :string, default: -> { SecureRandom.uuid }

第二个参数是类型,它也可以是一个自定义类型类实例,例如:

attribute :uuid, UuidType.new, default: -> { SecureRandom.uuid }

这个问题已经回答了很长时间了,但是我经常需要默认值,并且不喜欢将它们放在数据库中。我创建了一个DefaultValues关注点:

module DefaultValues
  extend ActiveSupport::Concern

  class_methods do
    def defaults(attr, to: nil, on: :initialize)
      method_name = "set_default_#{attr}"
      send "after_#{on}", method_name.to_sym

      define_method(method_name) do
        if send(attr)
          send(attr)
        else
          value = to.is_a?(Proc) ? to.call : to
          send("#{attr}=", value)
        end
      end

      private method_name
    end
  end
end

然后像这样在我的模型中使用它:

class Widget < ApplicationRecord
  include DefaultValues

  defaults :category, to: 'uncategorized'
  defaults :token, to: -> { SecureRandom.uuid }
end