I'm trying to find the best way to set default values for objects in Rails.
我正在寻找为Rails中的对象设置默认值的最佳方法。
The best I can think of is to set the default value in the new
method in the controller.
我能想到的最好的方法是在控制器中设置新方法的默认值。
Does anyone have any input if this is acceptable or if there's a better way to do it?
如果这是可以接受的,或者有更好的方法,有人有意见吗?
15 个解决方案
#1
88
"Correct" is a dangerous word in Ruby. There's usually more than one way to do anything. If you know you'll always want that default value for that column on that table, setting them in a DB migration file is the easiest way:
在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
Because ActiveRecord autodiscovers your table and column properties, this will cause the same default to be set in any model using it in any standard Rails app.
由于ActiveRecord自动发现表和列属性,这将导致在任何模型中使用它在任何标准Rails应用程序中设置相同的默认值。
However, if you only want default values set in specific cases -- say, it's an inherited model that shares a table with some others -- then another elegant way is do it directly in your Rails code when the model object is created:
但是,如果您只希望在特定的情况下设置默认值——例如,它是一个继承的模型,与其他一些模型共享一个表——那么另一种优雅的方法是在创建模型对象时直接在Rails代码中执行:
class GenericPerson < Person
def initialize(attributes=nil)
attr_with_defaults = {:last_name => "Doe"}.merge(attributes)
super(attr_with_defaults)
end
end
Then, when you do a GenericPerson.new()
, it'll always trickle the "Doe" attribute up to Person.new()
unless you override it with something else.
然后,当您执行一个GenericPerson.new()时,它总是会将“Doe”属性输入到Person.new(),除非您用其他东西覆盖它。
#2
54
Based on SFEley's answer, here is an updated/fixed one for newer Rails versions:
根据SFEley的回答,以下是更新的/修复的版本:
class SetDefault < ActiveRecord::Migration
def change
change_column :table_name, :column_name, :type, default: "Your value"
end
end
#3
20
First of all you can't overload initialize(*args)
as it's not called in all cases.
首先,您不能重载initialize(*args),因为它并非在所有情况下都被调用。
Your best option is to put your defaults into your migration:
您最好的选择是将您的默认值放入您的迁移中:
add_column :accounts, :max_users, :integer, :default => 10
Second best is to place defaults into your model but this will only work with attributes that are initially nil. You may have trouble as I did with boolean
columns:
第二种方法是将默认值放置到模型中,但这只适用于最初为nil的属性。你可能会遇到麻烦,就像我在布尔列中遇到的那样:
def after_initialize
if new_record?
max_users ||= 10
end
end
You need the new_record?
so the defaults don't override values loaded from the datbase.
你需要new_record吗?因此,默认值不会覆盖从datbase加载的值。
You need ||=
to stop Rails from overriding parameters passed into the initialize method.
您需要||=阻止Rails重写传递给initialize方法的参数。
#4
12
You can also try change_column_default
in your migrations (tested in Rails 3.2.8):
您还可以在您的迁移中尝试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
change_column_default Rails API文档
#5
7
If you are referring to ActiveRecord objects, you have (more than) two ways of doing this:
如果你指的是ActiveRecord对象,你有(不止)两种方法:
1. Use a :default parameter in the DB
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
More info here: http://api.rubyonrails.org/classes/ActiveRecord/Migration.html
更多信息:http://api.rubyonrails.org/classes/ActiveRecord/Migration.html
2. Use a callback
E.G. before_validation_on_create
例如before_validation_on_create
More info here: http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html#M002147
更多信息:http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html M002147
#6
7
In Ruby on Rails v3.2.8, using the after_initialize
ActiveRecord callback, you can call a method in your model that will assign the default values for a new object.
在Ruby on Rails v3.2.8中,使用after_initialize ActiveRecord回调,可以调用模型中的一个方法,该方法将为新对象分配默认值。
after_initialize callback is triggered for each object that is found and instantiated by a finder, with after_initialize being triggered after new objects are instantiated as well (see ActiveRecord Callbacks).
after_initialize回调对于查找程序发现并实例化的每个对象都将被触发,而after_initialize也将在新对象实例化之后被触发(参见ActiveRecord回调)。
So, IMO it should look something like:
所以,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
for this instance unless the instance contains an attribute_whose_presence_has_been_validated
previously on save/update. The default_value
will then be used in conjunction with your view to render the form using the default_value
for the bar
attribute.
Foo。这个实例的bar = default_value,除非该实例在保存/更新之前包含一个attribute_whose_presence_has_been_been_validate。然后,default_value将与您的视图一起使用,使用bar属性的default_value来呈现表单。
At best this is hacky...
充其量这是一种黑客行为……
EDIT - use 'new_record?' to check if instantiating from a new call
Instead of checking an attribute value, use the new_record?
built-in method with rails. So, the above example should look like:
不是检查属性值,而是使用new_record?使用rails内置方法。因此,上面的例子应该是:
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
This is much cleaner. Ah, the magic of Rails - it's smarter than me.
这是干净了很多。啊,Rails的魔力——它比我聪明。
#7
5
For boolean fields in Rails 3.2.6 at least, this will work in your migration.
至少对于Rails 3.2.6中的布尔字段,这将在迁移中起作用。
def change
add_column :users, :eula_accepted, :boolean, default: false
end
Putting a 1
or 0
for a default will not work here, since it is a boolean field. It must be a true
or false
value.
输入1或0作为默认值在这里不起作用,因为它是一个布尔字段。它必须是一个真值或假值。
#8
1
If you are just setting defaults for certain attributes of a database backed model I'd consider using sql default column values - can you clarify what types of defaults you are using?
如果您只是为数据库支持的模型的某些属性设置默认值,我将考虑使用sql默认列值——您能澄清您使用的默认类型吗?
There are a number of approaches to handle it, this plugin looks like an interesting option.
有很多方法可以处理它,这个插件看起来很有趣。
#9
1
The suggestion to override new/initialize is probably incomplete. Rails will (frequently) call allocate for ActiveRecord objects, and calls to allocate won't result in calls to initialize.
重写new/initialize的建议可能是不完整的。Rails将(经常)调用ActiveRecord对象的分配,而调用分配不会导致调用初始化。
If you're talking about ActiveRecord objects, take a look at overriding after_initialize.
如果您正在讨论ActiveRecord对象,请查看覆盖after_initialize。
These blog posts (not mine) are useful:
这些博客文章(不是我的)很有用:
Default values Default constructors not called
默认值默认构造函数未调用
[Edit: SFEley points out that Rails actually does look at the default in the database when it instantiates a new object in memory - I hadn't realized that.]
[编辑:SFEley指出当Rails在内存中实例化一个新对象时,它确实会查看数据库中的默认值——我还没有意识到这一点。]
#10
1
I needed to set a default just as if it was specified as default column value in DB. So it behaves like this
我需要设置一个默认值,就像它在DB中被指定为默认列值一样。它是这样的
a = Item.new
a.published_at # => my default value
a = Item.new(:published_at => nil)
a.published_at # => nil
Because after_initialize callback is called after setting attributes from arguments, there was no way to know if the attribute is nil because it was never set or because it was intentionally set as nil. So I had to poke inside a bit and came with this simple solution.
因为after_initialize回调是在设置来自参数的属性之后调用的,所以无法知道属性是否为nil,因为它从未被设置,或者因为它被故意设置为nil。所以我需要深入研究,得出这个简单的解。
class Item < ActiveRecord::Base
def self.column_defaults
super.merge('published_at' => Time.now)
end
end
Works great for me. (Rails 3.2.x)
工作非常适合我。(Rails 3.2.x)
#11
1
A potentially even better/cleaner potential way than the answers proposed is to overwrite the accessor, like this:
一个可能比建议的答案更好/更干净的方法是覆盖访问器,如下所示:
def status
self['name_of_var'] || 'desired_default_value'
end
See "Overwriting default accessors" in the ActiveRecord::Base documentation and more from * on using self.
请参阅ActiveRecord::Base文档中的“覆盖默认访问器”,更多来自使用self的*。
#12
0
i answered a similar question here.. a clean way to do this is using Rails attr_accessor_with_default
我在这里回答了一个类似的问题。一种干净的方法是使用Rails attr_accessor_with_default
class SOF
attr_accessor_with_default :is_awesome,true
end
sof = SOF.new
sof.is_awesome
=> true
UPDATE
更新
attr_accessor_with_default has been deprecated in Rails 3.2.. you could do this instead with pure Ruby
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
#13
0
If you're talking about ActiveRecord objects, I use the 'attribute-defaults' gem.
如果您正在谈论ActiveRecord对象,我将使用“attribute-defaults”gem。
Documentation & download: https://github.com/bsm/attribute-defaults
文档和下载:https://github.com/bsm/attribute-defaults
#14
0
You could use the rails_default_value gem. eg:
您可以使用rails_default_value gem。例如:
class Foo < ActiveRecord::Base
# ...
default :bar => 'some default value'
# ...
end
https://github.com/keithrowell/rails_default_value
https://github.com/keithrowell/rails_default_value
#15
-3
You can override the constructor for the ActiveRecord model.
您可以重写ActiveRecord模型的构造函数。
Like this:
是这样的:
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
#1
88
"Correct" is a dangerous word in Ruby. There's usually more than one way to do anything. If you know you'll always want that default value for that column on that table, setting them in a DB migration file is the easiest way:
在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
Because ActiveRecord autodiscovers your table and column properties, this will cause the same default to be set in any model using it in any standard Rails app.
由于ActiveRecord自动发现表和列属性,这将导致在任何模型中使用它在任何标准Rails应用程序中设置相同的默认值。
However, if you only want default values set in specific cases -- say, it's an inherited model that shares a table with some others -- then another elegant way is do it directly in your Rails code when the model object is created:
但是,如果您只希望在特定的情况下设置默认值——例如,它是一个继承的模型,与其他一些模型共享一个表——那么另一种优雅的方法是在创建模型对象时直接在Rails代码中执行:
class GenericPerson < Person
def initialize(attributes=nil)
attr_with_defaults = {:last_name => "Doe"}.merge(attributes)
super(attr_with_defaults)
end
end
Then, when you do a GenericPerson.new()
, it'll always trickle the "Doe" attribute up to Person.new()
unless you override it with something else.
然后,当您执行一个GenericPerson.new()时,它总是会将“Doe”属性输入到Person.new(),除非您用其他东西覆盖它。
#2
54
Based on SFEley's answer, here is an updated/fixed one for newer Rails versions:
根据SFEley的回答,以下是更新的/修复的版本:
class SetDefault < ActiveRecord::Migration
def change
change_column :table_name, :column_name, :type, default: "Your value"
end
end
#3
20
First of all you can't overload initialize(*args)
as it's not called in all cases.
首先,您不能重载initialize(*args),因为它并非在所有情况下都被调用。
Your best option is to put your defaults into your migration:
您最好的选择是将您的默认值放入您的迁移中:
add_column :accounts, :max_users, :integer, :default => 10
Second best is to place defaults into your model but this will only work with attributes that are initially nil. You may have trouble as I did with boolean
columns:
第二种方法是将默认值放置到模型中,但这只适用于最初为nil的属性。你可能会遇到麻烦,就像我在布尔列中遇到的那样:
def after_initialize
if new_record?
max_users ||= 10
end
end
You need the new_record?
so the defaults don't override values loaded from the datbase.
你需要new_record吗?因此,默认值不会覆盖从datbase加载的值。
You need ||=
to stop Rails from overriding parameters passed into the initialize method.
您需要||=阻止Rails重写传递给initialize方法的参数。
#4
12
You can also try change_column_default
in your migrations (tested in Rails 3.2.8):
您还可以在您的迁移中尝试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
change_column_default Rails API文档
#5
7
If you are referring to ActiveRecord objects, you have (more than) two ways of doing this:
如果你指的是ActiveRecord对象,你有(不止)两种方法:
1. Use a :default parameter in the DB
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
More info here: http://api.rubyonrails.org/classes/ActiveRecord/Migration.html
更多信息:http://api.rubyonrails.org/classes/ActiveRecord/Migration.html
2. Use a callback
E.G. before_validation_on_create
例如before_validation_on_create
More info here: http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html#M002147
更多信息:http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html M002147
#6
7
In Ruby on Rails v3.2.8, using the after_initialize
ActiveRecord callback, you can call a method in your model that will assign the default values for a new object.
在Ruby on Rails v3.2.8中,使用after_initialize ActiveRecord回调,可以调用模型中的一个方法,该方法将为新对象分配默认值。
after_initialize callback is triggered for each object that is found and instantiated by a finder, with after_initialize being triggered after new objects are instantiated as well (see ActiveRecord Callbacks).
after_initialize回调对于查找程序发现并实例化的每个对象都将被触发,而after_initialize也将在新对象实例化之后被触发(参见ActiveRecord回调)。
So, IMO it should look something like:
所以,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
for this instance unless the instance contains an attribute_whose_presence_has_been_validated
previously on save/update. The default_value
will then be used in conjunction with your view to render the form using the default_value
for the bar
attribute.
Foo。这个实例的bar = default_value,除非该实例在保存/更新之前包含一个attribute_whose_presence_has_been_been_validate。然后,default_value将与您的视图一起使用,使用bar属性的default_value来呈现表单。
At best this is hacky...
充其量这是一种黑客行为……
EDIT - use 'new_record?' to check if instantiating from a new call
Instead of checking an attribute value, use the new_record?
built-in method with rails. So, the above example should look like:
不是检查属性值,而是使用new_record?使用rails内置方法。因此,上面的例子应该是:
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
This is much cleaner. Ah, the magic of Rails - it's smarter than me.
这是干净了很多。啊,Rails的魔力——它比我聪明。
#7
5
For boolean fields in Rails 3.2.6 at least, this will work in your migration.
至少对于Rails 3.2.6中的布尔字段,这将在迁移中起作用。
def change
add_column :users, :eula_accepted, :boolean, default: false
end
Putting a 1
or 0
for a default will not work here, since it is a boolean field. It must be a true
or false
value.
输入1或0作为默认值在这里不起作用,因为它是一个布尔字段。它必须是一个真值或假值。
#8
1
If you are just setting defaults for certain attributes of a database backed model I'd consider using sql default column values - can you clarify what types of defaults you are using?
如果您只是为数据库支持的模型的某些属性设置默认值,我将考虑使用sql默认列值——您能澄清您使用的默认类型吗?
There are a number of approaches to handle it, this plugin looks like an interesting option.
有很多方法可以处理它,这个插件看起来很有趣。
#9
1
The suggestion to override new/initialize is probably incomplete. Rails will (frequently) call allocate for ActiveRecord objects, and calls to allocate won't result in calls to initialize.
重写new/initialize的建议可能是不完整的。Rails将(经常)调用ActiveRecord对象的分配,而调用分配不会导致调用初始化。
If you're talking about ActiveRecord objects, take a look at overriding after_initialize.
如果您正在讨论ActiveRecord对象,请查看覆盖after_initialize。
These blog posts (not mine) are useful:
这些博客文章(不是我的)很有用:
Default values Default constructors not called
默认值默认构造函数未调用
[Edit: SFEley points out that Rails actually does look at the default in the database when it instantiates a new object in memory - I hadn't realized that.]
[编辑:SFEley指出当Rails在内存中实例化一个新对象时,它确实会查看数据库中的默认值——我还没有意识到这一点。]
#10
1
I needed to set a default just as if it was specified as default column value in DB. So it behaves like this
我需要设置一个默认值,就像它在DB中被指定为默认列值一样。它是这样的
a = Item.new
a.published_at # => my default value
a = Item.new(:published_at => nil)
a.published_at # => nil
Because after_initialize callback is called after setting attributes from arguments, there was no way to know if the attribute is nil because it was never set or because it was intentionally set as nil. So I had to poke inside a bit and came with this simple solution.
因为after_initialize回调是在设置来自参数的属性之后调用的,所以无法知道属性是否为nil,因为它从未被设置,或者因为它被故意设置为nil。所以我需要深入研究,得出这个简单的解。
class Item < ActiveRecord::Base
def self.column_defaults
super.merge('published_at' => Time.now)
end
end
Works great for me. (Rails 3.2.x)
工作非常适合我。(Rails 3.2.x)
#11
1
A potentially even better/cleaner potential way than the answers proposed is to overwrite the accessor, like this:
一个可能比建议的答案更好/更干净的方法是覆盖访问器,如下所示:
def status
self['name_of_var'] || 'desired_default_value'
end
See "Overwriting default accessors" in the ActiveRecord::Base documentation and more from * on using self.
请参阅ActiveRecord::Base文档中的“覆盖默认访问器”,更多来自使用self的*。
#12
0
i answered a similar question here.. a clean way to do this is using Rails attr_accessor_with_default
我在这里回答了一个类似的问题。一种干净的方法是使用Rails attr_accessor_with_default
class SOF
attr_accessor_with_default :is_awesome,true
end
sof = SOF.new
sof.is_awesome
=> true
UPDATE
更新
attr_accessor_with_default has been deprecated in Rails 3.2.. you could do this instead with pure Ruby
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
#13
0
If you're talking about ActiveRecord objects, I use the 'attribute-defaults' gem.
如果您正在谈论ActiveRecord对象,我将使用“attribute-defaults”gem。
Documentation & download: https://github.com/bsm/attribute-defaults
文档和下载:https://github.com/bsm/attribute-defaults
#14
0
You could use the rails_default_value gem. eg:
您可以使用rails_default_value gem。例如:
class Foo < ActiveRecord::Base
# ...
default :bar => 'some default value'
# ...
end
https://github.com/keithrowell/rails_default_value
https://github.com/keithrowell/rails_default_value
#15
-3
You can override the constructor for the ActiveRecord model.
您可以重写ActiveRecord模型的构造函数。
Like this:
是这样的:
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