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

我有以下型号:

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查询或将所有实体加载到内存中的情况下,在参数上过滤所有post和预先加载的关联?

步骤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) 

看起来你基本上建模了一个has_many :through关系:Item has_and_belongs_to_many User,Rating是连接模型。 您可以:through “活动记录关联的Rails指南”中的关系来了解: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 

然后,假设您在DB中有以下记录:

 $ 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 } => [[#], []]