如何在 Rails 中设置默认值?

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

The best I can think of is to set the default value in the new method in the controller.

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

121166 次浏览

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

有许多方法可以处理它,这个 插件看起来是一个有趣的选择。

如果您引用的是 ActiveRecord 对象,那么您有(多于)两种方法来实现这一点:

1. 在 DB 中使用: default 参数

脑电图。

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

可以重写 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

“正确”在 Ruby 是个危险的词。做任何事情通常都有不止一种方法。如果您知道 一直都是希望该表上的列具有这个默认值,那么在 DB 迁移文件中设置它们是最简单的方法:

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(),除非您用其他方法覆盖它。

覆盖 new/initialize 的建议可能不完整。Rails 会(经常)调用为 ActiveRecord 对象分配,而要分配的调用不会导致要初始化的调用。

如果您讨论的是 ActiveRecord 对象,那么可以看一下在 _ initialize 之后重写。

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

默认值 未调用默认构造函数

[编辑: SFEley 指出,当 Rails 在内存中实例化一个新对象时,它确实会查看数据库中的默认值——我没有意识到这一点。]

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

Your best option is to put your defaults into your migration:

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

第二个最好的方法是在模型中放置默认值,但是这只适用于最初为空的属性。你可能会像我对待 boolean栏目那样遇到麻烦:

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

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

您需要 ||=来阻止 Rails 重写传递到 initialize 方法中的参数。

我回答了一个类似的问题 给你。一个干净的方法是使用 Rails Attr _ accessor _ with _ default

class SOF
attr_accessor_with_default :is_awesome,true
end


sof = SOF.new
sof.is_awesome


=> true

更新

Attr _ accessor _ with _ default 在 Rails 3.2中已经被弃用了. . 你可以用纯 Ruby 来代替

class SOF
attr_writer :is_awesome


def is_awesome
@is_awesome ||= true
end
end


sof = SOF.new
sof.is_awesome


#=> true

Based on SFEley's answer, here is an updated/fixed one for newer Rails versions:

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

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

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

10作为默认值在这里不起作用,因为它是一个布尔字段。它必须是 truefalse值。

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

对于查找器找到并实例化的每个对象,都会触发 after _ initialize 回调,而 after _ initialize 也会在新对象实例化后触发 (see ActiveRecord Callbacks).

因此,我认为它应该是这样的:

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

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

At best this is hacky...

EDIT-使用‘ new _ record?’检查是否从新调用实例化

与其检查属性值,不如使用内置的 new_record?方法和 ails:

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 的魔力比我聪明多了。

您还可以在迁移中尝试 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 文档

If you're talking about ActiveRecord objects, I use the 'attribute-defaults' gem.

文档和下载: https://github.com/bsm/attribute-defaults

我需要设置一个默认值,就像它在 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)

一种可能比提出的答案更好/更干净的潜在方法是覆盖访问器,如下所示:

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

See "Overwriting default accessors" in ActiveRecord: : 基本文档 and 更多来自 StackOverflow 的关于使用 self 的信息.

您可以使用 ails _ default _ value gem。例如:

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

Https://github.com/keithrowell/rails_default_value

In case you're dealing with a Model, you can use the Attriutes API in Rails 5+ Http://api.rubyonrails.org/classes/activerecord/attributes/classmethods.html#method-i-attribute

只需添加一个具有适当列名的迁移,然后在模型中将其设置为:

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

生成迁移并使用 change_column_default,它是简洁和可逆的:

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