Rails polymorphic many to many association

后端 未结 4 457
无人及你
无人及你 2021-02-04 21:39

I\'m trying setup a generic sort of web of related objects. Let say I have 4 models.

  • Book
  • Movie
  • Tag
  • Category

I would lik

4条回答
  •  眼角桃花
    2021-02-04 22:16

    Support for polymorphism has improved dramatically since the early days. You should be able to achieve this in Rails 2.3 by using a single join table for all your models -- a Relation model.

    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
    

    A drawback of this kind of data structure is that you are forced to create two different roles for what may be an equal pairing. If I want to see all the related movies for my Book, I have to add the sets together:

    ( pwned_movies + pwning_movies ).uniq
    

    A common example of this problem is the "friend" relationship in social networking apps. One solution used by Insoshi, among others, is to register an after_create callback on the join model ( Relation, in this case ), which creates the inverse relationship. An after_destroy callback would be similarly necessary, but in this way at the cost of some additional DB storage you can be confident that you will get all your related movies in a single DB query.

    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
    

提交回复
热议问题