我正在尝试寻找在Rails中设置对象默认值的最佳方式。
我能想到的最好办法是在控制器的new
方法中设置默认值。
请问是否有其他更好的做法或者对此方法是否可行有任何建议?
我正在尝试寻找在Rails中设置对象默认值的最佳方式。
我能想到的最好办法是在控制器的new
方法中设置默认值。
请问是否有其他更好的做法或者对此方法是否可行有任何建议?
在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
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
类方法调用的新模型对象上运行。那篇博客文章中关于ActiveRecord直接调用.allocate
的讨论是关于从数据库加载现有数据的模型对象。在我看来,这种方式对于ActiveRecord来说是一个可怕的想法。但这不是重点。 - SFEleychange_column_default :people, :last_name, nil
- Nolan Amychange_column :people, :last_name, :string, default: "Doe"
。 - GoBusto根据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
进行测试。
虽然接受的答案与结果相同,但至少可以假定! - gfdup
和down
操作。 - Fabrizio Bertoglio首先,你无法重载initialize(*args)
方法,因为它并不会在所有情况下被调用。
最好的选择是将默认值放入迁移中:
add_column :accounts, :max_users, :integer, :default => 10
其次,最好的办法是将默认值放入模型中,但这仅适用于最初为nil
的属性。您可能会遇到像我一样在处理boolean
列时出现问题:
def after_initialize
if new_record?
max_users ||= 10
end
end
你需要new_record?
以避免默认值覆盖从数据库加载的值。
你需要||=
来防止Rails覆盖传递给初始化方法的参数。
after_initialize
。你想使用 after_initiation :your_method_name
.... 2 使用 self.max_users ||= 10
- Jesse Wolgamottafter_initialize do
而不是 def after_initialize
。 - fotanuschange_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
class StoreListing < ActiveRecord::Base
attribute :country, :string, default: 'PT'
end
例如:
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
after_initialize
ActiveRecord回调函数,你可以调用模型中的一个方法来为新对象分配默认值。
因此,在我看来,它应该是这样的:对于每个由finder找到和实例化的对象,都会触发after_initialize回调,同时也会在实例化新对象后触发after_initialize(参见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
对于这个实例,如果实例之前在保存/更新时没有包含已验证的存在性属性,则 Foo.bar = default_value
。然后将使用 default_value
与您的视图一起使用,以使用 bar
属性的 default_value
渲染表单。
最好情况下,这是 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的魔力——它比我还聪明。
after_initialize
回调函数?Rails 的回调文档中有一个示例,在 before_create
中设置默认值而无需额外的条件检查。 - DfrSubscription
。 - Dfrafter_initialize
回调而非before_create
回调的原因是为了在用户创建新对象时为用户设置默认值(以在视图中使用)。before_create
回调在用户获得新对象、提供输入并将对象提交给控制器进行创建之后才会被调用。然后,控制器会检查任何before_create
回调函数。这似乎有些违反直觉,但这是一个命名问题——before_create
指的是create
操作。实例化一个新对象并不会create
该对象。 - erroricafter_initialize :assign_defaults_on_new_Foo, :if => Proc.new{|f| f.new_record? }
- ideaoforderdef change
add_column :users, :eula_accepted, :boolean, default: false
end
1
或0
是行不通的,因为它是一个布尔字段。它必须是true
或false
的值。change_column_default
,这非常简洁且可逆。class SetDefaultAgeInPeople < ActiveRecord::Migration[5.2]
def change
change_column_default :people, :age, { from: nil, to: 0 }
end
end
我需要设置一个默认值,就像在数据库中指定默认列值一样。这样它的行为就是这样的。
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)