我正在尝试找到在Rails中为对象设置默认值的最佳方法。
我能想到的最好的方法是在控制器的new
方法中设置默认值。
如果可以接受或者有更好的方法,是否有人有任何意见?
答案 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
答案 4 :(得分:7)
如果你指的是ActiveRecord对象,你有两种方法可以做到这一点:
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
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 ......
不使用检查属性值,而是使用带有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
将1
或0
置于默认值将无效,因为它是一个布尔字段。它必须是true
或false
值。
答案 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 documentation和more 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。
答案 14 :(得分:0)
您可以使用rails_default_value gem。例如:
class Foo < ActiveRecord::Base
# ...
default :bar => 'some default value'
# ...
end
答案 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