Rails中的多个INNER JOIN具有多种关系?

时间:2011-10-30 19:45:20

标签: ruby-on-rails-3 activerecord

我有以下型号:

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,导致User.find(1).bookmarked_tags导致

  

ActiveRecord :: HasManyThroughSourceAssociationMacroError:无效   源反射宏:has_many:通过for_many   :bookmarked_tags,:through =&gt; :bookmarked_images。使用:来源   指定源反射。

那么:我怎样才能让Rails运行我用User.find(1).bookmarked_tags发布的查询?

1 个答案:

答案 0 :(得分: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查找所需):

<强> 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

现在我们可以为 user.rb

添加bookmarked_tags方法
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