【问题标题】:Rails .joins() between tables and .where() condition表和 .where() 条件之间的 Rails .joins()
【发布时间】:2017-03-12 12:12:09
【问题描述】:

总结

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

我想用joinAd 模型查询Location 模型,以过滤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)

【问题讨论】:

    标签: ruby-on-rails ruby ruby-on-rails-5


    【解决方案1】:

    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]) 谢谢
    • 我已经编辑了我的答案,这应该可以解决你的问题。
    • 感谢 RSB,它仍然无法正常工作。这是 location_params &lt;ActionController::Parameters {"country"=&gt;"IT", "location"=&gt;"Leizpig"} permitted: true&gt; 而这是 ads_params &lt;ActionController::Parameters {"remote"=&gt;"1"} permitted: true&gt; 并且查询是空的 #&lt;Location::ActiveRecord_Relation:0x3f9eeac68e00&gt;
    • 我不知道如何解决它。我正在考虑做两个步骤:1.首先我根据位置模型过滤所有内容 2.我根据广告模型过滤结果。如果您有任何建议,请告诉我,然后我们用最终答案编辑您的答案,我会接受。
    • 我在想问题可能不是查询,而是位置和广告之间的关联。我从 Location Model 中选择,但 Location has_many :ads。即使我能够通过连接进行查询,那么当我执行 location.ads 时,它也会丢失我在 Ad 上所做的过滤器
    【解决方案2】:

    @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&amp;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的解决方案,但是当我在控制台或调试中尝试查询时它不起作用。
    • 抱歉,问题是参数需要转换成.to_h
    猜你喜欢
    • 2010-09-23
    • 2012-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-03
    • 1970-01-01
    • 2019-07-16
    相关资源
    最近更新 更多