扶手:多对多多态关系(Rails: Many to many polymorphic relatio

2019-07-30 05:02发布

请参阅更新的意见。

我一直在挣扎着爬在这一个清晰而直接的答案,我希望这一次我会得到它! :d我肯定有很多使用Rails还是学习,但我明白,我所面临的问题,真的希望更多的帮助。

  • 我有一个名为“任务”的模式。
  • 我有一个所谓的“目标”抽象模型。
  • 我想涉及目标的子类的多个实例任务。
  • 我不使用单表继承。
  • 我想查询多态性关系返回一个混合的结果设定目标的子类。
  • 我想查询目标的子类的单个实例来获得,他们在有关系的任务。

所以,我想多态多任务和目标的子类之间一对多的关系是为了。 更具体地说,我将能够做这样的事情在控制台(当然还有其他地方):

task = Task.find(1)
task.targets
[...array of all the subclasses of Target here...]

但! 假设模型“商店”,“软件”,“办公室”,“车辆”,这是“目标”存在的所有子类,这将是很好也穿越在其他方向的关系:

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...]

通过多态关系暗示的数据库表似乎是能够做这个穿越的,但我看到一些反复出现的主题,试图找到这对我打败多态性关系的精神答案:

  • 用我的例子是,人们似乎要定义存储,软件,办公,车辆的任务,我们可以告诉马上不是一个多态关系, 它只返回一个类型的模型
  • 最后一点相似,人还是要以某种方式或形式来定义任务存储,软件,办公及车辆。 这里最重要的一点是,这种关系是盲目的子类。 我的多晶型最初将只作为目标,而不是他们个人的子类类型进行交互。 再次定义任务中的每个子类开始在多态性关系的目的蚕食。
  • 我看到,在连接表的模型可能是为了,这似乎有点正确的我,只是它增加了一些复杂性我以为Rails的愿意废除。 在这一个我请求缺乏经验。

这似乎是在任一轨功能或社区集体知识的一个小孔。 所以希望能计算器记事我的答案搜索!

谢谢大家谁帮助!

Answer 1:

您可以结合多态性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

......等等。



Answer 2:

虽然SFEley提出的答案是伟大的,有没有一些缺陷:

  • 从目标(商店/车)任务的检索工作,但向后的习惯。 这基本上是因为你不能遍历:通过关联多态数据类型,因为SQL不能分辨出它是什么表。
  • 与每一个模型:通过协会需要与中间表中的直接关联
  • 的:通过分配关联应该是复数
  • 的:如声明不会与一起工作:通过,你需要与中间载台所需的直接关联先指定它

考虑到这一点,我简单的解决办法是:

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

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



Answer 3:

你提到的has_many_polymorphs解决方案并不坏。

class Task < ActiveRecord::Base
  has_many_polymorphs :targets, :from => [:store, :software, :office, :vehicle]
end

似乎做你想要的一切。

它提供了以下方法:

到任务:

t = Task.first
t.targets   # Mixed collection of all targets associated with task t
t.stores    # Collection of stores associated with task t
t.softwares # same but for software
t.offices   # same but for office
t.vehicles  # same but for vehicles

以软件,商店,办公室,交通工具:

s = Software.first    # works for any of the subtargets.
s.tasks               # lists tasks associated with s

如果我正确遵循了意见,剩下的唯一问题是,你不想在每次创建一个新的类型子目标的时间来修改应用程序/模型/ task.rb。 Rails的方式似乎需要您修改两个文件来创建一个双向关联。 has_many_polymorphs只要求您更改任务文件。 好像一场胜利给我。 或者至少它会如果你没有反正编辑新的模型文件。

大约有这几个方面,但他们似乎喜欢太多的工作,以避免在一段时间改变一个文件,每一次。 但是,如果你是坚决反对自己修改任务添加到多态的关系,这是我的建议:

保留的子目标的清单,我会在LIB建议/子目标格式,每行一个条目,在本质上是table_name.underscore。 (大写字母有下划线前缀,然后一切都被换成小写)

