使用JOIN语句扩展ActiveRecord多态关联?

时间:2016-10-25 11:36:51

标签: mysql ruby-on-rails ruby inheritance activerecord

我的表格设置为var outputData = inputData.map( Object.values );Child的{​​{1}}关系。

他们共享一个主键(1:1):

  • ParentidParent以及id
  • typenameChild

idhealth的多态继承。我的目标是Child应该返回Parent对象,该对象响应Child.find(1)Child。 SQL语句假设看起来像这样:

name

因此,我试图在ActiveRecord中使用多态继承:

health

当我尝试执行SELECT parents.id, parents.name, childs.health FROM parents LEFT JOIN childs ON childs.id = Parents.id WHERE parents.id = 1 AND parents.type = 'Child' LIMIT 1时,我看到:

class Parent < ApplicationRecord class Child < Parent

值得注意的是,Child.find(1)表上没有SELECT `parents`.* FROM `parents` WHERE `parents`.`type` IN ('Child') AND `parents`.`ID` = 1 LIMIT 1 => #<Child id: 1, type: "Child", name: "Hello">,但我收到了JOIN个对象。这会导致child对象不响应Child的意外行为。奇怪的是,如果我向Child类(health)添加显式表关联,则查询模式将更改为:

Child

现在我可以访问self.table_name = "childs",但不能访问> c = Child.find(1) Obtainable Load (0.3ms) SELECT `childs`.* FROM `childs` WHERE `childs`.`ID` = 2 LIMIT 1health

如何正确创建此JOIN关联,以便尝试加载type对象成功加入来自父级的数据?

编辑:这些表是在ActiveRecord迁移之外创建的(它们也可以由其他预先存在的非Ruby应用程序访问),因此我无法更改其架构。我可以想到一些奇特的元编程方法,比如响应name,这可能让我通过连接懒得加载缺少的属性......但我担心我最终还是要重新实现一个一堆ActiveRecord,如Childmethod_missing等(这将导致错误)。所以我正在寻找一些原生/干净(ish)方法来实现这一目标。

1 个答案:

答案 0 :(得分:2)

这不是这里描述的典型Rails多态关联:http://guides.rubyonrails.org/association_basics.html#polymorphic-associations

所以,在这种情况下,当某些其他应用程序先前创建表格时,我建议您执行以下操作:

class Child < ApplicationRecord
  self.table_name = "childs"
  belongs_to :parent, foreign_key: :id, dependent: :destroy
  delegate :name, :type, to: :parent
  delegate :name=, to: :parent, allow_nil: true

  after_initialize do
    self.build_parent(type: "Child") if parent.nil?
  end
end

class Parent < ApplicationRecord
  self.inheritance_column = 'foo' # otherwise, type column will be used for STI
  has_one :child, foreign_key: :id
  delegate :health, to: :child
end

现在您可以访问运行状况,类型和名称:

> c = Child.joins(:parent).find(1)
  Child Load (0.2ms)  SELECT  "childs".* FROM "childs" INNER JOIN "parents" ON "parents"."id" = "childs"."id" WHERE "childs"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
=> #<Child id: 1, health: "Good", created_at: "2016-10-27 21:42:55", updated_at: "2016-10-27 21:44:08">
irb(main):002:0> c.health
=> "Good"
irb(main):003:0> c.type
  Parent Load (0.1ms)  SELECT  "parents".* FROM "parents" WHERE "parents"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
=> "Child"
irb(main):004:0> c.name
=> "Hello"

,类似于父母:

p = Parent.joins(:child).find(1)
  Parent Load (0.1ms)  SELECT  "parents".* FROM "parents" INNER JOIN "childs" ON "childs"."id" = "parents"."id" WHERE "parents"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
=> #<Parent id: 1, type: nil, name: "Hello", created_at: "2016-10-27 21:40:35", updated_at: "2016-10-27 21:40:35">
irb(main):003:0> p.name
=> "Hello"
irb(main):004:0> p.health
  Child Load (0.1ms)  SELECT  "childs".* FROM "childs" WHERE "childs"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
=> "Good"

如果您愿意,可以像这样指定LEFT JOIN:

irb(main):003:0> p = Parent.joins("LEFT JOIN childs ON (childs.id = parents.id)").select("parents.id, parents.name, childs.health").find(1)
  Parent Load (0.2ms)  SELECT  parents.id, parents.name, childs.health FROM "parents" LEFT JOIN childs ON (childs.id = parents.id) WHERE "parents"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
=> #<Parent id: 1, name: "Hello">
irb(main):004:0> p.name
=> "Hello"
irb(main):005:0> p.health
  Child Load (0.1ms)  SELECT  "childs".* FROM "childs" WHERE "childs"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
=> "Good"