【问题标题】:has_many/belongs_to relationships & query questionshas_many/belongs_to 关系和查询问题
【发布时间】:2015-10-15 20:31:51
【问题描述】:

我现在正在构建一个考勤跟踪器。我目前有三个模型。我正在为我的用户模型使用设计

class User < ActiveRecord::Base
  has_many :clients
  has_many :attendances

class Client < ActiveRecord::Base
  belongs_to :user
  has_many :attendances

class Attendance < ActiveRecord::Base
  belongs_to :user
  belongs_to :client

考勤表上的列是user_idclient_idcreated_atupdated_at

这是我的思考过程:
我可以获得每个User的所有出席人数
我还可以获得特定Client的所有出席人数
我可以通过以下方式获取特定UserClient 的所有出勤记录:Attendance.joins(:user, :client).where(:user_id =&gt; current_user) 返回一个

&lt;ActiveRecord::Relation [#&lt;Attendance id: 18, client_id: 151, created_at: "2015-07-24 21:36:16", updated_at: "2015-07-24 21:36:16", user_id: 4&gt;, #&lt;Attendance id: 19, client_id: 101, created_at: "2015-07-24 21:37:10", updated_at: "2015-07-24 21:37:10", user_id: 4&gt;, #&lt;Attendance id: 20, client_id: 114, created_at: "2015-07-24 21:37:39", updated_at: "2015-07-24 21:37:39", user_id: 4&gt;, #&lt;Attendance id: 21, client_id: 123, created_at: "2015-07-24 21:38:26", updated_at: "2015-07-24 21:38:26", user_id: 4&gt;]&gt;

我能否以某种方式返回Client 表以获取first_nameemail 之类的信息以及另一个whereincludejoins 语句?

或者我完全错过了什么,可能需要一个连接表并建立has_many, through: 关系?

【问题讨论】:

  • 您已经可以访问所有这些内容了。 attendences.each {|a| p a.client.first_name } 还是我误解了您要完成的工作?
  • Attendance.joins(:user, :client).where(:user_id =&gt; current_user).select('clients.*')

标签: ruby-on-rails activerecord devise sqlite


【解决方案1】:

Brad 上面的评论在技术上是正确的,但由于您查询中的 .joins 子句仅包含用于执行实际查询的那些表,因此来自 Users 和 Clients 表的数据实际上不会被加载Rails,因此为了获取所有数据,您最终将执行 N+1 个查询(Rails 应用程序运行缓慢的常见原因!)。那就是:

irb> User.joins(:groups).each { |u| u.groups.map(&:name) }
  User Load (8.6ms)  SELECT "users".* FROM "users" INNER JOIN "groups" ON "groups"."user_id" = "users"."id"
  Group Load (1.2ms)  SELECT "groups".* FROM "groups" WHERE "groups"."user_id" = $1  [["user_id", 1]]
  Group Load (0.6ms)  SELECT "groups".* FROM "groups" WHERE "groups"."user_id" = $1  [["user_id", 1]]
  Group Load (0.7ms)  SELECT "groups".* FROM "groups" WHERE "groups"."user_id" = $1  [["user_id", 1]]
  Group Load (0.7ms)  SELECT "groups".* FROM "groups" WHERE "groups"."user_id" = $1  [["user_id", 2]]
  Group Load (0.6ms)  SELECT "groups".* FROM "groups" WHERE "groups"."user_id" = $1  [["user_id", 3]]

现在还不错,但想象一下,如果您有一千个用户!不过,我们可以解决这个问题。如果您使用.includes 方法,您将连接到其他表并将它们的数据加载到内存中。它仍然运行 2 个查询,但这是一个改进:

irb(main):016:0> User.includes(:groups).each { |u| u.groups.map(&:name) }
  User Load (0.6ms)  SELECT "users".* FROM "users"
  Group Load (1.2ms)  SELECT "groups".* FROM "groups" WHERE "groups"."user_id" IN (1, 2, 3, 4)

所以,对于你的情况,试试这个:

Attendance.includes(:user, :client).where(user_id: current_user)

有关includesjoins 之间区别的更多信息,请参阅this blog post,或谷歌“rails joins vs includes”。

【讨论】:

  • 谢谢罗伯特。这为我解决了这个问题,但随后又引发了另一个问题。我可以获得特定用户或客户和出勤所需的所有记录。有没有一种有效的方法来将这些包含/加入字符串化,然后使用外部 client_id 来获取所述特定客户的名字或地址?
  • 假设这些只是Client 记录上的属性,这将是相当有效的(只涉及我上面提到的两个查询):@client_first_names = Attendance.includes(:user, :client).where(user_id: current_user).map { |attendance| attendance.client.first_name }。您可以将其拆分为多行以使其更易于阅读!
  • 另外,如果你出于某种原因想要这些名字,你可以这样做:Attendance.joins(:user, :client).where(user_id: current_user).pluck('clients.first_name')。请参阅here 了解我在那里所做的更多信息。
  • 哇。这在这两种情况下都绝对有效,谢谢。我没有考虑将.map.includes 方法一起使用。这让我发疯了!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多