如何在Rails的activerecord模型中为属性创build默认值?
我想通过在ActiveRecord中定义它来为属性创build一个默认值。 默认情况下,每次创buildlogging,我想有一个属性:status
的默认值。 我试图做到这一点:
class Task < ActiveRecord::Base def status=(status) status = 'P' write_attribute(:status, status) end end
但创build后,我仍然从数据库中检索到这个错误:
ActiveRecord::StatementInvalid: Mysql::Error: Column 'status' cannot be null
所以我认为这个价值不适用于这个属性。
在Rails中这样做的优雅方式是什么?
非常感谢。
您可以在迁移中为列设置默认选项
.... add_column :status, :string, :default => "P" ....
要么
您可以使用before_save
callback
class Task < ActiveRecord::Base before_save :default_values def default_values self.status ||= 'P' # note self.status = 'P' if self.status.nil? might be safer (per @frontendbeauty) end end
因为刚才我遇到了这个问题,而Rails 3.0的选项有些不同,所以我会给出这个问题的另一个答案。
在Rails 3.0中,你想要做这样的事情:
class MyModel < ActiveRecord::Base after_initialize :default_values private def default_values self.name ||= "default value" end end
您可以在不编写任何代码的情况下执行此操作:)您只需要为数据库中的列设置默认值即可。 你可以在你的迁移中做到这一点。 例如:
create_table :projects do |t| t.string :status, :null => false, :default => 'P' ... t.timestamps end
希望有所帮助。
当我需要默认值通常为新的行动的视图呈现之前的新logging。 以下方法将仅为新logging设置默认值,以便在渲染表单时可用。 before_save
和before_create
太晚了 , 如果你想在input字段中显示默认值 ,将不会工作。
after_initialize do if self.new_record? # values will be available for new record forms. self.status = 'P' self.featured = true end end
解决scheme取决于几件事情。
缺省值是否依赖于创build时可用的其他信息? 你可以用最小的后果来擦除数据库吗?
如果你回答了第一个问题,那么你想用Jim的解决scheme
如果你回答第二个问题,那么你想用Daniel的解决scheme
如果您对这两个问题都回答“否”,那么您最好添加并运行新的迁移。
class AddDefaultMigration < ActiveRecord::Migration def self.up change_column :tasks, :status, :string, :default => default_value, :null => false end end
:string可以用ActiveRecord :: Migration识别的任何typesreplace。
CPU很便宜,所以Jim解决scheme中Task的重新定义不会引起很多问题。 特别是在生产环境中。 这种迁移是正确的方式,因为它被加载并调用的次数要less得多。
我会考虑使用在这里find的attr_defaults。 你最疯狂的梦想将实现。
只是加强吉姆的答案
使用存在可以做
class Task < ActiveRecord::Base before_save :default_values def default_values self.status = status.presence || 'P' end end
正如我所看到的,当需要默认值时,有两个问题需要解决。
- 新对象初始化时需要存在的值。 使用after_initialize是不合适的,因为如前所述,在调用#find的过程中会被调用,这将导致性能下降。
- 保存时需要保留默认值
这是我的解决scheme:
# the reader providers a default if nil # but this wont work when saved def status read_attribute(:status) || "P" end # so, define a before_validation callback before_validation :set_defaults protected def set_defaults # if a non-default status has been assigned, it will remain # if no value has been assigned, the reader will return the default and assign it # this keeps the default logic DRY status = status end
我很想知道为什么人们会想到这种方法。
对于Rails支持的列types – 就像这个问题中的string一样 – 最好的方法是像Daniel Kristensen指出的那样在数据库本身设置列缺省值。 Rails会在DB上反思并相应地初始化对象。 此外,这使得您的数据库不会有人在您的Rails应用程序之外添加一行,并且忘记初始化该列。
对于列types,Rails不支持开箱即用 – 例如ENUM列 – Rails将无法反省列默认值。 对于这些情况,你不想使用after_initialize(每次从数据库加载对象,每次使用.new创build对象时),before_create(因为它发生在validation之后)或before_save(因为它也会在更新时发生,这通常不是你想要的)。
相反,你需要在:create中设置before_validation属性,如下所示:
before_validation :set_status_because_rails_cannot, on: :create def set_status_because_rails_cannot self.status ||= 'P' end
我现在find了一个更好的方法来做到这一点:
def status=(value) self[:status] = 'P' end
在Ruby中,方法调用允许没有括号,所以我应该把局部variables命名为其他的东西,否则Ruby会认为它是一个方法调用。
在你的例子中,你将“P”分配给一个局部variables“状态”。 尝试self.status = 'P'
这个国王的事情。 尽pipe如其他答案中提到的那样使用after_initialize
会更好。
- 如何处理Ruby on Rails错误:“请安装postgresql适配器:`gem install activerecord-postgresql-adapter'”
- 在Rails 3中设置会话超时
- Rails 3 / Form没有模型:我如何创build一个不绑定到模型的表单?
- Rails 3的devise,current_user不能在模型中访问?
- 在Rails的has_many关系中默认使用一个范围
- rails生成模型字段:type,字段有什么选项:type?
- Rails如何跟踪为数据库运行哪些迁移?
- 你能在Rails 3search的date做比较比较吗?
- has_and_belongs_to_many,避免连接表中的愚蠢