活动记录:急切加载与参数的关联

时间:2013-03-21 08:46:35

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

我有以下型号:

class Rating < ActiveRecord::Base
  belongs_to :item
  belongs_to :user
end

class Item < ActiveRecord::Base
  has_many :ratings
end

我想获取所有项目,以及特定用户的评分,以显示每个项目旁边的当前用户的评分(如果存在!)。

我试过......

Item.includes(:ratings).where('ratings.user_id = ?', user_id)

...但是这不会给我没有评级的项目。

我的第一个想法是与参数的has_many关联,然后使用includes方法传递该参数。但这似乎并不存在。

如何在不进行N + 1查询或将所有实体加载到内存中的情况下,对参数过滤所有帖子和热切加载关联?

3 个答案:

答案 0 :(得分:5)

第1步

在模型层中访问current_user(此处概述了一种技术:https://stackoverflow.com/a/2513456/163203

第2步

添加与在运行时评估的条件的关联。

Rails 2

class Item < ActiveRecord::Base
  has_many :ratings
  has_many :current_user_ratings, 
           :class_name => "Rating", 
           :conditions => 'ratings.user_id = #{User.current_user.try(:id)}'
end

Rails 3.1及以上

class Item < ActiveRecord::Base
  has_many :ratings
  has_many :current_user_ratings, 
           :class_name => "Rating", 
           :conditions => proc { ["ratings.user_id = ?", User.current_user.try(:id)] }
end

第3步

Item.includes(:current_user_ratings)

答案 1 :(得分:0)

看起来您基本上建模has_many :through关系:项目has_and_belongs_to_many用户,评级是连接模型。您可以在Rails Guide to Active Record Associations中了解:through关系。

如果是这种情况,我建议使用has_many :through构建模型关系,如下所示:

class Rating < ActiveRecord::Base
  attr_accessible :item_id, :user_id
  belongs_to :item
  belongs_to :user
end

class User < ActiveRecord::Base
  has_many :ratings
  has_many :rated_items, :through => :ratings
end

class Item < ActiveRecord::Base
  has_many :ratings
  has_many :rated_by_users, :through => :ratings, :source => :user
end

然后,假设您在数据库中有以下记录:

$ sqlite3 db/development.sqlite3 'SELECT * FROM items';
1|2013-03-22 03:21:31.264545|2013-03-22 03:21:31.264545
2|2013-03-22 03:24:01.703418|2013-03-22 03:24:01.703418

$ sqlite3 db/development.sqlite3 'SELECT * FROM users';
1|2013-03-22 03:21:28.029502|2013-03-22 03:21:28.029502

$ sqlite3 db/development.sqlite3 'SELECT * FROM ratings';
1|1|1|2013-03-22 03:22:01.730235|2013-03-22 03:22:01.730235

您可以使用以下声明请求所有项目及其关联的评级和用户实例:

items = Item.includes(:rated_by_users)

这将为您执行3个SQL查询:

Item Load (0.1ms)  SELECT "items".* FROM "items" 
Rating Load (0.2ms)  SELECT "ratings".* FROM "ratings" WHERE "ratings"."item_id" IN (1, 2)
User Load (0.2ms)  SELECT "users".* FROM "users" WHERE "users"."id" IN (1)

尝试访问对每个项目进行评级的用户可以通过调用每个项目上的#rated_by_users关联方法来完成:

> items.map {|item| item.rated_by_users }
=> [[#<User id: 1, created_at: "2013-03-22 03:21:28", updated_at: "2013-03-22 03:21:28">], []] 

答案 2 :(得分:0)

正如我最近在this blog post中所写的那样,对于您的情况,我建议以下几点:

items = Item.all
ActiveRecord::Associations::Preloader.new.preload(items, :ratings, Rating.where(user_id: user_id))

您可以使用预加载器的自定义范围,并访问用户已经确定范围的items.each { |i| i.ratings }