【问题标题】:Rails: How can I set default values in ActiveRecord?Rails:如何在 ActiveRecord 中设置默认值?
【发布时间】:2010-09-24 14:51:25
【问题描述】:

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

【问题讨论】:

  • 看起来你自己回答了这个问题,有两种不同的变体:)
  • 请注意,'self.status = ACTIVE 的“标准”Ruby 习语,除非 self.status' 是 'self.status ||= ACTIVE'
  • Jeff Perrin 的回答比目前标记为接受的要好得多。 default_scope 是设置默认值的不可接受的解决方案,因为它具有改变查询行为的巨大副作用。
  • 鉴于对这个问题的所有支持,我会说 Ruby 需要一个 setDefaultValue 方法用于 ActiveRecord

标签: ruby-on-rails rails-activerecord


【解决方案1】:

每个可用方法都有几个问题,但我认为定义after_initialize 回调是可行的方法,原因如下:

  1. default_scope 将为新模型初始化值,但这将成为您找到模型的范围。如果您只想将一些数字初始化为 0,那么这不是您想要的。
  2. 在您的迁移中定义默认值也有部分时间...正如已经提到的,当您调用 Model.new 时,这将不起作用
  3. 覆盖initialize 可以工作,但不要忘记调用super
  4. 使用像 phusion 这样的插件有点荒谬。这是 ruby​​,我们真的需要一个插件来初始化一些默认值吗?
  5. 从 Rails 3 开始,覆盖 after_initialize 已弃用。当我在 rails 3.0.3 中覆盖 after_initialize 时,我在控制台中收到以下警告:

弃用警告:Base#after_initialize 已被弃用,请改用 Base.after_initialize :method。 (从 /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    

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

注意事项:

  1. 对于布尔字段:

    self.bool_field = true if self.bool_field.nil?

    有关详细信息,请参阅 Paul Russell 对此答案的评论

  2. 如果您只为模型选择列的子集(即,在 Person.select(:firstname, :lastname).all 之类的查询中使用 select),如果您的 init 方法访问的列没有' t 被包含在select 子句中。您可以像这样防范这种情况:

    self.number ||= 0.0 if self.has_attribute? :number

    对于布尔列...

    self.bool_field = true if (self.has_attribute? :bool_value) &amp;&amp; self.bool_field.nil?

    另请注意,Rails 3.2 之前的语法有所不同(请参阅下面 Cliff Darling 的评论)

【讨论】:

  • 这绝对是实现这一目标的最佳方式。这真的很奇怪和不幸。在创建时建立模型属性默认值的明智首选方法似乎是 Rails 应该已经内置的东西。唯一的其他(可靠)方法,覆盖initialize,对于应该清晰和明确定义的东西似乎真的很复杂。在搜索这里之前,我花了几个小时浏览文档,因为我认为这个功能已经存在于某个地方,我只是不知道它。
  • 请注意 - 如果您有一个要默认的布尔字段,请不要执行 self.bool_field ||= true,因为即使您明确将其初始化为 false,这也会强制该字段为 true .而是使用self.bool_field = true if self.bool_field.nil?
  • 关于第 2 点,Model.new 实际上与迁移中定义的默认值一起工作(仅适用于我?),或者更准确地说,与表列的默认值一起工作。但我认识到 Jeff 基于 after_initialize 回调的方法可能是最好的方法。只是一个问题:它是否适用于脏但未保存的对象?在您的示例中, Person.new.number_was 会返回 0.0 吗?
  • 谨慎使用此方法并结合选择具有活动记录的特定列。在这种情况下,只会在对象中找到查询中指定的属性,并且初始化代码将抛出 MissingAttributeError。您可以添加额外的检查,如下所示:self.number ||= 0.0 if self.has_attribute? :number 对于布尔值:self.bool_field = true if (self.has_attribute? :bool_value) &amp;&amp; self.bool_field.nil?。这是 Rails 3.2+ - 之前,使用self.attributes.has_key?,你需要一个字符串而不是一个符号。
  • 对关联执行此操作将在查找时急切加载这些关联。以return if !new_record? 开头initialize 以避免性能问题。
【解决方案2】:

