validates_presence_of 导致 after_initialize 被一个奇怪的自我调用 [英] validates_presence_of causes after_initialize to be called with a weird self

查看:44
本文介绍了validates_presence_of 导致 after_initialize 被一个奇怪的自我调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个运行良好的模型:

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