2011-12-28 65 views
7

藝術家有許多活動(基本上是用戶之間的交互的高速緩存):有沒有辦法來加載多態關聯的關聯?

class Activity < ActiveRecord::Base 

    belongs_to :receiver, :class_name => 'Artist', :foreign_key => :receiver_id #owns the stuff done "TO" him 
    belongs_to :link, :polymorphic => true 
    belongs_to :creator, :class_name => 'Artist', :foreign_key => :creator_id #person who initiated the activity 

end 

例如:

Activity.create(:receiver_id => author_id, :creator_id => artist_id, :link_id => id, :link_type => 'ArtRating') 

我想爲每位藝術家創建活動流頁面,包括列表不同類型的事件,ArtRatings(喜歡,不喜歡的),收藏,繼等

控制器看起來是這樣的:

class ActivityStreamController < ApplicationController 
    def index 
    @activities = @artist.activities.includes([:link,:creator,:receiver]).order("id DESC").limit(30) 
    end 
end 

的DB調用正確急切地加載多態鏈接對象:

SELECT "activities".* FROM "activities" WHERE (("activities"."receiver_id" = 6 OR "activities"."creator_id" = 6)) ORDER BY id DESC LIMIT 30 
    ArtRating Load (0.5ms) SELECT "art_ratings".* FROM "art_ratings" WHERE "art_ratings"."id" IN (137, 136, 133, 130, 126, 125, 114, 104, 103, 95, 85, 80, 73, 64) 
    SELECT "follows".* FROM "follows" WHERE "follows"."id" IN (14, 10) 
    SELECT "favorites".* FROM "favorites" WHERE "favorites"."id" IN (25, 16, 14) 

但是,當我顯示每個ArtRating,我還需要引用的文章標題,這屬於一個職位。在我看來,如果我這樣做:

activity.link.post 

它爲每個art_rating的文章做一個單獨的數據庫調用。有沒有辦法熱切地加載帖子?

更新的問題:

如果沒有辦法使用,實現職位的渴望加載「包括」語法,有沒有辦法做手工的預先加載查詢自己和它注入@活動對象?

我在數據庫日誌中看到:

SELECT "art_ratings".* FROM "art_ratings" WHERE "art_ratings"."id" IN (137, 136, 133, 130, 126, 125, 114, 104, 103, 95, 85, 80, 73, 64) 

有沒有一種方法,我可以訪問來自@activities IDS的這個列表對象?如果是這樣,我可以做2個附加查詢,其中1個獲取該列表中的art_ratings.post_id(s),另一個查詢將這些post_ids中的所有帖子選中。然後以某種方式將'post'結果注入到@activities中,以便在遍歷集合時作爲activity.link.post提供。可能?

+0

這裏有什麼進展? – bogardon 2013-10-06 22:24:19

+0

