Rails多态多对多关联

Ale*_*yne 3 ruby-on-rails

我正在尝试设置一个通用的相关对象网络.假设我有4个型号.

  • 电影
  • 标签
  • 类别

我想能够做到:

book = Book.find(1)
book.relations << Tag.find(2)
book.relations << Category.find(3)
book.relations #=> [Tag#2, Category#3]

movie = Movie.find(4)
movie.relations << book
movie.relations << Tag.find(5)
movie.relations #=> [Book#1, Tag#5]
Run Code Online (Sandbox Code Playgroud)

基本上我希望能够获取任何模型类(或我允许的模型类)的任何2个对象,并声明它们是相关的.

显然我不想创建一大堆连接表.这似乎并不是很多通过关联,而不是一个多态的关联.

这是Rails可以通过它的关联声明支持的东西,还是我应该在这里滚动我自己的逻辑?

aus*_*ton 7

自早期以来,对多态性的支持已经大大改善.您应该能够在Rails 2.3中通过对所有模型使用单个连接表来实现这一点 - 一个Relation模型.

class Relation
  belongs_to :owner, :polymorphic => true
  belongs_to :child_item, :polymorphic => true
end

class Book
  has_many :pwned_relations, :as => :owner, :class_name => 'Relation'
  has_many :pwning_relations, :as => :child_item, :class_name => 'Relation'

  # and so on for each type of relation
  has_many :pwned_movies, :through => :pwned_relations, 
           :source => :child_item, :source_type => 'Movie'
  has_many :pwning_movies, :through => :pwning_relations, 
           :source => :owner, :source_type => 'Movie'
end
Run Code Online (Sandbox Code Playgroud)

这种数据结构的一个缺点是,您被迫为可能相同的配对创建两个不同的角色.如果我想查看我的书的所有相关电影,我必须将这些集合添加到一起:

( pwned_movies + pwning_movies ).uniq
Run Code Online (Sandbox Code Playgroud)

这个问题的一个常见例子是社交网络应用程序中的"朋友"关系.Insoshi使用的一种解决方案是after_create在连接模型上注册回调(Relation在这种情况下),这会产生反向关系.一个after_destroy回调将是同样必要的,但这种方式在一些额外的DB存储的成本,你可以相信你会得到一个单一的数据库查询所有相关的电影.

class Relation
  after_create do 
    unless Relation.first :conditions => 
      [ 'owner_id = ? and owner_type = ? and child_item_id = ? and child_item_type = ?',       child_item_id, child_item_type, owner_id, owner_type ]
      Relation.create :owner => child_item, :child_item => owner
    end
  end
end
Run Code Online (Sandbox Code Playgroud)