我如何在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模型?


当前回答

我发现使用验证方法对设置默认值提供了很多控制。您甚至可以为更新设置默认值(或失败验证)。如果需要的话,您甚至可以为插入和更新设置不同的默认值。 注意,默认值不会设置,直到#valid?被称为。

class MyModel
  validate :init_defaults

  private
  def init_defaults
    if new_record?
      self.some_int ||= 1
    elsif some_int.nil?
      errors.add(:some_int, "can't be blank on update")
    end
  end
end

关于定义after_initialize方法,可能会有性能问题,因为after_initialize也会被:find返回的每个对象调用: http://guides.rubyonrails.org/active_record_validations_callbacks.html#after_initialize-and-after_find

其他回答

Rails 6.1 +

现在您可以在模型上使用属性方法,而无需设置类型。

attribute :status, default: ACTIVE

or

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

注意,提供默认的to属性不能引用类的实例(lambda将在类的上下文中执行,而不是在实例中执行)。因此,如果需要根据实例或关联动态地将默认值设置为一个值,则仍然必须使用替代方法,例如after_initialize回调。如前所述,如果引用关联,建议将此限制为新记录,以避免n+1个查询。

after_initialize :do_something_that_references_instance_or_associations, if: :new_record?

我也看到人们在迁徙中使用这种方法,但我更愿意看到 在模型代码中定义。 是否有一个规范的方法来设置字段的默认值 ActiveRecord模型?

在Rails 5之前,规范的Rails方法实际上是在迁移中设置它,只需要在db/schema中查找即可。rb每当想要看到什么默认值是由DB为任何模型设置。

与@Jeff Perrin的回答相反(这有点老),在使用Model时,迁移方法甚至会应用默认值。新的,由于一些Rails魔法。在Rails 4.1.16中验证。

最简单的东西往往是最好的。较少的知识负债和代码库中的潜在混淆点。而且它‘just works’。

class AddStatusToItem < ActiveRecord::Migration
  def change
    add_column :items, :scheduler_type, :string, { null: false, default: "hotseat" }
  end
end

或者,在不创建新列的情况下更改列,然后执行以下操作:

class AddStatusToItem < ActiveRecord::Migration
  def change
    change_column_default :items, :scheduler_type, "hotseat"
  end
end

或者更好:

class AddStatusToItem < ActiveRecord::Migration
  def change
    change_column :items, :scheduler_type, :string, default: "hotseat"
  end
end

查看官方RoR指南以获得列更改方法中的选项。

null: false禁止在DB中使用null值,而且,作为一个额外的好处,它还会进行更新,以便所有先前为null的DB记录也会使用该字段的默认值设置。如果您愿意,您可以在迁移中排除这个参数,但我发现它非常方便!

Rails 5+的规范方式是,正如@Lucas Caton所说:

class Item < ActiveRecord::Base
  attribute :scheduler_type, :string, default: 'hotseat'
end

我使用属性默认的宝石

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

这里有一个我用过的解决方案,我有点惊讶还没有加入。

它分为两部分。第一部分是在实际迁移中设置默认值,第二部分是在模型中添加验证,以确保存在为真。

add_column :teams, :new_team_signature, :string, default: 'Welcome to the Team'

您将看到这里已经设置了默认值。现在在验证中,你想要确保字符串总是有一个值,那么就这样做吧

 validates :new_team_signature, presence: true

这将为您设置默认值。(对我来说,我有“Welcome to the Team”),然后它将进一步确保该对象始终存在一个值。

希望有帮助!

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

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

这是所有。

我希望这对你们有帮助