使用has_many关系将查询范围重用于同一个STI表

时间:2015-03-30 11:35:36

标签: ruby-on-rails ruby activerecord arel aasm

Child1和Child2与Entity有STI关系,Child2 has_many Child1。 Child1有一个由AASM管理的状态列。

class Entity < ActiveRecord::Base
end

class Child1 < Entity
  include AASM
  aasm_column 'status' do
    state :owned #also creates scope Child1.owned
    state :sold
  end

  belongs_to :child2
end

class Child2 < Entity
  has_many :child1s
end

我想在Child1上为Child1上的每个状态创建一个范围。它应该返回所有具有一个或多个处于该状态的Child1记录的Child2记录。理想情况下,它会重用AASM自动创建的范围,例如

scope :owned, -> {joins(:child1s).merge(Child1.owned)} #in Child2

......这很干净又干爽。不幸的是,这个生成的SQL被同一个表上的连接搞糊涂了:

irb(main):001:0> Child2.owned
  Child2 Load (35.5ms)  SELECT "entities".* FROM "entities"
INNER JOIN "entities" "child1_entities" ON "child1_entities"."child2_id" = "entities"."id" AND "child1_entities"."type" IN ('Child1')
WHERE "entities"."type" IN ('Child2') AND "entities"."status" = 'owned'

where子句的最后一部分应为child1_entities.status = 'owned'

我可以在SQL或Arel中编写整个查询,但我希望找到一些东西,即使我必须去那里为child1连接指定一个别名,我仍然可以重用我在Child1中已有的范围。

1 个答案:

答案 0 :(得分:3)

您可以在不使用任何具有两个范围关系的第三方宝石的情况下执行此操作

class Child2 < Entity
  has_many :child1s
  has_many :owned_child1s, -> {owned}, class_name: "Child1"
  has_many :sold_child1s, -> {sold}, class_name: "Child1"
end

然后找到所有拥有或出售的Child2:child1s,你将使用以下查询

Child2.joins(:owned_child1s) #=> All Child2 having owned children
Child2.joins(:sold_child1s)  #=> All Child2 having sold children

您可以使用以下命令将其作为Child2的范围:

class Child2 < Entity    
  has_many :child1s
  has_many :owned_child1s, -> {owned}, class_name: "Child1"
  has_many :sold_child1s, -> {sold}, class_name: "Child1"

  scope :with_owned_child1s, -> {joins(:owned_child1s)}
  scope :with_sold_child1s, -> {joins(:solid_child1s)}
end

然后,作用域关系使代码更具可读性

Child2.with_owned_child1s.where(....) etc.