在ActiveRecord

时间:2015-05-04 11:58:28

标签: ruby-on-rails postgresql ruby-on-rails-4 activerecord

我正在尝试找到将参考模型包含在基本上是复合键的最佳方法。

我有ChecklistItem(每日要做的事情列表),然后是ChecklistChecks(将ChecklistItemUser联系在一起特定的一天。这些清单可以适用于所有Stores(空store_id)或特定Store

这会拉出所有ChecklistItems及其相关检查:

ChecklistItem.includes(:checklist_checks).where(store_id: [nil,@store.id], list_type: 'open')

问题是那里会有多天的支票。我需要的是从特定日期提取所有ChecklistItems和任何支票。

我尝试添加这样的条件:

ChecklistItem.includes(:checklist_checks).where(store_id: [nil,@store.id], list_type: 'open', checklist_checks: {store_id: @store.id, report_date: @today})

问题是只会提取具有关联ChecklistItems的<{1}}。

它生成的SQL本质上是:

ChecklistCheck

我认为问题是SELECT checklist_items.*, checklist_checks.* FROM checklist_items LEFT OUTER JOIN checklist_checks ON checklist_checks.checklist_item_id = checklist_items.id WHERE checklist_items.list_type = 'open' AND checklist_checks.store_id = 1 AND checklist_checks.report_date = '2015-05-03' AND (checklist_items.store_id = 1 OR checklist_items.store_id IS NULL) 上的条件在WHERE子句中。如果我可以将它们移动到连接的ON子句,那么一切都会起作用。

是否有Rails方式最终得到这样的东西?

checklist_checks

更新 我发现了这个:enter link description here 它建议使用SELECT checklist_items.*, checklist_checks.* FROM checklist_items LEFT OUTER JOIN checklist_checks ON checklist_checks.checklist_item_id = checklist_items.id AND checklist_checks.store_id = 1 AND checklist_checks.report_date = '2015-05-03' WHERE checklist_items.list_type = 'open' AND (checklist_items.store_id = 1 OR checklist_items.store_id IS NULL) ,然后将结果数组和模型传递给find_by_sql

我试过了,我的ActiveRecord::Associations::Preloader.new.preload提取了正确的内容,但find_by_sql列在生成的对象中为零。

id

1 个答案:

答案 0 :(得分:1)

使用Arel生成自定义连接子句的解决方案:

class ChecklistItem < ActiveRecord::Base

  has_many :checklist_checks


  # ...

  def self.superjoin(date, store_id)

    # build the ON clause for the join
    on = Arel::Nodes::On.new(
      Arel::Nodes::Equality.new(ChecklistChecks.arel_table[:checklist_item_id], ChecklistItem.arel_table[:id]).\
      and(ChecklistItem.arel_table[:store_id].eq(1)).\
      and(ChecklistChecks.arel_table[:report_date].eq(date))
    )
    joins(Arel::Nodes::OuterJoin.new(ChecklistChecks.arel_table, on))
      .where(store_id: [nil, store_id], list_type: 'open' )
  end
end

我将它捆绑到一个模型方法中,以便更容易在rails控制台中进行测试。

irb(main):117:0> ChecklistItem.superjoin(1,2)
  ChecklistItem Load (0.5ms)  SELECT "checklist_items".* FROM "checklist_items" LEFT OUTER JOIN "checklist_checks" ON "checklist_checks"."checklist_item_id" = "checklist_items"."id" AND "checklist_items"."store_id" = 1 AND "checklist_checks"."report_date" = 1 WHERE (("checklist_items"."store_id" = 2 OR "checklist_items"."store_id" IS NULL)) AND "checklist_items"."list_type" = 'open'
=> #<ActiveRecord::Relation []>