【问题标题】:Set has_many or has_one dynamically according to object own attributes根据对象自身属性动态设置has_many或has_one
【发布时间】:2015-04-23 15:59:36
【问题描述】:

在一个类中我需要根据对象 *own 属性动态设置 has_many 或 has_one 关联(这样外来对象就不需要改变了)。

类似:

class Child < ActiveRecord::Base
   if orphan == true #<-- I can't find the good solution for this condition
     has_one :parent
   else 
     has_many :parents
   end
end

关于我需要保留的“父”类:

class Parent < ActiveRecord::Base
  belongs_to :children #this is true if the child is orphan or not
end

有办法吗?

以防万一:我使用的是 rails 3.2.14

【问题讨论】:

  • 我不相信这是可能的描述。你确定这是最好的解决方案吗?为什么你“需要”这样做?为什么不只使用一个别名方法来返回单个父项或基于child.parent.size 的数组?
  • 嗨乔,问题是我想尽可能少地修改应用程序。问题是,在我指定的情况下,“孩子”是一个“请求”(对一个组提出),而“父母”是一个由各个组满足的“协议”——过去是在“has_one”关联中链接。你可以想象,它们都是相当复杂的模型(有几个视图和控制器与之关联),我想要一个 DRY 代码,尽可能少地改变。 --也就是说,是的,如果 child.orphan == true 而不是调用父级,则条件可能会转到控制器

标签: ruby-on-rails ruby-on-rails-3 activerecord


【解决方案1】:

我的第一个想法是使用单表继承,因为 Orphan 是一种具有不同行为的特定孩子。

在决定这是否是正确的路线时,我发现this post 真的很有帮助。

如果您选择使用 STI,您最终会得到类似...

class Child < ActiveRecord::Base
end

class Orphan < Child
  has_one :parent
end

class StereotypicalFamilyUnit < Child
  has_many :parents
end

Rails 还将假定并期望您在 Child 表中有一个“类型”字符串列。当您调用 Orphan.new 时,它将创建一个“孤儿”类型的子项。如果你做“Orphan.first”,它基本上就是在幕后做 Child.where(type: "Orphan").first。

另一种选择(我最终在我正在构建的应用程序中选择了它来代替 STI)是像你一样拥有一个布尔值 suggested and use scopes,它可能看起来像:

class Child < ActiveRecord::Base
  has_many :parents

  scope :orphan, -> { where(orphan: true) }
end

这让您可以改为调用 Child.orphan.new 或 Child.orphan.first,并在 orphan 为 true 时在视图/控制器中执行不同的操作。然后,您还可以按照 this prior post 中的建议为孤儿创建自定义验证。

【讨论】:

  • (继续)实际上您的解决方案有效,但我仍然需要修改“父母”模型(belongs_to child 似乎不起作用,所以我需要指定 belongs_to :orphan 和 @987654328 @ 最后,我只是简单地同时添加了:has_one :parenthas_many :parents,它成功了(我现在可以调用 child.parent 或 child.parents)顺便说一句:我想知道那是 / 不是错误——虽然我不是这个。