我想通过在ActiveRecord中定义属性来创建一个默认值。默认情况下,每次创建记录时,我都希望attribute:status有一个默认值。我试着这样做:

class Task < ActiveRecord::Base
  def status=(status)
    status = 'P'
    write_attribute(:status, status)
  end
end

但是在创建时,我仍然从数据库中检索这个错误:

ActiveRecord::StatementInvalid: Mysql::Error: Column 'status' cannot be null

因此,我假定该值没有应用于属性。

在Rails中做这件事的优雅方式是什么?

多谢。


当前回答

在我看来,在需要默认值时需要解决两个问题。

在初始化新对象时,需要该值。使用after_initialize并不合适,因为如前所述,它将在调用#find期间被调用,这将导致性能下降。 保存时需要保留默认值

以下是我的解决方案:

# 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

我很想知道为什么人们会想到这种方法。

其他回答

现在我找到了一个更好的方法:

def status=(value) 
  self[:status] = 'P' 
end 

在Ruby中,方法调用不允许有括号,因此我应该将局部变量命名为其他变量,否则Ruby会将其识别为方法调用。

你不需要写任何代码就可以做到这一点:)你只需要为数据库中的列设置默认值。您可以在迁移中这样做。例如:

create_table :projects do |t|
  t.string :status, :null => false, :default => 'P'
  ...
  t.timestamps
end

在我看来,在需要默认值时需要解决两个问题。

在初始化新对象时,需要该值。使用after_initialize并不合适,因为如前所述,它将在调用#find期间被调用,这将导致性能下降。 保存时需要保留默认值

以下是我的解决方案:

# 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 3.0的选项有点不同,所以我将提供这个问题的另一个答案。

在Rails 3.0中,你想做的事情是这样的:

class MyModel < ActiveRecord::Base
  after_initialize :default_values

  private
    def default_values
      self.name ||= "default value"
    end
end

我会考虑使用这里找到的attr_defaults。你最疯狂的梦想将会成真。