【问题标题】:Multiple INNER JOINs in a Rails has_many relationship?Rails has_many关系中的多个INNER JOIN?
【发布时间】:2011-10-30 19:45:20
【问题描述】:

我有以下型号:

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,这会导致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: 无效 源反射宏 :has_many :through for has_many :bookmarked_tags, :through => :bookmarked_images。使用 :source 到 指定源反射。

那么:如何让 Rails 运行我使用 User.find(1).bookmarked_tags 发布的查询?

【问题讨论】:

    标签: ruby-on-rails-3 activerecord


    【解决方案1】:

    有两种解决方案

    第一个解决方案

    在数据库中创建一个类似于连接表的视图:

    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 查找所需):

    tag.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
    

    【讨论】:

    • 哇,有趣。我认为需要一种方法,但我从未使用过 Arel 的 .joins。谢谢,这行得通(我选择了第二种解决方案)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-18
    • 1970-01-01
    • 2013-02-27
    • 1970-01-01
    • 2011-05-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多