Rails中的.joins()和.where()如何在表之间连接和添加条件?

7

概述

非常感谢您的帮助。 我有一个名为locationsads的表格。位置has_many:ads

我想要使用Location模型的joinAd模型进行查询,以根据LocationAd的参数过滤条目。

@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>

更新

  1. 第一个问题 - 来自Péter Tóth

解决方案。使用.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]的广告。

  1. 第二个问题

我能够执行这个查询:

@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)
2个回答

8

由于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)

希望能帮到你!

抱歉,我无法使用“{ country: location_params[:country] }”选择特定列。有几个字段,并且有一个方法可以删除它们(如果它们为空)。如果“location_params [: country]”为空,则将其删除并将其设置为“nil”。我想只需将“location_params”输入查询而不指定要选择的特定列。您知道如何修复以下查询以使其正常工作吗? @locations = Location.joins(:ads).where(locations: new_params, ads: new_params[:ads_attributes]) 谢谢。 - Fabrizio Bertoglio
我已经编辑了我的回答,这应该解决你的问题。 - Rajdeep Singh
谢谢 RSB,它仍然不起作用。这是location_params <ActionController :: Parameters {"country"=>"IT", "location"=>"Leizpig"} permitted: true>,而这是ads_params<ActionController :: Parameters {"remote"=>"1"} permitted: true>,查询为空 #<Location::ActiveRecord_Relation:0x3f9eeac68e00> - Fabrizio Bertoglio
我无法想出如何解决它。我正在考虑采取两个步骤: 1.首先,我会根据位置模型过滤所有内容。 2.然后,我会根据广告模型过滤结果。如果您有任何建议,请告诉我,我们将编辑您的答案并接受。 - Fabrizio Bertoglio
我在思考问题可能不是查询,而是位置和广告之间的关联。我正在从位置模型中选择,但位置有许多广告。即使我能够通过连接进行查询,但当我执行location.ads时,它将丢失我对广告所做的过滤器。 - Fabrizio Bertoglio
你想用另一个讨论的内容来编辑你的答案吗?我认为这个问题也应该把你作为正确答案。http://stackoverflow.com/questions/42760581/rails-where-query-not-working?noredirect=1#comment72637634_42760581 - Fabrizio Bertoglio

6

@locations[0].ads将获取第一个位置对象的所有广告,无论如何。当然,如果它们尚未被获取,它只会执行获取过程。一种解决方案是侧向加载广告

@locations = Location.joins(:ads).where(locations: {id: 1}, ads: {id: 1}).includes(:ads)
@locations[0].ads

这样可以避免N + 1查询问题。但是你应该小心使用,因为@locations[0].ads.reload会加载所有的广告,无论之前使用了什么过滤器。
提示:这取决于您的目的是什么,如果您只需要根据某些标准查找广告,则建议从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

谢谢。这解决了我无法执行 @locations[0].ads 的特定问题,但我的问题现在是让 @locations 填充。在我的情况下,我无法使用 id 或特定参数,因为当它们为空时,这些字段会被另一种方法删除。因此,我想使用 @RSB 的解决方案,但当我在控制台或调试中尝试查询时,它不起作用。 - Fabrizio Bertoglio
抱歉,问题在于需要将参数转换为 .to_h。 - Fabrizio Bertoglio

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接