從我所瞭解的情況來看,你有一套應該作爲鏈接附加到活動上的模型,我收集他們都至少共享一個關聯,這是'post'。這些鏈接模型中是否有足夠的共同點來使用[單表繼承](http://apidock.com/rails/ActiveRecord/Base)並將所有表放在一個表中? – CMW 2013-10-25 13:03:36

回答

0

試試這個:

@artist.activities.includes([{:link => :post},:creator,:receiver])... 

See the Rails docs for more.

+0

通常會起作用,但文檔沒有說明通過多態關聯進行的熱切加載。獲取錯誤:「找不到名爲'post'的關聯;也許你拼錯了它?」 – Homan 2011-12-28 16:56:02

+0

我測試了這個,它工作正常(3.2.14)。你確定你所有的「鏈接」關聯都有關聯嗎? – aromero 2013-11-01 01:33:25

0

如果我得到這個權利,你不僅要加載一個多態性協會的協會,但態關聯的多態性協會

這基本上意味着通過一堆來自數據庫中某些字段的未定義表格,將一個定義的表格與另一個定義的表格結合起來。

由於活動和帖子都通過多態對象連接,因此它們都有something_idsomething_type列。

現在我認爲活動記錄不會讓你這樣做的開箱但基本上你想要的東西,如:

class Activity 
    has_one :post, :primary_key => [:link_id, :link_type], :foreign_key => [:postable_id, :postable_type] 
end 

(假設你在Post態關聯是belongs_to :postable

那倒然後給你一個查詢排序PostActivity之間的直接關聯,這是一個非常奇怪的承擔habtm與'多態連接表'(我剛剛提出了這個詞)。因爲它們共享相同的多態關聯對象,所以它們可以連接。有點像朋友的朋友的東西。如我所知,AR不會讓你開箱即用(雖然它會很棒),但是有一些寶石會給你複合的外來和/或主鍵。也許你可以找到一個幫助你以這種方式解決你的問題。

0

鑑於你更新的問題(這是不可行的使用AR的include),你可以試着用以下一個額外的查詢來獲取相關文章:

  • 取活動,那麼建立一個數組鏈接post_ids

    @activities = @artist.activities.includes([:link,:creator,:receiver]) 
    activity_post_ids = @activities.map{|a| a.link.post_id}.compact 
    
  • 然後加載的帖子在一個查詢並將其存儲在一個Hash,通過索引他們的id

    activity_posts = Hash[Post.where(id: activity_post_ids).map{|p| [p.id, p]}] 
    #=> {1 => #<Post id:1>, 3 => #<Post id:3>, ... } 
    
  • 終於環比@activities,設置各個相關環節

    post屬性
    @activities.each{|a| a.link.post = activity_posts[a.link.post_id]} 
    
4

TL; DR我的解決方案讓你想

artist.created_activities.includes(:link)渴望負荷這裏的一切是我的第一嘗試吧:https://github.com/JohnAmican/music

的幾個注意事項:

  • 我靠default_scope ,所以這不是最佳的。
  • 看起來你正在使用STI。我的解決方案沒有。這意味着你不能簡單地致電activities對藝術家;你必須參考created_activitiesreceived_activities。可能有解決方法。如果我找到任何東西,我會更新。
  • 我改變了一些名字,因爲它讓我感到困惑。

如果你進入控制檯,並做created_activities.includes(:link),相應的東西變得急切加載:

irb(main):018:0> artist.created_activities.includes(:link) 
    Activity Load (0.2ms) SELECT "activities".* FROM "activities" WHERE "activities"."creator_id" = ? [["creator_id", 1]] 
    Rating Load (0.3ms) SELECT "ratings".* FROM "ratings" WHERE "ratings"."id" IN (1) 
    RatingExplanation Load (0.3ms) SELECT "rating_explanations".* FROM "rating_explanations" WHERE "rating_explanations"."rating_id" IN (1) 
    Following Load (0.3ms) SELECT "followings".* FROM "followings" WHERE "followings"."id" IN (1) 
    Favorite Load (0.2ms) SELECT "favorites".* FROM "favorites" WHERE "favorites"."id" IN (1) 
=> #<ActiveRecord::Relation [#<Activity id: 1, receiver_id: 2, creator_id: 1, link_id: 1, link_type: "Rating", created_at: "2013-10-31 02:36:27", updated_at: "2013-10-31 02:36:27">, #<Activity id: 2, receiver_id: 2, creator_id: 1, link_id: 1, link_type: "Following", created_at: "2013-10-31 02:36:41", updated_at: "2013-10-31 02:36:41">, #<Activity id: 3, receiver_id: 2, creator_id: 1, link_id: 1, link_type: "Favorite", created_at: "2013-10-31 02:37:04", updated_at: "2013-10-31 02:37:04">]> 

最起碼,這證明了Rails有這樣做的能力。避開default_scope似乎是一個告訴Rails你想要做什麼而不是技術限制的問題。

UPDATE:

原來,當你通過一個範圍塊的關聯,並調用該協會,self該塊內指的是關係。所以,你可以反思它,並採取適當的行動:

class Activity < ActiveRecord::Base 
    belongs_to :creator, class_name: 'Artist', foreign_key: :creator_id, inverse_of: :created_activities 
    belongs_to :receiver, class_name: 'Artist', foreign_key: :receiver_id, inverse_of: :received_activities 
    belongs_to :link, -> { self.reflections[:activity].active_record == Rating ? includes(:rating_explanation) : scoped }, polymorphic: true 
end 

我更新了我的代碼,以反映(哈哈)這一點。

這可以清理乾淨。例如,也許您在訪問活動鏈接時並不總是希望加載rating_explanations。有很多方法可以解決這個問題。如果你願意,我可以發佈一個。

但是,我認爲這顯示的最重要的是在關聯的範圍塊內,您可以訪問正在構建的ActiveRecord::Relation。這將允許你有條件地做事情。

0

這個簡單ActiveRecord應該工作:

@artist.activities.includes([:link => :post,:creator,:receiver]).order("id DESC").limit(30) 

如果不是這樣,如果你得到一個錯誤,如"Association named 'post' was not found; perhaps you misspelled it?",那麼你有一個模型的問題,因爲你的link關聯的至少一個不具備post協會。

多態關聯旨在與通用接口一起使用。如果你要求post用於任何多態關聯,那麼你所有的關聯都應該實現該關聯(post之一)。

檢查您在多態關聯中使用的所有模型是否實現了關聯。