validates_presence_of 导致 after_initialize 被一个奇怪的自我调用 [英] validates_presence_of causes after_initialize to be called with a weird self
问题描述
我有一个运行良好的模型:
I've had this model which was working fine:
class Weight < ActiveRecord::Base
belongs_to :user
validates_presence_of :weight, :measured_on
attr_accessible :weight, :measured_on
def after_initialize
self.measured_on ||= Date.today
end
end
我添加了这一行
validates_uniqueness_of :measured_on, :scope => :user_id
它开始在验证时抛出错误.不是验证错误,而是 Ruby 错误:
and it started throwing an error on validation. Not a validation error but a Ruby error:
>> w.valid?
ActiveRecord::MissingAttributeError: missing attribute: measured_on
from /Users/pupeno/Projects/sano/app/models/weight.rb:8:in `after_initialize'
我在 after_initialize 中放置了一个调试器语句,我注意到了一些意想不到的事情.当我创建一个新的权重时,它会按预期工作,并且 after_initialize 上的 self 对象是预期的权重:
I've put a debugger statement in after_initialize and I've noticed something unexpected. When I create a new weight it works as expected and the self object on after_initialize is the expected weight:
>> w = Weight.new
/Users/pupeno/Projects/sano/app/models/weight.rb:9
self.measured_on ||= Date.today
(rdb:1) p self
#<Weight id: nil, user_id: nil, weight: nil, measured_on: nil, created_at: nil, updated_at: nil>
(rdb:1) c
=> #<Weight id: nil, user_id: nil, weight: nil, measured_on: "2009-11-22", created_at: nil, updated_at: nil>
当我运行 w.valid 时?它变得很奇怪.after_initialize 再次被调用,我不知道为什么,并且 self 对象不是我所期望的:
When I run w.valid? it gets weird. after_initialize is called again, I'm not sure why, and the self object is nothing I expected:
>> w.valid?
/Users/pupeno/Projects/sano/app/models/weight.rb:9
self.measured_on ||= Date.today
(rdb:1) p self
#<Weight id: 1>
(rdb:1) p self.inspect
"#<Weight id: 1>"
(rdb:1) p self.class
Weight(id: integer, user_id: integer, weight: float, measured_on: date, created_at: datetime, updated_at: datetime)
(rdb:1) p self.measured_on
ActiveRecord::MissingAttributeError Exception: missing attribute: measured_on
(rdb:1)
似乎创建了另一个 Weight 对象,除了 id 集之外没有任何属性.任何想法为什么?这是一个错误还是预期的行为?我在after_initialize上设置了measured_on是不是做错了什么?
It seems like another Weight object was created without any attributes but the id set. Any ideas why? Is this a bug or the expected behavior? Am I doing something wrong by setting the measured_on on after_initialize?
如果有人遇到同样的问题,我目前的解决方法是
My current workaround, in case anybody is having the same problem, is
class Weight < ActiveRecord::Base
belongs_to :user
validates_presence_of :weight, :measured_on
validates_uniqueness_of :measured_on, :scope => :user_id
attr_accessible :weight, :measured_on
def after_initialize
if self.has_attribute? :measured_on
self.measured_on ||= Date.today
end
end
end
但我想要一个合适的解决方案.
but I'd like to have a proper solution.
推荐答案
我认为您遇到了一个我最近与之抗争的 Rails 错误.请参阅此博客条目链接到相关灯塔虫.
I think you're hitting a rails bug I recently battled with. See This blog entry linking to the related lighthouse bug.
我的理解是,发生的事情是之前的一些 rails 代码执行从表名中选择 id"以查看条目是否存在或匹配.然后该对象缓存该表存在的唯一字段是id".然后运行您的代码,然后属性"值不正确,仅反映 id 字段.
My understanding is that what's happening is that some prior piece of rails code does a "select id from tablename" to see if an entry exists or matches. The object then caches that the only field that exists for the table is "id". Your code then runs, and the "attributes" value is then incorrect, reflecting only the id field.
据我所知,这只发生在这个特定的代码路径被命中时,并且通常不会让事情感到不安,除非在进行验证时.
From what I could find, this only happened when this particular code path was hit, and didn't generally upset things, except when doing validations.
我为解决这个问题所做的是将 after_initialise 代码包装在 begin/rescue ActiveRecord::MissingAttributeError 块中.然后我在应用程序和每个项目上方写了一个大注释,表明何时发布新版本的 rails,我们可以删除它.
What I did to get around it was wrap the after_initialise code in a begin/rescue ActiveRecord::MissingAttributeError block. Then I wrote a big note in the application and above each item indicating when a new version of rails is released, we could remove this.
是的,我相信还有更优雅的解决方案.
Yes, I'm sure there are more elegant solutions.
def after_initialize
begin
# ... updates here
# self.unique_reference = UUIDTools::UUID.random_create.to_s
rescue ActiveRecord::MissingAttributeError
end
end
或者你也可以这样做:
def after_initialize
if self.has_attribute? :measured_on
self.measured_on ||= Date.today
end
end
这篇关于validates_presence_of 导致 after_initialize 被一个奇怪的自我调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!