如何在Rails中设置默认值?

时间:2009-07-27 04:13:45

标签: ruby-on-rails ruby

我正在尝试找到在Rails中为对象设置默认值的最佳方法。

我能想到的最好的方法是在控制器的new方法中设置默认值。

如果可以接受或者有更好的方法,是否有人有任何意见?

17 个答案:

答案 0 :(得分:94)

“正确”在Ruby中是一个危险的词。通常有多种方法可以做任何事情。如果您知道总是想要该表上该列的默认值,那么在数据库迁移文件中设置它们是最简单的方法:

class SetDefault < ActiveRecord::Migration
  def self.up
    change_column :people, :last_name, :type, :default => "Doe"
  end

  def self.down
    # You can't currently remove default values in Rails
    raise ActiveRecord::IrreversibleMigration, "Can't remove the default"
  end
end

由于ActiveRecord会自动发现您的表和列属性,因此在任何标准Rails应用程序中使用它都会导致在任何模型中设置相同的默认值。

但是,如果您只想要在特定情况下设置默认值 - 比如,它是一个与其他一些共享表的继承模型 - 那么另一种优雅方式是在创建模型对象时直接在Rails代码中执行:

class GenericPerson < Person
  def initialize(attributes=nil)
    attr_with_defaults = {:last_name => "Doe"}.merge(attributes)
    super(attr_with_defaults)
  end
end

然后,当你执行GenericPerson.new()时,它总是将“Doe”属性涓流到Person.new(),除非你用别的东西覆盖它。

答案 1 :(得分:55)

根据SFEley的回答,这里是针对较新Rails版本的更新/修复版本:

class SetDefault < ActiveRecord::Migration
  def change
    change_column :table_name, :column_name, :type, default: "Your value"
  end
end

答案 2 :(得分:20)

首先,你不能重载initialize(*args),因为在所有情况下都没有调用它。

您最好的选择是将默认值放入迁移中:

add_column :accounts, :max_users, :integer, :default => 10

第二个最好的方法是将默认值放入模型中,但这只适用于最初为零的属性。您可能会遇到boolean列的问题:

def after_initialize
  if new_record?
    max_users ||= 10
  end
end

您需要new_record?,因此默认值不会覆盖从数据库加载的值。

您需要||=来阻止Rails覆盖传递给initialize方法的参数。

答案 3 :(得分:13)

您还可以在迁移中尝试change_column_default(在Rails 3.2.8中测试):

class SetDefault < ActiveRecord::Migration
  def up
    # Set default value
    change_column_default :people, :last_name, "Smith"
  end

  def down
    # Remove default
    change_column_default :people, :last_name, nil
  end
end

change_column_default Rails API docs

答案 4 :(得分:7)

如果你指的是ActiveRecord对象,你有两种方法可以做到这一点:

1。在DB

中使用:default参数

E.G。

class AddSsl < ActiveRecord::Migration
  def self.up
    add_column :accounts, :ssl_enabled, :boolean, :default => true
  end

  def self.down
    remove_column :accounts, :ssl_enabled
  end
end

此处有更多信息:http://api.rubyonrails.org/classes/ActiveRecord/Migration.html

2。使用回调

E.G。 before_validation_on_create

此处有更多信息:http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html#M002147

答案 5 :(得分:7)

在Ruby on Rails v3.2.8中,使用after_initialize ActiveRecord回调,您可以在模型中调用一个方法,为新对象分配默认值。

  

对于由查找程序找到并实例化的每个对象触发after_initialize回调,并在实例化新对象后触发after_initialize   (see ActiveRecord Callbacks)。

所以,IMO应该看起来像:

class Foo < ActiveRecord::Base
  after_initialize :assign_defaults_on_new_Foo
  ...
  attr_accessible :bar
  ...
  private
  def assign_defaults_on_new_Foo
    # required to check an attribute for existence to weed out existing records
    self.bar = default_value unless self.attribute_whose_presence_has_been_validated
  end
end
此实例的

Foo.bar = default_value除非实例在保存/更新时包含attribute_whose_presence_has_been_validated。然后default_value将与您的视图结合使用,以使用default_value属性的bar呈现表单。

充其量这是hacky ......

编辑 - 使用'new_record?'检查是否从新呼叫实例化

