我正在尝试找到将参考模型包含在基本上是复合键的最佳方法。
我有ChecklistItem
(每日要做的事情列表),然后是ChecklistChecks
(将ChecklistItem
与User
联系在一起特定的一天。这些清单可以适用于所有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
答案 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 []>