4

假設我有以下型號:匹配嵌套模型關聯屬性包括

class Post < ActiveRecord::Base 
    has_many :authors 

class Author < ActiveRecord::Base 
    belongs_to :post 

而且假設Author模型有一個屬性,name

我想通過作者的名字搜索給定作者「alice」的所有帖子。假設有另一位作者「bob」與alice合寫了一篇文章。

如果我使用includeswhere搜索的第一個結果:

post = Post.includes(:authors).where("authors.name" => "alice").first 

你會看到該職位只有一個作家,現在,即使其實還有更多:

post.authors #=> [#<Author id: 1, name: "alice", ...>] 
post.reload 
post.authors #=> [#<Author id: 1, name: "alice", ...>, #<Author id: 2, name: "bob", ...>] 

問題似乎是includeswhere的組合,它將範圍正確地限制爲所需的帖子,但同時隱藏除匹配的範圍之外的所有關聯。

我想最終得到鏈接的ActiveRecord::Relation,所以上面的重載解決方案並不令人滿意。通過joins更換includes解決了這個,但不急於負荷的關聯:

Post.joins(:authors).where("authors.name" => "alice").first.authors 
#=> [#<Author id: 1, name: "alice", ...>, #<Author id: 2, name: "bob", ...>] 
Post.joins(:authors).where("authors.name" => "alice").first.authors.loaded? 
#=> false 

有什麼建議?在此先感謝,我已經在這個問題上徘徊了一段時間。

+0

注意:我認識到,帖子/作者的關聯應該更實際地成爲HABTM,但它不會爲此目的而改變任何內容。 – 2012-02-09 03:46:53

回答

1

我看到你在做什麼,如預期的行爲,至少這就是SQL的工作原理......你將作者的聯接限制在authors.id = 1的位置,那麼爲什麼它會加載其他的呢? ActiveRecord只需要數據庫返回的行,它無法知道是否有其他人,而無需根據posts.id執行另一個查詢。

下面是使用子查詢一個可能的解決方案,這將作爲一個可鏈接的關係,在一個查詢執行:

relation = Post.find_by_id(id: Author.where(id:1).select(:post_id)) 

如果添加包括,你會看到查詢發生以下兩種方法之一:

relation = relation.includes(:authors) 

relation.first 
# 1. Post Load SELECT DISTINCT `posts`.`id`... 
# 2. SQL SELECT `posts`.`id` AS t0_r0, `posts`.`title` AS t0_r1, ... 

relation.all.first 
# 1. SQL SELECT `posts`.`id` AS t0_r0, `posts`.`title` AS t0_r1, ... 

所以根據情況,ActiveRecord的決定是否加載所有相關作者前了ID用一個簡單的查詢。有時,分兩步運行查詢會更有意義。

+0

非常感謝,這很有道理。在這種情況下,我仍然發現rails的行爲是不直觀的,而不是我認爲普通用戶期望的行爲,但也許這是不可避免的。 – 2012-07-22 12:47:55

-1

其實,這是因爲這段代碼:

post = Post.includes(:authors).where("authors.name" => "alice").first 

返回因爲「首先」第一個匹配的記錄。我認爲,如果你這樣做:

post = Post.includes(:authors).where("authors.name" => "alice") 

你還是會回到與「愛麗絲」和她的其他合作作者的所有帖子,如果我理解你的要求正確。

+0

不,這不是問題。查詢返回正確的帖子(在這個例子中,alice是作者之一)。但是,當我訪問該查詢的結果時,作者關於帖子的關聯(在本例中是第一個,但沒關係)只有我搜索的作者,即使可能有其他作者。 – 2012-07-19 06:19:49