【问题标题】:Rails polymorphic many to many associationRails 多态多对多关联
【发布时间】:2009-06-06 03:09:46
【问题描述】:

我正在尝试设置一种通用的相关对象网络。假设我有 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]

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

显然我不想创建一大堆连接表。这似乎不是一个有很多直通关联,也不是一个多态关联。

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

【问题讨论】:

    标签: ruby-on-rails


    【解决方案1】:

    从早期开始,对多态性的支持有了显着改善。您应该能够在 Rails 2.3 中通过为所有模型使用单个连接表来实现这一点——一个关系模型。

    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
    

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

    ( pwned_movies + pwning_movies ).uniq
    

    这个问题的一个常见例子是社交网络应用程序中的“朋友”关系。 Insoshi 使用的一种解决方案是在连接模型上注册一个after_create 回调(在本例中为Relation),这会创建反向关系。 after_destroy 回调同样是必要的,但通过这种方式,以一些额外的数据库存储为代价,您可以确信您将在单个数据库查询中获得所有相关电影。

    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
    

    【讨论】:

      【解决方案2】:

      我想出了一些解决方案。不过我不确定它是不是最好的。看来你不能通过多态 has_many。

      所以,我有点假。但这意味着放弃我非常喜欢的协会代理魔法,这让我很伤心。在基本状态下,它是这样工作的。

      book = Book.find(1)
      book.add_related(Tag.find(2))
      book.add_related(Category.find(3))
      book.related        #=> [Tag#2, Category#3]
      book.related(:tags) #=> [Tag#2]
      

      我将它封装在一个可重用的模块中,可以通过单个 has_relations 类方法将其添加到任何模型类中。

      http://gist.github.com/123966

      我真的希望我不必完全重新实现关联代理来处理这个问题。

      【讨论】:

        【解决方案3】:

        我认为完全按照您的描述进行操作的唯一方法是连接表。不过还不错,只有 6 个,而且您几乎可以一劳永逸地忘记它们。

        【讨论】:

        • 这只是一个例子。我的实际数据模型没有这些确切的东西,而是更多的东西。最后,如果我走这条路,我们会查看十几个连接表或更多。
        • 哎呀...明白了。我会再考虑一下。
        【解决方案4】:

        取决于您的电影/书籍数据库表的相关程度

        如果你声明了会怎样

        class Items < ActiveRecord::Base
          has_many :tags
          has_many :categories
          has_and_belongs_to_many :related_items,
               :class => "Items",
               :join_table => :related_items,
               :foreign_key => "item_id",
               :associated_foreign_key => "related_item_id"
        end
        
        class Books < Items
        class Movies < Items
        

        确保您在项目表中输入类型

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-08-30
          • 2021-03-23
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多