【问题标题】:Rails Polymorphic has_manyRails 多态 has_many
【发布时间】:2011-03-13 15:59:10
【问题描述】:

使用 Ruby on Rails,如何实现多态 has_many 关系,其中所有者始终是已知的,但关联中的项目将是某种多态(但同质)类型,由所有者中的列指定?例如,假设Producerhas_many 产品,但生产者实例实际上可能有许多自行车、冰棒或鞋带。我可以轻松地让每个产品类(自行车、冰棒等)与 Producer 有belongs_to 关系,但是给定生产者实例,如果产品类型不同(每个生产者实例),我如何获取产品集合?

Rails 多态关联允许生产者属于许多产品,但我需要相反的关系。例如:

class Bicycle < ActiveRecord::Base
  belongs_to :producer
end

class Popsicle < ActiveRecord::Base
  belongs_to :producer
end

class Producer < ActiveRecord::Base
  has_many :products, :polymorphic_column => :type # last part is made-up...
end

所以我的 Producer 表已经有一个“类型”列,它对应于某些产品类别(例如自行车、冰棒等),但我怎样才能让 Rails 让我做类似的事情:

>> bike_producer.products
#=> [Bicycle@123, Bicycle@456, ...]
>> popsicle_producer.products
#=> [Popsicle@321, Popsicle@654, ...]

对不起,如果这是明显的或常见的重复;我很难轻松实现它。

【问题讨论】:

  • 作为一个注释,我强烈建议不要使用 Factory 作为模型名称,因为 factory_girl 是一个非常常用的扩展,用于生成模型而不是固定装置,这可能会让阅读你的人感到非常困惑代码。
  • @jamie - 感谢您的提示,我已将术语切换为“生产者”,希望不会与任何并发库混淆 =)
  • 没问题。另外,我还没有找到一个好的解决方案。据我所知,Rails 中尚不存在对象具有多态子对象的能力。不过,您可能想查看blog.hasmanythrough.com/2006/4/3/polymorphic-through 以供参考。
  • 是的,请阅读那篇文章。我已经破解了一个简单的实例方法,它使用列值到类名的映射并执行 find_by_producer_id,它工作正常,但我没有得到任何关联便利方法。我认为这肯定是一个已解决的问题,但也许不是......
  • 与从基本产品模型继承所有不同产品相比,使用多态关系有什么优势?单表继承似乎可以解决您的许多问题。

标签: ruby-on-rails associations polymorphic-associations


【解决方案1】:

您必须在生产商上使用 STI,而不是在产品上。这样,每种类型的生产者都有不同的行为,但在一个 producers 表中。

(几乎)完全没有多态性!

class Product < ActiveRecord::Base
  # does not have a 'type' column, so there is no STI here,
  # it is like an abstract superclass.
  belongs_to :producer
end

class Bicycle < Product
end

class Popsicle < Product
end

class Producer < ActiveRecord::Base
  # it has a 'type' column so we have STI here!!
end

class BicycleProducer < Producer
  has_many :products, :class_name => "Bicycle", :inverse_of => :producer
end

class PopsicleProducer < Producer
  has_many :products, :class_name => "Popsicle", :inverse_of => :producer
end

【讨论】:

  • 产品从同一个父类继承会影响它们的可变性吗?例如,Bicycle 是否有“frame_material”属性,而冰棒有“flavor”?
  • @maerics 没有 STI,所有属性都来自子类的每个特定表。
【解决方案2】:

请以格式为准

class Bicycle < ActiveRecord::Base 
  belongs_to :bicycle_obj,:polymorphic => true 
end 

class Popsicle < ActiveRecord::Base
  belongs_to :popsicle_obj , :polymorphic => true 
end 

class Producer < ActiveRecord::Base 
  has_many :bicycles , :as=>:bicycle_obj 
  has_many :popsicle , :as=>:popsicle_obj 
end 

使用此代码。如果您有任何问题,请发表评论。

【讨论】:

  • 等等 - 你为什么要发布两个答案?
  • 我评论的答案格式不正确,这就是我给出单独答案的原因。
  • 感谢@Jamie Wong:实际上我是stackoverflow的新人,我不知道格式化文本
  • 如果您需要大量空间来回复评论,请编辑您的原始答案并注意它是对评论的回复。
  • 这个策略并不是我想要的,因为当我有一个生产者时,我需要知道它将有什么样的产品,以便决定是调用p.bicycles 还是@987654323 @,我想要一个返回产品的方法(例如p.products)。
【解决方案3】:

这是我目前正在使用的解决方法。它不提供您从真正的 ActiveRecord::Associations 获得的任何便利方法(收集操作),但它确实提供了一种获取给定生产者的产品列表的方法:

class Bicycle < ActiveRecord::Base
  belongs_to :producer
end

class Popsicle < ActiveRecord::Base
  belongs_to :producer
end

class Producer < ActiveRecord::Base
  PRODUCT_TYPE_MAPPING = {
    'bicycle' => Bicycle,
    'popsicle' => Popsicle
  }.freeze
  def products
    klass = PRODUCT_TYPE_MAPPING[self.type]
    klass ? klass.find_all_by_producer_id(self.id) : []
  end
end

另一个缺点是我必须维护类型字符串到类型类的映射,但这可以自动化。但是,此解决方案足以满足我的目的。

【讨论】:

    【解决方案4】:

    我发现 Rails 中没有记录多态关联。 有一个单表继承模式,这是获得最多文档的, 但是如果你没有使用单表继承,那么就会缺少一些信息。

    belongs_to 关联可以使用 :polymorphic => true 选项启用。但是,除非您使用单表继承,否则 has_many 关联不起作用,因为它需要知道可能具有外键的表集。

    (根据我的发现),我认为干净的解决方案是为基类提供一个表和模型,并在基表中有外键。

    create_table "products", :force => true do |table|
        table.integer  "derived_product_id"
        table.string   "derived_product_type"
        table.integer  "producer_id"
      end
    
      class Product < ActiveRecord::Base
        belongs_to :producer
      end
    
      class Producer < ActiveRecord::Base
        has_many :products
      end
    

    然后,对于 Production 对象 producer,您应该使用 producer.products.derived_products 获取产品。

    我还没有使用 has_many 来压缩与 producer.derived_products 的关联,所以我无法评论如何让它发挥作用。

    【讨论】:

      【解决方案5】:
      class Note < ActiveRecord::Base
      
       belongs_to :note_obj, :polymorphic => true
       belongs_to :user
      
      
      end
      
      
      class Contact < ActiveRecord::Base
      
       belongs_to :contact_obj, :polymorphic => true
       belongs_to :phone_type 
      
      end
      
      
      
      class CarrierHq < ActiveRecord::Base
      
      
       has_many :contacts, :as => :contact_obj
       has_many :notes, :as => :note_obj
      
      
      end
      

      【讨论】:

      • 您能否稍微解释一下您的答案,甚至可以修改它以使用问题中的生产者/产品术语?
      • class Bicycle true end class Popsicle true end class Producer :bicycle_obj has_many :popsicle , :as=>:popsicle_obj end 如果您对使用对象有任何问题,请使用此代码,然后请使用代码对此发表评论。
      猜你喜欢
      • 2011-06-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-08
      相关资源
      最近更新 更多