Ser*_*gey 3 postgresql activerecord ruby-on-rails
鉴于这个简单的模型:
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
Run Code Online (Sandbox Code Playgroud)
而事实是:
这怎么可能?
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
[]
Run Code Online (Sandbox Code Playgroud)
调试器
将调试器放在"some_filter"方法中时,就会发生这种情况.
[12] pry(#<Article>)> self.noframe = nil
=> nil
[13] pry(#<Article>)> self.valid?
Article Exists (0.5ms) SELECT 1 AS one FROM "articles" WHERE "articles"."url" = 'http://www.example.com' LIMIT 1
=> true
[14] pry(#<Article>)> self.noframe = false
=> false
[15] pry(#<Article>)> self.valid?
Article Exists (0.5ms) SELECT 1 AS one FROM "articles" WHERE "articles"."url" = 'http://www.example.com' LIMIT 1
=> true
[16] pry(#<Article>)> self.save
Article Exists (0.5ms) SELECT 1 AS one FROM "articles" WHERE "articles"."url" = 'http://www.example.com' LIMIT 1
=> false
Run Code Online (Sandbox Code Playgroud)
更多信息
为了使它更有趣,当我更改"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
Run Code Online (Sandbox Code Playgroud)
一切都会奏效.