Rails:如何在ActiveRecord中设置默认值?

452
如何在 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模型中设置字段的默认值是否有规范的方法?


看起来你自己回答了这个问题,而且还有两种不同的变体 :) - Adam Byrtek
26
请注意,“self.status = ACTIVE unless self.status”的“标准”Ruby习惯用法是“self.status ||= ACTIVE”。 - Mike Woodhouse
1
Jeff Perrin的答案比当前标记为接受的答案好得多。default_scope不是设置默认值的可接受解决方案,因为它具有巨大的副作用,会改变查询的行为。 - lawrence
请参阅https://dev59.com/_W865IYBdhLWcg3wHK3u - Viktor Trón
2
鉴于这个问题的所有赞数,我认为Ruby需要一个用于ActiveRecord的setDefaultValue方法。 - spartikus
最佳答案:https://dev59.com/mHRC5IYBdhLWcg3wVvjL#41292328 - Blair Anderson
29个回答

0

虽然在大多数情况下,为设置默认值而这样做会令人困惑和笨拙,但你也可以使用:default_scope。请查看squil's comment here


0

已经用了一段时间了。

# post.rb
class Post < ApplicationRecord
  attribute :country, :string, default: 'ID'
end

0
如果该列恰好是“状态”类型列,并且您的模型适合使用状态机,则考虑使用aasm gem,之后您可以简单地执行
  aasm column: "status" do
    state :available, initial: true
    state :used
    # transitions
  end

它仍然不会为未保存的记录初始化值,但比起使用init或其他方法自己编写代码,这样做更加简洁,并且您可以获得aasm的其他好处,例如为所有状态提供范围。


0

0

这是我使用过的一个解决方案,我有点惊讶它还没有被添加进来。

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

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

所以你会看到这里已经设置了默认值。现在在验证中,您希望确保字符串始终有一个值,因此只需执行以下操作

 validates :new_team_signature, presence: true

这将为您设置默认值(对我来说是“欢迎加入团队”),然后它会进一步确保该对象始终存在一个值。

希望能有所帮助!


0
# db/schema.rb
create_table :store_listings, force: true do |t|
  t.string :my_string, default: "original default"
end

StoreListing.new.my_string # => "original default"

# app/models/store_listing.rb
class StoreListing < ActiveRecord::Base
  attribute :my_string, :string, default: "new default"
end

StoreListing.new.my_string # => "new default"

class Product < ActiveRecord::Base
  attribute :my_default_proc, :datetime, default: -> { Time.now }
end

Product.new.my_default_proc # => 2015-05-30 11:04:48 -0600
sleep 1
Product.new.my_default_proc # => 2015-05-30 11:04:49 -0600

0

在开发一个Rails 6应用时,我遇到了类似的挑战。

这是我如何解决它的方法

我有一个Users表和一个Roles表。 Users表属于Roles表。 我还有一个继承自Users表的AdminStudent模型。

当创建用户时,需要设置角色的默认值,例如具有id = 1admin角色或具有id = 2student角色。

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?

但第一个更清晰、更精确。

就这些。

希望这能有所帮助。


-2

在Rails 3中使用default_scope

API文档

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

讨论


如果您使用meta_where,由于一个错误,default_scope可能无法为新的AR对象分配默认值。 - Viktor Trón
这个meta_where问题现在已经被修复了 [http://metautonomous.lighthouseapp.com/projects/53011/tickets/36-default_scope-does-not-work] - Viktor Trón
3
不要使用 default_scope,因为它会在你设置的字段上添加这个条件,导致所有的查询都受到影响。这几乎永远不是你想要的结果。 - brad
@brad,有趣的是你提到了这个问题,我完全同意,它很邪恶:)。请参见我的评论http://stackoverflow.com/questions/10680845/owner-filteder-model-objects-on-rails-3。 - Viktor Trón

-2
从API文档http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html中可以看到,您可以在模型中使用before_validation方法,它为创建和更新调用提供了特定的初始化选项。例如,在此示例中(再次从API文档示例中获取代码),数字字段被初始化为信用卡。您可以轻松地将其调整为设置任何您想要的值。
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

很惊讶这里还没有人提出过这个建议


before_validation 不会在对象准备好被持久化之前设置默认值。如果处理过程需要在持久化之前读取默认值,那么这些值将不会准备好。 - mmell
你永远不会在验证检查期间设置默认值。这甚至不是任何黑客技巧。在初始化期间执行此操作。 - Sachin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接