store
software
office
vehicle

创建配置/初始化/ subtargets.rb以及与此填充:

SubtargetList = File.open("#{RAILS_ROOT}/lib/subtargets").read.split.reject(&:match(/#/)).map(&:to_sym)

接下来你会想要么创建一个自定义生成器或新的rake任务。 要生成新的子目标,并添加型号名称的子目标列表文件,上面定义。 你可能最终会做一些裸露的骨头,使变化并传递参数给标准的生成。

对不起,我真的不觉得自己现在走你通过正确的,但这里有一些 资源

最后替换has_many_polymorphs声明与SubtargetList名单

class Task < ActiveRecord::Base
  has_many_polymorphs :targets, :from => SubtargetList
end

从你这点可以添加一个新的子目标与

$ script/generate subtarget_model home

一旦你重新加载您的控制台或重新启动生产服务器,这将自动更新您的多态列表。

正如我说,这是一个大量的工作来自动更新列表子目标。 但是,如果你走这条路线,你可以调整自定义生成确保所有的子目标模型所需的部分是那里当你生成它。



Answer 4:

使用STI:

class Task < ActiveRecord::Base
end

class StoreTask < Task
  belongs_to :store, :foreign_key => "target_id"
end

class VehicleTask < Task
  belongs_to :vehicle, :foreign_key => "target_id"
end

class Store < ActiveRecord::Base
  has_many :tasks, :class_name => "StoreTask", :foreign_key => "target_id"
end

class Vehicle < ActiveRecord::Base
  has_many :tasks, :class_name => "VehicleTask", :foreign_key => "target_id"
end

在你DATABSE你将需要: Task type:stringTask target_id:integer

其优点是,现在你有哪些可具体每个任务类型,通过模型。

又见STI和多态模型一起

干杯!



Answer 5:

这可能不是一个特别有用的答案,但是简单地说,我不认为有一个简单的或AUTOMAGIC办法做到这一点。 至少,不容易与简单的一对一或一对多的关联。

我认为,创建连接表ActiveRecord模型是解决这个问题的正确方法。 正常has_and_belongs_to_many关系呈现出两个指定表之间的连接,而在你的情况下,它听起来像你想之间加入tasks和任何一个storessoftwaresoffices ,或vehicles (顺便说一下,有没有理由不使用STI在这里吗?看起来这将有助于通过限制表,你有)的数量降低复杂性。 所以你的情况,连接表还需要知道的名字Target涉及的子类。 就像是

create_table :targets_tasks do |t|
  t.integer :target_id
  t.string :target_type
  t.integer :task_id
end

然后,在你的Task类,你的Target小类和TargetsTask类,您可以设置has_many使用协会:through在作为记录关键字的ActiveRecord ::协会:: ClassMethods的RDoc页面 。

但尽管如此,只有让你的方式的一部分,因为:through将不知道使用target_type字段作为Target的子类的名字。 对于这一点,你也许可以编写一些自定义选择/取景器SQL片段,也记录在的ActiveRecord ::协会:: ClassMethods 。

希望这可以让你在正确的方向前进。 如果你发现一个完整的解决方案,我很想看看吧!



Answer 6:

我同意其他人我会去使用STI的混合物,代表团将是更容易实现的解决方案。

在你的问题的心脏是在哪里存储目标的所有子类的记录。 ActiveRecord的通过STI模式选择的数据库。

你可以在一个类变量将它们存储在目标和使用继承回调到新添加到它。 然后你就可以动态生成,你需要从阵列和杠杆的method_missing的内容的代码。



Answer 7:

你有追逐的蛮力的方法:

class Task 
  has_many :stores
  has_many :softwares
  has_many :offices
  has_many :vehicles

  def targets
    stores + softwares + offices + vehicles
  end
  ...

它可能不是优雅,但说实话,它不是那么详细,并没有什么本质上低效的有关代码。



文章来源: Rails: Many to many polymorphic relationships