如何在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_savecallback

 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_savebefore_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 

正如我所看到的,当需要默认值时,有两个问题需要解决。

  1. 新对象初始化时需要存在的值。 使用after_initialize是不合适的,因为如前所述,在调用#find的过程中会被调用,这将导致性能下降。
  2. 保存时需要保留默认值

这是我的解决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会更好。