Rails:多对多的多态关系

Ale*_*zzi 33 ruby-on-rails polymorphic-associations

查看评论以获取更新.

我一直在努力在这个问题上得到明确而直截了当的答案,我希望这次我能得到它!:D我肯定还有很多东西需要学习Rails,但我确实理解我面临的问题,并且非常感谢额外的帮助.

  • 我有一个名为"任务"的模型.
  • 我有一个名为"目标"的抽象模型.
  • 我想将Target的子类的多个实例与Task相关联.
  • 我没有使用单表继承.
  • 我想查询多态关系以返回Target的子类的混合结果集.
  • 我想查询Target的子类的各个实例来获取它们与之关系的任务.

因此,我认为任务与子目标的子类之间的多对多关系是有序的.更详细地说,我将能够在控制台(当然还有其他地方)做这样的事情:

task = Task.find(1)
task.targets
[...array of all the subclasses of Target here...]
Run Code Online (Sandbox Code Playgroud)

但!假设存在"存储","软件","办公室","车辆"等模型,它们都是"目标"的子类,那么在另一个方向上遍历关系也会很好:

store = Store.find(1)
store.tasks
[...array of all the Tasks this Store is related to...]
software = Software.find(18)
software.tasks
[...array of all the Tasks this Software is related to...]
Run Code Online (Sandbox Code Playgroud)

多态关系隐含的数据库表似乎能够进行这种遍历,但我在尝试寻找一个能够打败多态关系精神的答案时看到一些反复出现的主题:

  • 仍然使用我的例子,人们似乎想要在任务中定义商店,软件,办公室,车辆,我们可以立即告诉他们不是多态关系,因为它只返回一种类型的模型.
  • 与最后一点类似,人们仍然希望以单向形式或形式定义任务中的商店,软件,办公室和车辆.这里重要的一点是这种关系对子类化是盲目的.我的多态性最初只会与目标进行交互,而不是作为其各自的子类类型.再次定义Task中的每个子类开始蚕食多态关系的目的.
  • 我看到连接表的模型可能是有序的,这似乎对我来说有点正确,除了它增加了我认为Rails愿意废除的一些复杂性.我对这一点感到缺乏经验.

它似乎是导轨功能或集体社区知识中的一个小漏洞.所以希望stackoverflow可以记录我的搜索答案!

感谢所有帮助的人!

SFE*_*ley 56

您可以结合使用多态并has_many :through获得灵活的映射:

class Assignment < ActiveRecord::Base
  belongs_to :task
  belongs_to :target, :polymorphic => true
end

class Task < ActiveRecord::Base
  has_many :targets, :through => :assignment
end

class Store < ActiveRecord::Base
  has_many :tasks, :through => :assignment, :as => :target
end

class Vehicle < ActiveRecord::Base
  has_many :tasks, :through => :assignment, :as => :target
end
Run Code Online (Sandbox Code Playgroud)

......等等.

  • 我遇到了类似的问题,但关于多态的关系结束.我猜这个答案是不完整的,我添加了额外的`has_many:assigments,:as => target`到`Task`和`Vehicle`模型,以及`has_many:tasks,:through =>:assignment,:as =>: target`缩写为`has_many:tasks,:through =>:assignments` (4认同)
  • 我得到了相当于`ActiveRecord :: HasManyThroughAssociationNotFoundError:在我在Rails 3中的应用程序中设置它时,找不到关联│:模型任务中的赋值.我正在使用模块(两者都在同一模块中) ),会导致此错误吗? (3认同)
  • 这似乎非常简单地解决了这个问题,并且Rails 100%支持. (2认同)

Rap*_*oni 12

尽管SFEley提出的答案很好,但存在一些缺陷:

  • 从目标(商店/车辆)检索任务是有效的,但后退不行.这基本上是因为你无法遍历:通过关联到多态数据类型,因为SQL无法分辨它所在的表.
  • 具有:through关联的每个模型都需要与中间表直接关联
  • :通过分配关联应该是复数形式
  • :as语句不能与:through一起使用,您需要首先使用中间表所需的直接关联来指定它

考虑到这一点,我最简单的解决方案是:

class Assignment < ActiveRecord::Base
  belongs_to :task
  belongs_to :target, :polymorphic => true
end

class Task < ActiveRecord::Base
  has_many :assignments
  # acts as the the 'has_many targets' needed
  def targets
    assignments.map {|x| x.target}
  end
end

class Store < ActiveRecord::Base
  has_many :assignments, as: :target
  has_many :tasks, :through => :assignment
end

class Vehicle < ActiveRecord::Base
  has_many :assignments, as: :target
  has_many :tasks, :through => :assignment, :as => :target
end
Run Code Online (Sandbox Code Playgroud)

参考文献:http: //blog.hasmanythrough.com/2006/4/3/polymorphic-through


Cod*_*all 0

这可能不是一个特别有用的答案,但简单地说,我认为没有一种简单或自动的方法可以做到这一点。至少,不像更简单的一对一或多对多关联那么容易。

我认为为连接表创建 ActiveRecord 模型是解决该问题的正确方法。正常has_and_belongs_to_many关系假设两个指定表之间存在联接,而在您的情况下,听起来您想要在 、 、 或 中的任何一个之间联接(顺便说tasks一句,是否有理由不在这里使用 STI?看起来像通过限制您拥有的表的数量来帮助降低复杂性)。因此,在您的情况下,连接表还需要知道所涉及的子类的名称。就像是storessoftwaresofficesvehiclesTarget

create_table :targets_tasks do |t|
  t.integer :target_id
  t.string :target_type
  t.integer :task_id
end
Run Code Online (Sandbox Code Playgroud)

然后,在您的Task类、Target子类和类中,您可以使用ActiveRecord::Associations::ClassMethods rdoc 页上记录的关键字来TargetsTask设置关联。has_many:through

但是,这仍然只能帮助您完成部分任务,因为:through您不知道如何使用该target_type字段作为Target子类名称。为此,您也许可以编写一些自定义 select/finder SQL 片段,这些片段也记录在ActiveRecord::Associations::ClassMethods中。

希望这能让您朝着正确的方向前进。如果您找到完整的解决方案,我很乐意看到它!