问题是当Restaurant
没有任何符合条件的MenuItems
时,ActiveRecord表示无法找到Restaurant
。这是相关的代码:
class Restaurant < ActiveRecord::Base
has_many :menu_items, dependent: :destroy
has_many :meals, through: :menu_items
def self.with_meals_of_the_week
includes({menu_items: :meal}).where(:'menu_items.date' => Time.now.beginning_of_week..Time.now.end_of_week)
end
end
生成的sql代码:
Restaurant Load (0.0ms)←[0m ←[1mSELECT DISTINCT "restaurants".id FROM "restaurants"
LEFT OUTER JOIN "menu_items" ON "menu_items"."restaurant_id" = "restaurants"."id"
LEFT OUTER JOIN "meals" ON "meals"."id" = "menu_items"."meal_id" WHERE
"restaurants"."id" = ? AND ("menu_items"."date" BETWEEN '2012-10-14 23:00:00.000000'
AND '2012-10-21 22:59:59.999999') LIMIT 1←[0m [["id", "1"]]
但是,根据Rails Guides的这一部分,这不应该发生:
Post.includes(:comments).where("comments.visible", true)
如果在此包含查询的情况下,任何帖子都没有评论,则仍会加载所有帖子。
答案 0 :(得分:2)
生成的SQL是您查询的正确翻译。但看看它, 只是在SQL级别(我稍微缩短了一点):
SELECT *
FROM
"restaurants"
LEFT OUTER JOIN
"menu_items" ON "menu_items"."restaurant_id" = "restaurants"."id"
LEFT OUTER JOIN
"meals" ON "meals"."id" = "menu_items"."meal_id"
WHERE
"restaurants"."id" = ?
AND
("menu_items"."date" BETWEEN '2012-10-14' AND '2012-10-21')
左外连接执行您希望它们执行的工作:餐馆 与menu_items和饭菜相结合;如果没有menu_item 去餐厅,餐厅仍然保留在结果,与 所有缺失的部分(menu_items.id,menu_items.date,...)填入NULL
现在看看那里的第二部分:BETWEEN运营商要求, menu_items.date不为null!还有这个 是您在没有用餐的情况下过滤掉所有餐厅的地方。
因此我们需要以使null-date ok的方式更改查询。
回到红宝石,你可以写:
def self.with_meals_of_the_week
includes({menu_items: :meal})
.where('menu_items.date is NULL or menu_items.date between ? and ?',
Time.now.beginning_of_week,
Time.now.end_of_week
)
end
生成的SQL现在是
.... WHERE (menu_items.date is NULL or menu_items.date between '2012-10-21' and '2012-10-28')
和没有用餐的餐馆留在。
答案 1 :(得分:0)
正如在Rails指南中所说的那样,只有当你不使用“where”子句和“includes”时才会返回查询中的所有帖子,因为使用“where”子句生成OUTER JOIN请求到带有WHERE的DB外表所以DB不会返回任何内容。
当你需要一些对象(全部或部分对象 - 使用基本模型的位置)并且如果有相关模型只获得所有对象时,这样的实现是非常有用的,但如果不是 - 那么只需获取基本模型列表
另一方面,如果您尝试在包含表格时使用条件,那么在大多数情况下,您只想选择具有此条件的对象,这意味着您只想选择具有meal_items的餐馆。
所以在你的情况下,如果你仍然只想使用2个查询(而不是N + 1),我可能会这样做:
class Restaurant < ActiveRecord::Base
has_many :menu_items, dependent: :destroy
has_many :meals, through: :menu_items
cattr_accessor :meals_of_the_week
def self.with_meals_of_the_week
restaurants = Restaurant.all
meals_of_the_week = {}
MenuItems.includes(:meal).where(date: Time.now.beginning_of_week..Time.now.end_of_week, restaurant_id => restaurants).each do |menu_item|
meals_of_the_week[menu_item.restaurant_id] = menu_item
end
restaurants.each { |r| r.meals_of_the_week = meals_of_the_week[r.id] }
restaurants
end
end
更新:当您只是尝试在模型
上执行条件时,Rails 4将引发弃用警告对不起可能的拼写错误。
答案 2 :(得分:0)
我认为对此有一些误解
如果没有where条件,这将生成两个查询的正常集合。
如果在此包含查询的情况下,则没有任何评论 帖子,所有帖子仍然会被加载。通过使用连接(INNER JOIN),连接条件必须匹配,否则不会有记录 回。 [from guides]
我认为这些陈述并未引用示例Post.includes(:comments).where("comments.visible", true)
但请参阅没有where
语句Post.includes(:comments)
所以一切正常!这是LEFT OUTER JOIN的工作方式。
所以...你写道:“如果在这包括查询的情况下,任何帖子都没有评论,那么所有的帖子都会被加载。”好!但只有当没有where
条款时才会这样!你错过了这句话的背景。