使用first_or_create时,在after_create回调中,Model查询会自动添加额外条件

Anj*_*jan 3 activerecord ruby-on-rails ruby-on-rails-3 ruby-on-rails-3.2

出于解释目的,我将使用SQLite创建一个全新的Rails(3.2.13)项目.

rails new TestApp
cd TestApp/
rake db:create
rails g model Blog name:string description:string
rake db:migrate
Run Code Online (Sandbox Code Playgroud)

这是Blog模型的内容.

class Blog < ActiveRecord::Base
  attr_accessible :description, :name

  after_create :print_other_name


  private

  def print_other_name
    # Just for example, running a query here.
    blog = Blog.first
  end
end
Run Code Online (Sandbox Code Playgroud)

然后打开一个rails console.

1.9.3-p125 :001 > blog = Blog.where( name: 'First Blog' ).first_or_create!( description: 'This is the first blog' )

  Blog Load (0.2ms)  SELECT "blogs".* FROM "blogs" WHERE "blogs"."name" = 'First Blog' LIMIT 1
   (0.1ms)  begin transaction
  SQL (63.9ms)  INSERT INTO "blogs" ("created_at", "description", "name", "updated_at") VALUES (?, ?, ?, ?)  [["created_at", Thu, 09 May 2013 11:30:31 UTC +00:00], ["description", "This is the first blog"], ["name", "First Blog"], ["updated_at", Thu, 09 May 2013 11:30:31 UTC +00:00]]
  ======>>>>>>> Blog Load (0.6ms)  SELECT "blogs".* FROM "blogs" WHERE "blogs"."name" = 'First Blog' LIMIT 1
   (1.5ms)  commit transaction
 => #<Blog id: 1, name: "First Blog", description: "This is the first blog", created_at: "2013-05-09 11:30:31", updated_at: "2013-05-09 11:30:31">
Run Code Online (Sandbox Code Playgroud)

在上面的代码块中,请查看查询后运行的INSERT查询:

Blog Load (0.6ms)  SELECT "blogs".* FROM "blogs" WHERE "blogs"."name" = 'First Blog' LIMIT 1
Run Code Online (Sandbox Code Playgroud)

这是由Blog.first模型中的行生成的查询after_create.

什么应该是一个LIMIT 1没有任何条件的简单查询,现在有一个name条件添加到查询.经过大量测试后,我意识到添加的条件是Blog.where( name: 'First Blog' ).first_or_create!....行中提到的条件.

换句话说,我在where之前使用的任何条件first_or_create似乎都会自动添加到after_create回调中运行的所有查询中.

我无法想象为什么这会是预期的行为,但如果是,我无法在任何地方找到它.

有没有人对这种行为有任何见解?这打破了我在after_create回调中的所有查询.

jok*_*lan 5

first_or_create包装由where子句所定义的范围你的整个查询.您可以通过两种方式解决此问题:

  1. 而不是first_or_create使用find_or_create_by

    Blog.find_or_create_by( name: 'First Blog' )
    
    Run Code Online (Sandbox Code Playgroud)
  2. 用于unscoped包含以下查询的所有回调:

    def print_other_name
      # Just for example, running a query here.
      blog = Blog.unscoped.first
    end
    
    Run Code Online (Sandbox Code Playgroud)