不使用检查属性值,而是使用带有rails的new_record?内置方法。因此,上面的示例应如下所示:

class Foo < ActiveRecord::Base
  after_initialize :assign_defaults_on_new_Foo, if: 'new_record?'
  ...
  attr_accessible :bar
  ...
  private
  def assign_defaults_on_new_Foo
    self.bar = default_value
  end
end

这更清洁。啊,Rails的魔力 - 它比我聪明。

答案 6 :(得分:5)

至少对于Rails 3.2.6中的布尔字段,这将适用于您的迁移。

def change
  add_column :users, :eula_accepted, :boolean, default: false
end

10置于默认值将无效,因为它是一个布尔字段。它必须是truefalse值。

答案 7 :(得分:4)

如果要处理模型,则可以在Rails 5+中使用Attriutes API http://api.rubyonrails.org/classes/ActiveRecord/Attributes/ClassMethods.html#method-i-attribute

只需添加具有正确列名的迁移,然后在模型中使用以下命令进行设置即可:

class StoreListing < ActiveRecord::Base
  attribute :country, :string, default: 'PT'
end

答案 8 :(得分:1)

如果您只是为数据库支持的模型的某些属性设置默认值,我会考虑使用sql默认列值 - 您能否说明您使用的默认类型是什么?

有很多方法可以处理它,plugin看起来像一个有趣的选项。

答案 9 :(得分:1)

覆盖新/初始化的建议可能不完整。 Rails将(经常)为ActiveRecord对象调用allocate,而对allocate的调用不会导致调用初始化。

如果您正在讨论ActiveRecord对象,请查看覆盖after_initialize。

这些博客文章(不是我的)非常有用:

Default values Default constructors not called

[编辑:SFEley指出Rails在实例化内存中的新对象时实际上确实查看了数据库中的默认值 - 我没有意识到。]

答案 10 :(得分:1)

我需要设置一个默认值,就好像它被指定为DB中的默认列值一样。所以它的行为就像这样

a = Item.new
a.published_at # => my default value

a = Item.new(:published_at => nil)
a.published_at # => nil

因为在从参数设置属性后调用了after_initialize回调,所以无法知道该属性是否为nil,因为它从未设置过,或者因为故意将其设置为nil。所以我不得不捅了一下这个简单的解决方案。

class Item < ActiveRecord::Base
  def self.column_defaults
    super.merge('published_at' => Time.now)
  end
end

非常适合我。 (Rails 3.2.x)

答案 11 :(得分:1)

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

def status
  self['name_of_var'] || 'desired_default_value'
end

请参阅&#34;覆盖默认访问者&#34;在the ActiveRecord::Base documentationmore from StackOverflow on using self

答案 12 :(得分:0)

我回答了类似的问题here ..一个干净的方法就是使用Rails attr_accessor_with_default

class SOF
  attr_accessor_with_default :is_awesome,true
end

sof = SOF.new
sof.is_awesome

=> true

<强>更新

Rails 3.2中已经弃用了

attr_accessor_with_default ..您可以使用纯Ruby

来执行此操作
class SOF
  attr_writer :is_awesome

  def is_awesome
    @is_awesome ||= true
  end
end

sof = SOF.new
sof.is_awesome

#=> true

答案 13 :(得分:0)

如果您正在谈论ActiveRecord对象,我使用'attribute-defaults'gem。

文档&amp;下载:https://github.com/bsm/attribute-defaults

答案 14 :(得分:0)

您可以使用rails_default_value gem。例如:

class Foo < ActiveRecord::Base
  # ...
  default :bar => 'some default value'
  # ...
end

https://github.com/keithrowell/rails_default_value

答案 15 :(得分:0)

生成迁移并使用change_column_default,简洁且可逆:

class SetDefaultAgeInPeople < ActiveRecord::Migration[5.2]
  def change
    change_column_default :people, :age, { from: nil, to: 0 }
  end
end

答案 16 :(得分:-3)

您可以覆盖ActiveRecord模型的构造函数。

像这样:

def initialize(*args)
  super(*args)
  self.attribute_that_needs_default_value ||= default_value
  self.attribute_that_needs_another_default_value ||= another_default_value
  #ad nauseum
end