导轨 5+

您可以在模型中使用attribute 方法,例如:

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

您还可以将 lambda 传递给 default 参数。示例:

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

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

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

【讨论】:

  • 啊啊啊这就是我要找的宝石! default 也可以使用 proc,例如默认值:-> { Time.current.to_date }
  • 确保将类型指定为第二个参数,否则类型将是Value,并且不会进行类型转换。
  • 令我高兴的是,这也适用于 store_accessor,例如给定store_accessor :my_jsonb_column, :locale,然后您可以定义attribute :locale, :string, default: 'en'
  • 哦,太棒了,我需要在表单中显示默认值,这很好用。谢谢卢卡斯。
  • 仍然可以将这些设置为nil。如果他们不能是nil DB not null + DB default + github.com/sshaw/keep_defaults 是我的经验
【解决方案3】:

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

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

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

【讨论】:

  • 另一个问题是当你想要一个外键的默认值时。您不能将 ID 值硬编码到外键字段中,因为在不同的 DB 上 ID 可能不同。
  • 还有一个问题是,这样你就不能初始化非持久访问器(不是数据库列的属性)。
  • 另一个问题是你不一定能在一个​​地方看到所有的默认值。它们可能分散在不同的迁移中。
  • declan,有 db/schema.rb
  • 我想为未来的读者提一提:至少从我所读到的内容来看,这与 AR 的原则背道而驰。模型的逻辑应该存在于模型类中,并且数据库应该尽可能地无知。对我来说,默认值构成了关于模型的特定逻辑。
【解决方案4】:

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

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

【讨论】:

  • 这是非常重要的一点。我不得不想象,在大多数情况下,设置记录的默认值只是在持久化新记录之前完成,而不是在加载持久化记录时完成。
  • 谢谢老兄,你拯救了我的一天。
  • :before_create 怎么样?
  • :before_create 如何处理单独的新调用和保存调用?在切换到它之前,我想检查一下并真正了解它。
【解决方案5】:

after_initialize 回调模式可以通过简单地执行以下操作来改进

after_initialize :some_method_goes_here, :if => :new_record?

如果您的 init 代码需要处理关联,这有一个重要的好处,因为如果您读取初始记录而不包括关联,则以下代码会触发微妙的 n+1。

class Account

  has_one :config
  after_initialize :init_config

  def init_config
    self.config ||= build_config
  end

end

