6

我有以下型号:

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 与一个参数的关联,然后使用 include 方法传递该参数。但这似乎不存在。

如何在不执行 N+1 查询或将所有实体加载到内存中的情况下获取所有帖子和预先加载的关联过滤参数?

4

3 回答 3

4

第1步

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

第2步

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

导轨 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)
于 2013-04-23T18:50:11.403 回答
1

正如我最近在这篇博文中所写的那样,我建议您采取以下措施:

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

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

于 2019-10-31T14:59:27.120 回答
0

看起来您基本上是在为has_many :through关系建模:Item has_and_belongs_to_many User,Rating 是连接模型。您可以阅读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

您可以使用以下语句请求所有项目,以及它们相关的 Rating 和 User 实例:

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">], []] 
于 2013-03-21T09:06:54.847 回答