在rails中避免n + 1查询错误有很多关联

时间:2015-08-02 10:31:17

标签: ruby ruby-on-rails-3 postgresql activerecord

这个特定主题有很多答案,但我找不到任何适合我的答案。我有一个配方应用程序,我需要所有给该配方特定评级的用户(例如:给特定配方评级为5的用户列表),而没有n + 1查询错误。我知道它可以使用包含选项解决,但属于参考我不知道如何使用它。我正在使用rails 3.2.15。

下面是我的应用程序的模态级别描述

class Recipe < ActiveRecord::Base
  belongs_to :user, :foreign_key => 'creator_id'
  has_many :photos, :dependent => :destroy
  has_many :recipe_ingredients
  has_many :ingredients, :through => :recipe_ingredients
  has_many :ratings, :dependent => :destroy
end


class  Rating < ActiveRecord::Base
  belongs_to :user, :foreign_key => 'rater_id'
  belongs_to :recipe
end

class Ingredient < ActiveRecord::Base
  belongs_to :user, :foreign_key => 'creator_id'
  has_many :recipe_ingredients
  has_many :recipes, :through => :recipe_ingredients 
end

class User < ActiveRecord::Base
  has_many :recipes , foreign_key: "creator_id", class_name: "Recipe", :dependent => :destroy
  has_many :ingredients, foreign_key: "creator_id", class_name: "Ingredient", :dependent => :destroy
  has_many :ratings, foreign_key: "rater_id", class_name: "Rating", :dependent => :destroy
end

我检索用户的查询是

@rec = Recipe.find(params[:id])
ratings = @rec.ratings.where(:ratings => params[:ratings])

users = ratings.map {|rate| rate.user}

这引入了一个n + 1查询错误,有没有正确使用rails的方法?

2 个答案:

答案 0 :(得分:1)

修改后的查询:

users = ratings.includes(:user).select('users.name')

考虑到您在用户中有名称列,我添加了select('users.name')。您可以使用所需的任何列进行视图。

答案 1 :(得分:1)

@VedprakashSingh 的回答有一个主要的缺点,即返回Rating填充了来自User的数据的实例。所以,突然之间,你没有采用User的所有方法。类型安全被扔出窗外。

您可以做的是使用joins / merge组合来获取另一个模型的实例。像这样:

User.joins(:ratings).merge(
  # Here you can place a relation of ratings
  @rec.ratings.where(:ratings => params[:ratings])
  # Yes, it's your line, copy-pasted from your question
) # ...and back here we get users joined with these

因此,您明确地使用您想要User的事实启动查询。然后你加入表格,得到一个巨大的集合,每个评级与user一起加入(所有评级都在数据库中!)。 merge的内容会将该集中的评分过滤为您想要的评分。

小心,因为我从问题中复制的行可能容易受到伪造参数的影响,因此未对其进行过滤。您有ratings表格,因此,如果params[:ratings]变成哈希(可以完成,它实际上就是用户的输入),它将会被视为条件的哈希,允许用户发送任何基于哈希的条件。

yoursite.com/whatever?ratings[id]=12

该查询字符串将导致params[:ratings]

{"id" => "12"} # Damn right, a Ruby hash

Strong parameters存在是有原因的。

相关问题