如何在ActiveRecord中设置默认值?

浪子不回头ぞ 提交于 2020-02-26 05:23:28

如何在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模型中的字段设置默认值?


#1楼

来自api文档http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html在模型中使用before_validation方法,它为您提供了为创建和更新调用创建特定初始化的选项,例如在此示例中(同样代码)取自api docs示例)数字字段初始化为信用卡。 您可以轻松地调整它以设置您想要的任何值

class CreditCard < ActiveRecord::Base
  # Strip everything but digits, so the user can specify "555 234 34" or
  # "5552-3434" or both will mean "55523434"
  before_validation(:on => :create) do
    self.number = number.gsub(%r[^0-9]/, "") if attribute_present?("number")
  end
end

class Subscription < ActiveRecord::Base
  before_create :record_signup

  private
    def record_signup
      self.signed_up_on = Date.today
    end
end

class Firm < ActiveRecord::Base
  # Destroys the associated clients and people when the firm is destroyed
  before_destroy { |record| Person.destroy_all "firm_id = #{record.id}"   }
  before_destroy { |record| Client.destroy_all "client_of = #{record.id}" }
end

感到惊讶的是他没有在这里被建议


#2楼

一些简单的情况可以通过在数据库模式中定义默认值来处理,但是不能处理许多棘手的情况,包括计算值和其他模型的键。 对于这些情况,我这样做:

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

#3楼

after_initialize解决方案的问题在于,无论是否访问此属性,都必须向从数据库中查找的每个对象添加after_initialize。 我建议采用一种懒惰的方法。

属性方法(getters)当然是方法本身,因此您可以覆盖它们并提供默认值。 就像是:

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


#4楼

只需执行以下操作即可改进after_initialize回调模式

after_initialize :some_method_goes_here, :if => :new_record?

如果您的初始化代码需要处理关联,这会产生非常重要的好处,因为如果您在不包含关联的情况下读取初始记录,则以下代码会触发一个微妙的n + 1。

class Account

  has_one :config
  after_initialize :init_config

  def init_config
    self.config ||= build_config
  end

end

#5楼

我使用了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"
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!