查看评论以了解更新。
我一直在努力在这个问题上得到明确而直截了当的答案,我希望这次我能得到它! :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...]
多态关系隐含的数据库表似乎能够进行这种遍历,但我在尝试寻找一个能够打败多态关系精神的答案时看到一些反复出现的主题:
这似乎是导轨功能或集体社区知识中的一个小漏洞。所以希望stackoverflow可以记录我的搜索答案!
感谢所有帮助过的人!
答案 0 :(得分:55)
您可以结合使用多态和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
......等等。
答案 1 :(得分:1)
你提到的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
如果我正确地关注了这些评论,那么唯一剩下的问题就是您不希望每次创建新类型的子目标时都必须修改app / models / task.rb。 Rails方式似乎要求您修改两个文件以创建双向关联。 has_many_polymorphs只需要您更改Tasks文件。对我来说似乎是一场胜利。或者至少,如果您不必编辑新的模型文件,那么至少也是如此。
有一些方法可以解决这个问题,但是它们看起来似乎太过分了,以避免每隔一段时间更换一个文件。但是,如果你自己修改了Task来增加多态关系,那么这就是我的建议:
保留一个子目标列表,我将在lib / subtargets中建议每行格式化一个条目,实际上是table_name.underscore。 (大写字母带有下划线前缀,然后一切都是小写的)
store
software
office
vehicle
创建config / initializers / subtargets.rb并填写:
SubtargetList = File.open("#{RAILS_ROOT}/lib/subtargets").read.split.reject(&:match(/#/)).map(&:to_sym)
接下来,您将要创建自定义生成器或新的rake任务。生成新的子目标并将模型名称添加到上面定义的子目标列表文件中。你可能最终会做一些简单的事情来做出改变并将参数传递给标准生成器。
很抱歉,我现在不想让您完成这项工作,但这里有some resources
最后用hastargetList
替换has_many_polymorphs声明中的列表class Task < ActiveRecord::Base
has_many_polymorphs :targets, :from => SubtargetList
end
从此时起,您可以使用
添加新的子目标$ script/generate subtarget_model home
一旦重新加载控制台或重新启动生产服务器,这将自动更新您的多态列表。
正如我所说,自动更新子目标列表需要做很多工作。但是,如果你选择这条路线,你可以调整自定义生成器,确保在生成子目标模型时,所有必需的部分都在那里。
答案 2 :(得分:1)
使用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
在您的数据库中,您需要:
Task type:string
和Task target_id:integer
优势在于,现在每个任务类型都有一个直通模型,可以是特定的。
另见STI and polymorphic model together
干杯!
答案 3 :(得分:0)
这可能不是一个特别有用的答案,但简单地说,我认为没有一种简单或自动的方式来做到这一点。至少,不像简单的一对一或多对话那么容易。
我认为为连接表创建ActiveRecord模型是解决问题的正确方法。正常的has_and_belongs_to_many
关系假定两个指定表之间的连接,而在您的情况下,您似乎想要在tasks
和stores
,softwares
中的任何一个之间加入{ {1}},或者offices
(顺便说一下,有没有理由不在这里使用STI?看起来它有助于通过限制你拥有的表数来降低复杂性)。因此,在您的情况下,连接表还需要知道所涉及的vehicles
子类的名称。像
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
关联记录在ActiveRecord::Associations::ClassMethods rdoc pages。
但是,这仍然只能帮助您解决问题,因为:through
无法使用:through
字段作为target_type
子类名称。为此,您可以编写一些自定义选择/查找器SQL片段,也在ActiveRecord::Associations::ClassMethods中进行了记录。
希望这能让你朝着正确的方向前进。如果你找到了完整的解决方案,我很乐意看到它!
答案 4 :(得分:0)
我同意其他人,我会寻求使用STI混合的解决方案,委托会更容易实现。
问题的核心是存储Target的所有子类的记录。 ActiveRecord通过STI模型选择数据库。
您可以将它们存储在Target中的类变量中,并使用继承的回调向其中添加新的回调。然后,您可以从该数组的内容动态生成您需要的代码,并利用method_missing。
答案 5 :(得分:0)
你有没有追求那种蛮力方法:
class Task
has_many :stores
has_many :softwares
has_many :offices
has_many :vehicles
def targets
stores + softwares + offices + vehicles
end
...
它可能不那么优雅,但说实话,它并不是那么冗长,而且代码本身没有任何效率。