当前位置:  开发笔记 > 编程语言 > 正文

如何在ActiveRecord中设置默认值?

如何解决《如何在ActiveRecord中设置默认值?》经验,为你挑选了9个好方法。

如何在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> Jeff Perrin..:

每种可用方法都存在几个问题,但我认为定义after_initialize回调是出于以下原因的方法:

    default_scope将初始化新模型的值,但随后将成为您找到模型的范围.如果您只想将某些数字初始化为0,那么这不是您想要的.

    在迁移定义的默认值也工作在部分时间......正如已经提到这个会不会当你只需要调用Model.new工作.

    覆盖initialize可以工作,但不要忘记打电话super!

    使用像phusion这样的插件有点荒谬.这是红宝石,我们真的需要一个插件来初始化一些默认值吗?

    从Rails 3开始,after_initialize 不推荐使用覆盖.当我after_initialize在rails 3.0.3中覆盖时,我在控制台中收到以下警告:

弃用警告:不推荐使用Base#after_initialize,请改用Base.after_initialize:方法.(来自/ 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    

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

注意事项:

    对于布尔字段,执行:

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

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

    如果您只为模型选择列的子集(即; select在类似查询中使用Person.select(:firstname, :lastname).all),您将获得一个MissingAttributeErrorif您的init方法是否访问未包含在该select子句中的列.你可以这样防范这种情况:

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

    并为布尔列...

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

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


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

2> Lucas Caton..:

在Rails 5+中,您可以在模型中使用属性方法,例如:

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


啊,这是我正在寻找的宝石!default也可以使用proc,例如default: - > {Time.current.to_date}

3> Laurent Farc..:

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

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

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


declan,有db/schema.rb
我想提及未来的读者:至少从我读过的内容来看,这与AR的原则相悖.模型的逻辑应该在模型类中,数据库应该尽可能无知.我的默认值构成了关于模型的特定逻辑.
另一个问题是当您需要外键的默认值时.您不能将ID值硬编码到外键字段中,因为在不同的DB上ID可能不同.
另一个问题是,通过这种方式,您无法初始化非持久访问器(非db列的属性).
另一个问题是您不一定能在一个​​地方看到所有默认值.他们可能会分散在不同的迁移中.

4> Joseph Lord..:

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

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`?

5> Brad Murray..:

只需执行以下操作即可改进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



6> Milan Novota..:

Phusion的家伙有一些不错的插件.



7> aidan..:

我用的是attribute-defaults宝石

从文档:运行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"



8> peterhurford..:

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

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

请参阅ActiveRecord :: Base文档中的 "覆盖默认访问器" 以及使用self时StackOverflow中的更多信息.



9> Blair Anders..:

类似的问题,但都有不同的上下文: - 如何在Rails activerecord的模型中为属性创建默认值?

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

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

您希望new.html表单在打开页面时具有默认值吗?使用/sf/ask/17360801/

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

推荐阅读
家具销售_903
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有