我正在尝试找到在Rails中为对象设置默认值的最佳方法。
我能想到的最好的new
方法是在控制器的方法中设置默认值。
如果这是可以接受的,或者有更好的方法可以进行,那么有人可以提供任何输入吗?
我正在尝试找到在Rails中为对象设置默认值的最佳方法。
我能想到的最好的new
方法是在控制器的方法中设置默认值。
如果这是可以接受的,或者有更好的方法可以进行,那么有人可以提供任何输入吗?
Answers:
“正确”在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()
时,Person.new()
除非您用其他方法覆盖它,否则它将始终将“ Doe”属性细化到最大。
.new
class方法调用的新模型对象上工作。该博客文章有关ActiveRecord直接调用的讨论.allocate
是关于从数据库中加载现有数据的模型对象。(而这是ActiveRecord的工作一个可怕的想法是这样,IMO但是,跑题的。)
change_column_default :people, :last_name, nil
stackoverflow.com/a/1746246/483520
change_column :people, :last_name, :string, default: "Doe"
根据SFEley的答案,以下是针对较新的Rails版本的更新/修复的代码:
class SetDefault < ActiveRecord::Migration
def change
change_column :table_name, :column_name, :type, default: "Your value"
end
end
change_column
可能是相当“结构化”的,因此无法推论出反向操作。如果不确定,只需运行即可,db:migrate
然后立即进行测试db:rollback
。接受的答案是相同的结果,但至少是假设的!
up
down
首先,您不能过载,initialize(*args)
因为在所有情况下都不会调用它。
最好的选择是将默认值放入迁移中:
add_column :accounts, :max_users, :integer, :default => 10
其次,最好将默认值放入模型中,但这仅适用于最初为零的属性。您可能会像我在处理boolean
列时那样遇到麻烦:
def after_initialize
if new_record?
max_users ||= 10
end
end
您需要new_record?
这样,因此默认值不会覆盖从datbase加载的值。
您需要||=
阻止Rails覆盖传递给initialize方法的参数。
after_initiation :your_method_name
.... 2使用self.max_users ||= 10
after_initialize do
代替def after_initialize
您还可以尝试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
如果您引用的是ActiveRecord对象,则有两种(或多种)方法可以做到这一点:
例如
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
例如 before_validation_on_create
此处提供更多信息:http : //api.rubyonrails.org/classes/ActiveRecord/Callbacks.html#M002147
在Ruby on Rails v3.2.8中,使用after_initialize
ActiveRecord回调,您可以在模型中调用一个方法,该方法将为新对象分配默认值。
查找器发现并实例化的每个对象都会触发after_initialize回调,在实例化新对象之后也会触发after_initialize(请参见ActiveRecord回调)。
因此,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
for bar
属性呈现表单。
充其量这是hacky ...
不用检查属性值,而是将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
这更干净。啊,Rails的魔力-比我聪明。
after_initialize
在这里建议回调?轨道上的回调文档有例如设置默认值在before_create
没有额外条件检查
Subscription
after_initialize
,而不是before_create
回调,是因为我们要在用户创建新对象时为用户(在视图中使用)设置默认值。在为用户提供了一个新对象,提供了他们的输入并将该对象提交给控制器之后,将before_create
调用该回调。然后,控制器检查是否有任何回调。似乎违反直觉,但这是一个命名法,指的是动作。实例化一个新对象不是该对象。before_create
before_create
create
create
如果您要使用模型,则可以在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
如果您只是为数据库支持的模型的某些属性设置默认值,我会考虑使用sql默认列值-您能否阐明正在使用的默认类型?
有很多方法可以处理它,这个插件看起来像是一个有趣的选项。
我需要设置一个默认值,就像在数据库中将其指定为默认列值一样。所以它表现得像这样
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
有关使用self的信息,请参见ActiveRecord :: Base文档中的 “覆盖默认访问器”,以及StackOverflow的更多内容。
我在这里回答了类似的问题..做到这一点的一种干净方法是使用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
attr_accessor_with_default
已废弃铁轨> 3.1.0
如果您正在谈论ActiveRecord对象,那么我将使用'attribute-defaults'gem。
您可以使用rails_default_value gem。例如:
class Foo < ActiveRecord::Base
# ...
default :bar => 'some default value'
# ...
end
您可以覆盖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