记录存在时,它不存在

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)

而事实是:

  • 没有观察员
  • "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
[]
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中不起作用?

Bro*_*tse 5

before_create有一个不明显的属性 - 如果它返回false值它终止整个保存链.同样适用于所有before-*回调.您可以在回调文档中找到它.您的方法返回false,因此整个事务回滚.将其更改为:

def some_filter
  self.noframe = false
  true
end
Run Code Online (Sandbox Code Playgroud)

一切都会奏效.