鉴于这个简单的模型:
class Article < ActiveRecord::Base validates :title, presence: true validates :url, presence: true, uniqueness: true validates :topic, presence: true before_create :some_filter private def some_filter self.noframe = false end end
而事实是:
没有观察员
"noframe"列上没有索引
这怎么可能?
attrs = { title: "a", url: "http://www.example.com", topic: "test" } Article.where(attrs).count => 0 Article.where(url:"http://www.example.com").count => 0 article = Article.new(attrs) article.save (0.2ms) BEGIN Article Exists (0.6ms) SELECT 1 AS one FROM "articles" WHERE "articles"."url" = 'http://www.example.com' LIMIT 1 (0.3ms) ROLLBACK article.errors.full_messages []
调试器
将调试器放在"some_filter"方法中时,就会发生这种情况.
[12] pry(#)> self.noframe = nil => nil [13] pry(# )> self.valid? Article Exists (0.5ms) SELECT 1 AS one FROM "articles" WHERE "articles"."url" = 'http://www.example.com' LIMIT 1 => true [14] pry(# )> self.noframe = false => false [15] pry(# )> self.valid? Article Exists (0.5ms) SELECT 1 AS one FROM "articles" WHERE "articles"."url" = 'http://www.example.com' LIMIT 1 => true [16] pry(# )> self.save Article Exists (0.5ms) SELECT 1 AS one FROM "articles" WHERE "articles"."url" = 'http://www.example.com' LIMIT 1 => false
更多信息
为了使它更有趣,当我更改"some_filter"以将noframe设置为nil或true时,我可以创建任意数量的记录,而不会出现"文章存在"错误.
当我直接设置noframe属性而不是在before_create过滤器内部时,它也有效.
为什么这个解决方法有效?
我可以通过用"after_create"替换"before_create"并使用update_attributes(noframe:false)更新noframe属性来"修复"它.但为什么这是一个解决方案呢?update_attributes还调用所有回调和验证,为什么这在before_create中不起作用?
before_create
有一个不明显的属性 - 如果它返回false值它终止整个保存链.同样适用于所有before-*
回调.您可以在回调文档中找到它.您的方法返回false,因此整个事务回滚.将其更改为:
def some_filter self.noframe = false true end
一切都会奏效.