【讨论】:

    【解决方案6】:

    Phusion 的人对此有一些很好的plugin

    【讨论】:

    • 注意,此插件允许架构迁移中的 :default 值与 Model.new 一起“正常工作”。
    • 我可以将迁移中的 :default 值与 Model.new 一起“正常工作”,这与 Jeff 在他的帖子中所说的相反。已验证在 Rails 4.1.16 中工作。
    【解决方案7】:

    比建议的答案更好/更清洁的潜在方法是覆盖访问器,如下所示:

    def status
      self['status'] || ACTIVE
    end
    

    参见the ActiveRecord::Base documentationmore from StackOverflow on using self 中的“覆盖默认访问器”。

    【讨论】:

    • attributes 返回的哈希值中的状态仍然为零。在 Rails 5.2.0 中测试。
    【解决方案8】:

    我使用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"
    

    【讨论】:

      【解决方案9】:

      类似的问题,但都略有不同的上下文: - How do I create a default value for attributes in Rails activerecord's model?

      最佳答案:取决于你想要什么!

      如果您希望每个对象都以一个值开头:使用after_initialize :init

      您希望new.html 表单在打开页面时具有默认值?使用https://stackoverflow.com/a/5127684/1536309

      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 
      

      如果您希望每个对象都有一个根据用户输入计算的值:使用 before_save :default_values 您希望用户输入X,然后输入Y = X+'foo'?使用:

      class Task < ActiveRecord::Base
        before_save :default_values
        def default_values
          self.status ||= 'P'
        end
      end
      

      【讨论】:

        【解决方案10】:

        我也看到人们将它放在迁移中,但我更愿意看到它 在模型代码中定义。

        是否有规范的方法来设置字段的默认值 ActiveRecord 模型?

        在 Rails 5 之前,规范的 Rails 方式实际上是在迁移中设置它,只要想查看数据库为任何模型设置的默认值,只需查看 db/schema.rb 即可。

        与@Jeff Perrin 的回答状态相反(这有点旧),迁移方法甚至会在使用Model.new 时应用默认值,因为 Rails 的一些魔力。已验证在 Rails 4.1.16 中工作。

        最简单的东西往往是最好的。减少代码库中的知识债务和潜在的混淆点。而且它“有效”。

        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 guide了解列更改方法的选项。

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

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

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

        【讨论】:

          【解决方案11】:

          这就是构造函数的用途!覆盖模型的initialize 方法。

          使用after_initialize 方法。

          【讨论】:

          • 通常你是正确的,但你不应该在 ActiveRecord 模型中覆盖初始化,因为它可能并不总是被调用。您应该改用after_initialize 方法。
          • 使用 default_scope 来设置默认值肯定是错误的。 after_initialize 是正确的答案。
          【解决方案12】:

          伙计们,我最终做了以下事情:

          def after_initialize 
           self.extras||={}
           self.other_stuff||="This stuff"
          end
          

          像魅力一样工作!

          【讨论】:

            【解决方案13】:

            这个问题已经回答很久了,但是我经常需要默认值并且不希望将它们放入数据库中。我创建了一个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
            

            【讨论】:

              【解决方案14】:

              我在进行复杂查找时遇到了after_initialize 给出ActiveModel::MissingAttributeError 错误的问题:

              例如:

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

              .where 中的“搜索”是条件哈希

              所以我最终通过以这种方式覆盖初始化来做到这一点:

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

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

              【讨论】:

              • 我喜欢它。我需要在 Rails 5.2.0 中做def initialize(*); super; default_values; end。此外,即使在 .attributes 哈希中,默认值也是可用的。
              【解决方案15】:

              after_initialize 方法已弃用,请改用回调。

              after_initialize :defaults
              
              def defaults
                self.extras||={}
                self.other_stuff||="This stuff"
              end
              

              但是,在迁移中使用 :default 仍然是最干净的方式。

              【讨论】:

              • 在 Rails 3 中:after_initialize 方法不推荐使用。其实宏风格的回调你举了一个IS deprecated的例子。详情:guides.rubyonrails.org/…
              【解决方案16】:

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

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

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

              【讨论】:

              • 这个解决方案和建议的替代方案在我的情况下不起作用:我有一个 STI 类层次结构,其中只有一个类具有该属性,以及将在数据库查询条件中使用的相应列。跨度>
              【解决方案17】:
              class Item < ActiveRecord::Base
                def status
                  self[:status] or ACTIVE
                end
              
                before_save{ self.status ||= ACTIVE }
              end
              

              【讨论】:

              • Mmmhh... 乍一看似乎很巧妙,但经过一番思考,我发现了一些问题。首先,所有默认值都不是在一个点上,而是分散在整个类中(想象一下搜索它们或更改它们)。第二个也是最糟糕的,你以后不能输入一个空值(甚至是一个假值!)。
              • 为什么需要将空值设置为默认值?您无需执行任何操作即可使用 AR 开箱即用。至于使用布尔列时的 false 那么你是对的,这不是最好的方法。
              • 我不能代表其他编码习惯我没有遇到问题,因为我没有将我的 getter/setter 分散在类文件周围。此外,任何现代文本编辑器都应该可以轻松导航到方法(textmate 中的 shift-cmd-t)。
              • @paradoja - 我收回了这一点,我现在看到它在哪里也使用 null。不一定使用 null 作为默认值,但如果您确实想在某个时候将值更改为 null。很好的收获@paradoja,谢谢。
              • 我使用这种方法,因为它适用于动态生成的属性。
              【解决方案18】:

              我强烈建议使用“default_value_for”gem:https://github.com/FooBarWidget/default_value_for

              有些棘手的场景需要重写初始化方法,gem 就是这样做的。

              例子:

              您的数据库默认值为 NULL,您的模型/ruby 定义的默认值为“某个字符串”,但实际上您想要出于任何原因将该值设置为 nil:MyModel.new(my_attr: nil)

              这里的大多数解决方案都无法将值设置为 nil,而是将其设置为默认值。

              好的,所以不要采用||= 方法,而是切换到my_attr_changed?...

              但是现在假设您的数据库默认值是“某个字符串”,您的模型/ruby 定义的默认值是“某个其他字符串”,但是在某种情况下,您想要 将值设置为“一些字符串”(数据库默认值):MyModel.new(my_attr: 'some_string')

              这将导致 my_attr_changed?false,因为该值与 db 默认值匹配,这反过来将触发您的 ruby​​ 定义的默认代码并将值设置为“其他字符串”--再次,不是你想要的。


              出于这些原因,我认为仅使用 after_initialize 钩子无法正确完成。

              再次,我认为“default_value_for” gem 采用了正确的方法:https://github.com/FooBarWidget/default_value_for

              【讨论】:

                【解决方案19】:

                Rails 6.1+

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

                attribute :status, default: ACTIVE
                

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

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

                after_initialize :do_something_that_references_instance_or_associations, if: :new_record?
                

                【讨论】:

                • after_initialize 不是只为新记录运行吗?
                • 您可能会惊讶地发现after_initialize 回调在从数据库加载现有记录后运行。
                【解决方案20】:

                虽然在大多数情况下设置默认值会让人感到困惑和尴尬,但您也可以使用:default_scope。查看squil's comment here

                【讨论】:

                  【解决方案21】:

                  我发现使用验证方法可以很好地控制设置默认值。您甚至可以为更新设置默认值(或验证失败)。如果您真的想要,您甚至可以为插入和更新设置不同的默认值。 请注意,在 #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

                  【讨论】:

                  • 验证不是只在保存之前发生吗?如果您想在保存之前显示默认值怎么办?
                  • @nurettin 这是一个很好的观点,我明白你为什么有时会想要这样做,但 OP 没有提到这是一项要求。您必须自己决定是否需要在每个实例上设置默认值的开销,即使它没有保存。另一种方法是保留一个虚拟对象以供 new 操作重复使用。
                  【解决方案22】:

                  如果该列恰好是“状态”类型的列,并且您的模型适合使用状态机,请考虑使用aasm gem,之后您可以简单地执行此操作

                    aasm column: "status" do
                      state :available, initial: true
                      state :used
                      # transitions
                    end
                  

                  它仍然不会初始化未保存记录的值,但它比使用 init 或其他方式滚动您自己的值要干净一些,并且您可以获得 aasm 的其他好处,例如所有状态的范围。

                  【讨论】:

                    【解决方案23】:

                    https://github.com/keithrowell/rails_default_value

                    class Task < ActiveRecord::Base
                      default :status => 'active'
                    end
                    

                    【讨论】:

                      【解决方案24】:

                      这是我使用的一个解决方案,但我有点惊讶尚未添加。

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

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

                      所以你会看到这里已经设置了默认值。现在在验证中,您要确保字符串始终有一个值,所以只需这样做

                       validates :new_team_signature, presence: true
                      

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

                      希望有帮助!

                      【讨论】:

                        【解决方案25】:
                        # 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
                        

                        【讨论】:

                          【解决方案26】:

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

                          但第一个更干净,更精确。

                          就是这样。

                          我希望这会有所帮助

                          【讨论】:

                            【解决方案27】:

                            已经用了一段时间了。

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

                            【讨论】:

                              【解决方案28】:

                              在 Rails 3 中使用 default_scope

                              api doc

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

                              discussion

                              【讨论】:

                              【解决方案29】:

                              来自 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
                              

                              很惊讶这里没有推荐他的人

                              【讨论】:

                              • before_validation 在对象准备好持久化之前不会设置默认值。如果进程需要在持久化之前读取默认值,则这些值不会准备好。
                              • 无论如何,在验证检查期间您永远不会设置默认值。它甚至不是任何黑客。在初始化期间执行此操作
                              猜你喜欢
                              • 1970-01-01
                              • 2010-11-14
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 2015-05-14
                              • 1970-01-01
                              • 1970-01-01
                              相关资源
                              最近更新 更多