2011-10-30 111 views
0

我有以下型號:Rails has_many關係中的多個INNER JOIN?

class Image < ActiveRecord::Base 
    belongs_to :gallery 
    has_many :bookmarks 
    has_many :gallery_tags, :foreign_key => :gallery_id 
end 

class Bookmark < ActiveRecord::Base 
    belongs_to :user 
    belongs_to :image 
    has_many :gallery_tags, :through => :image, :source => :gallery_tags 
end 


class GalleryTag < ActiveRecord::Base 
    belongs_to :gallery 
    belongs_to :tag 
end 

class Gallery < ActiveRecord::Base 
    belongs_to :provider 
    has_many :images 
    belongs_to :user 
    has_many :gallery_tags 
    has_many :tags, :through => :gallery_tags 
end 

class Tag < ActiveRecord::Base 
end 

class User < ActiveRecord::Base 
    has_many :bookmarks 
    has_many :galleries 
end 

我希望能夠做到

User.find(1).bookmarked_tags 

和檢索通過與圖像相關的畫廊與用戶的所有書籤的圖像相關聯的所有標籤。標籤與畫廊相關聯。

查詢最終會看起來像這樣:

SELECT 
    tags.* 
FROM 
    tags 
    INNER JOIN gallery_tags ON gallery_tags.tag_id = tags.id 
    INNER JOIN images ON gallery_tags.gallery_id = images.gallery_id 
    INNER JOIN bookmarks ON images.id = bookmarks.image_id 
WHERE 
    bookmarks.user_id = 1 
GROUP BY 
    tags.id; 

我已經嘗試添加

has_many :tags, :through => :gallery_tags, :foreign_key => :gallery_id 

到圖像時,這會導致Image.find(34).tags添加導致

SELECT `tags`.* FROM `tags` INNER JOIN `gallery_tags` ON `tags`.id = `gallery_tags`.tag_id WHERE ((`gallery_tags`.gallery_id = 34)) 

(它不是在查詢中使用圖片的gallery_id),然後我嘗試添加

has_many :bookmarked_tags, :through => :bookmarked_images, :source => :tags 

到用戶,這將導致User.find(1).bookmarked_tags導致

ActiveRecord::HasManyThroughSourceAssociationMacroError: Invalid source reflection macro :has_many :through for has_many :bookmarked_tags, :through => :bookmarked_images. Use :source to specify the source reflection.

所以:我怎樣才能得到Rails的運行我貼有User.find查詢(1)。 bookmarked_tags?

回答

3

有兩種解決方案

首個解決方案

創建數據庫內部的觀點就像一個連接表:

CREATE VIEW user_bookmarks_tags (
    SELECT 
    bookmarks.user_id AS user_id 
    gallery_tags.tag_id AS tag_id 
    FROM 
    gallery_tags 
    INNER JOIN images ON gallery_tags.gallery_id = images.gallery_id 
    INNER JOIN bookmarks ON images.id = bookmarks.image_id 
    GROUP BY 
    user_id, tag_id 
) 

(你可以做到這一點遷移內) 這個視圖就像一個「表」,其列user_id | tag_id

現在我們可以使用has_and_belongs_to_many適應我們的模型:

user.rb

class User < ActiveRecord::Base 
    has_many :bookmarks 
    has_many :galleries 

    # we use our magic (view) join table here! 
    has_and_belongs_to_many :tags, :join_table => :user_bookmarks_tags 
end 

tag.rb

class Tag < ActiveRecord::Base 
    # we use our magic (view) join table here 
    has_and_belongs_to_many :users, :join_table => :user_bookmarks_tags 
end 

現在你可以這樣做:User.find(1).tagsTag.find(1).users :)


第二種解決

不要手動加入無一個觀點:

定義缺失的關係(需要自動加入foreign_key查找):

標籤。RB

class Tag < ActiveRecord::Base 
    has_many :gallery_tags 
end 

gallery_tag.rb

class GalleryTag < ActiveRecord::Base 
    belongs_to :gallery 
    belongs_to :tag 
    # new: 
    has_many :images, :through => :gallery 
end 

現在,我們可以添加一個bookmarked_tags方法我們user.rb

class User < ActiveRecordBase 
    has_many :bookmarks 
    has_many :galleries 

    def bookmarked_tags 
    Tag.joins(:gallery_tags => {:images => :bookmarks}).where('bookmarks.user_id = ?', self.id).group('tags.id') 
    end 
end 
+0

哇,有意思。我認爲一種方法是必要的,但我從未使用過Arel的.joins。謝謝,這有效(我與第二個解決方案一起)。 –