非常感谢你的帮助。
我有一个locations
和ads
表。位置has_many :ads
我想查询带有Location
join
模型的Ad
模型,以过滤Location
和Ad
参数的条目。
@locations = Location.joins(:ads).where(locations: location_params, ads: location_params[:ads_attributes])
这是location_params方法(将使用另一种正常工作的方法删除空字段。)
params.require(:location).permit(:country, {:ads_attributes => [:remote, :days]})
这是我的查询示例。我有一个方法从location_params中删除空字段。它工作正常。
SELECT "locations".* FROM "locations" INNER JOIN "ads"
ON "ads"."location_id" = "locations"."id"
WHERE "ads_attributes"."remote" = $1 [["remote", "1"]]
在这种情况下,location_params包含以下字段:
<ActionController::Parameters {"ads_attributes"=>
<ActionController::Parameters {"remote"=>"1"} permitted: true>} permitted: true>
这是结果,即使我在表格中有这些参数的条目
,也是一个空对象#<Location::ActiveRecord_Relation:0x3fb83def8190>
解。使用.includes(:ads)
不使用@locations[0].ads
重新执行查询。
@locations = Location.joins(:ads).where(locations: {id: 1}, ads: {id: 1})
问题是,当我从位置选择ads
时,它会再次执行查询并删除之前的过滤器ads.id = 1.
@locations[0].ads
结果是,不仅会选择ID = 1的广告,而且会选择该@location [0]的所有广告。
我可以执行查询:
@locations = Location.joins(:ads).where(locations: {id: 1}, ads: {id: 1})
或者
@locations = Location.joins(:ads).where(location_params)
但不是
@locations = Location.joins(:ads).where(locations: location_params, ads: ads_params)
但这可以解决如下: 对位置执行第一次查询
@locations = Location.joins(:ads).where(@location_params.require(:location).permit(:id)).includes(:ads)
返回包含这些参数的所有位置,然后我需要根据广告过滤器过滤@locations。问题是我无法执行以下查询。
@locations = @locations.joins(:ads).where(ads: @ads_params.require(:ads).permit(:id)).includes(:ads)
答案 0 :(得分:4)
您需要重写查询,因为ads_attributes
不是表格,请尝试此操作
ads_params = @ads_params.require(:ads).permit(:id).to_h
location_params = @location_params.require(:location).permit(:id).to_h
@locations = Location.joins(:ads).where(locations: location_params, ads: ads_params)
希望有所帮助!
答案 1 :(得分:3)
@locations[0].ads
都会获取第一个位置对象的所有ads
。当然,如果尚未获取它们,它将仅执行获取过程。一种解决方案是侧载ads
:
@locations = Location.joins(:ads).where(locations: {id: 1}, ads: {id: 1}).includes(:ads)
@locations[0].ads
这样可以避免N + 1查询问题。但是你应该小心使用它,因为@locations[0].ads.reload
将加载所有ads
,无论你之前使用的是什么过滤器。
提示:这取决于您的目的是什么,如果您只根据某些标准需要ads
,那么我建议您从Ad.join(:location).where(...).includes(:location)...
开始
<强>更新强>
如果我发送GET /locations?location[country]=IT&ad[remote]=1
class LocationsController < ApplicationController
def index
@locations = Location.joins(:ads).where(locations: location_filters, ads: ad_filters).includes(:ads)
end
private
def location_filters
params.require(:location).permit(:country)
end
def ad_filters
params.require(:ad).permit(:remote, :days)
end
end
或者如果你没有在每种情况下都提供所有的婴儿车,那么你可能想要构建一个查询:
class LocationsController < ApplicationController
def index
@locations = Location.joins(:ads).includes(:ads)
@locations = @locations.where(locations: location_filters) if location_filters.present?
@locations = @locations.where(ads: ad_filters) if ad_filters.present?
end
private
def location_filters
params.fetch(:location, {}).permit(:country)
end
def ad_filters
params.fetch(:ad, {}).permit(:remote, :days)
end
end