使用“has Many through”进行急切加载——我需要阿雷尔吗?

发布于 2024-12-05 12:28:46 字数 1652 浏览 1 评论 0原文

我有三个表:usersmembersprojects。中间是一个连接表,表达了其他两个表之间的多通关系;它有一些有趣的属性,包括 join_codeactivated

更广泛地说:

class User < ActiveRecord::Base
  has_many :members
  has_many :projects, :through => :members
end

class Member < ActiveRecord::Base
  belongs_to :user
  belongs_to :project
  # has a column called join_code
  # has a column called activated
  # Note that this class can be thought of as "membership"
end

class Project < ActiveRecord::Base
  has_many :members
  has_many :users, :through => :members
end

目标:给定一个特定用户,我想要一个查询来获取所有项目,并立即加载将这些项目链接到该用户的成员记录。

到目前为止,我在 user.rb 中有一个执行查询的方法:

def live_projects
  self.projects.order("projects.name").includes(:members).where(:members => {:join_code => nil, :activated => true})
end

但这还不够。然后我希望能够在视图代码中执行此操作:

<% current_user.live_projects.each do |project| %>
  <li project_id="<%= project.id %>">
    <% member = project.member %>
      (Do something with that member record here)
      <%= project.name %>
    <% end %>
  </li>
<% end %>

在这里,通常我有 project.members,但在我的上下文中,我只对那个 感兴趣 链接回用户的成员记录。

我认为原始 SQL 应该是这样的

select projects.*, members.* 
from projects inner join members on projects.id = members.project_id
where members.user_id = X and members.join_code is null and members.activated = 't';

如何在 Arel (或 ActiveRecord)中做到这一点?

I have three tables: users, members, projects. The middle is a join table expressing a has-many-through between the other two tables; and it has some attributes of interest, including join_code and activated.

More expansively:

class User < ActiveRecord::Base
  has_many :members
  has_many :projects, :through => :members
end

class Member < ActiveRecord::Base
  belongs_to :user
  belongs_to :project
  # has a column called join_code
  # has a column called activated
  # Note that this class can be thought of as "membership"
end

class Project < ActiveRecord::Base
  has_many :members
  has_many :users, :through => :members
end

Goal: Given a particular user, I want a query that will get all the projects, and eager load only the member records that link those projects to the user.

So far I have this method in user.rb that does a query:

def live_projects
  self.projects.order("projects.name").includes(:members).where(:members => {:join_code => nil, :activated => true})
end

But it's not enough. I'd like to then be able to do this in the view code:

<% current_user.live_projects.each do |project| %>
  <li project_id="<%= project.id %>">
    <% member = project.member %>
      (Do something with that member record here)
      <%= project.name %>
    <% end %>
  </li>
<% end %>

Here, normally, I'd have project.members, but in my context I'm only interested in that one member record that links back to the user.

Here is what I think the raw SQL should look like

select projects.*, members.* 
from projects inner join members on projects.id = members.project_id
where members.user_id = X and members.join_code is null and members.activated = 't';

How to do that in Arel (or ActiveRecord)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

逆流 2024-12-12 12:28:46

我在这里可能有一些答案,即我编写的​​ ActiveRecord 代码看起来相当合理。再次,这是该查询:

def live_projects
  self.projects.order("projects.name").includes(:members).where(:members => {:join_code => nil, :activated => true})
end

在使用示例数据运行 UI 时,它会从 Rails 服务器生成此输出:

Project Load (0.6ms)  SELECT "projects".* FROM "projects" INNER JOIN "members" ON "projects".id = "members".project_id WHERE "members"."join_code" IS NULL AND "members"."activated" = 't' AND (("members".user_id = 3)) ORDER BY projects.name
Member Load (2.0ms)  SELECT "members".* FROM "members" WHERE ("members".project_id IN (50,3,6,37,5,1))

然后在视图代码中我可以执行以下操作:

<% current_user.live_projects.each do |project| %>
  <li project_id="<%= project.id %>" class="<%= 'active' if project == @project %>">
    <% member = project.members.detect { |member| member.user_id == current_user.id } %>
    (Do something with that member record here)
  </li>
<% end %>

用于获取成员记录的表达式在视图中非常难看,但是 < code>select 是一个 Array 方法,而不是查询,除了上面显示的两个之外,Rails 服务器的输出中不会出现任何额外的数据库命中。因此我想我的 n+1 问题已经解决了。

I may have something of an answer here, namely that the ActiveRecord code I wrote seems pretty reasonable. Again, here's that query:

def live_projects
  self.projects.order("projects.name").includes(:members).where(:members => {:join_code => nil, :activated => true})
end

On a run through the UI with sample data it generates this output from Rails server:

Project Load (0.6ms)  SELECT "projects".* FROM "projects" INNER JOIN "members" ON "projects".id = "members".project_id WHERE "members"."join_code" IS NULL AND "members"."activated" = 't' AND (("members".user_id = 3)) ORDER BY projects.name
Member Load (2.0ms)  SELECT "members".* FROM "members" WHERE ("members".project_id IN (50,3,6,37,5,1))

Then later in the view code I can do this:

<% current_user.live_projects.each do |project| %>
  <li project_id="<%= project.id %>" class="<%= 'active' if project == @project %>">
    <% member = project.members.detect { |member| member.user_id == current_user.id } %>
    (Do something with that member record here)
  </li>
<% end %>

That expression to get the member record is pretty ugly in the view, but select is an Array method, not a query, and no extra DB hits other than the two shown above appear in the output from Rails server. Thus I guess my n+1 problem is solved.

卷耳 2024-12-12 12:28:46

Project 类上添加一个名为 live_members 的关联。

class Project < ActiveRecord::Base
  has_many :live_members, :class_name => "Member", 
                :conditions => {:join_code => nil, :activated => true}
  has_many :members
  has_many :users, :through => :members
end

User 类上添加一个名为 live_projects 的关联。

class User < ActiveRecord::Base
  has_many :members
  has_many :projects, :through => :members
  has_many :live_projects, :through => :members, :source => :project, 
             :include => :live_member, :order => "projects.name"
end

现在您可以:

user.live_projects

Add an association called live_members on the Project class.

class Project < ActiveRecord::Base
  has_many :live_members, :class_name => "Member", 
                :conditions => {:join_code => nil, :activated => true}
  has_many :members
  has_many :users, :through => :members
end

Add an association called live_projects on the User class.

class User < ActiveRecord::Base
  has_many :members
  has_many :projects, :through => :members
  has_many :live_projects, :through => :members, :source => :project, 
             :include => :live_member, :order => "projects.name"
end

Now you can:

user.live_projects
猥︴琐丶欲为 2024-12-12 12:28:46

您似乎希望最多有一个活跃成员将每个用户链接到一个项目。如果是这种情况,以下操作应该有效:

member.rb 中:

scope :live, where(:join_code => nil, :activated => true)

user.rb 中:

def live_projects_with_members
  members.live.includes(:project).group_by(&:project)
end

在您的视图中:

<% current_user.live_projects_with_members.each do |project, members| %>
  <% member = members.first %>
  <li project_id="<%= project.id %>" class="<%= 'active' if project == @project %>">
      (Do something with that member record here)
  </li>
<% end %>

如果您想添加额外的连接供您使用统计你可以这样做:

def live_projects_with_members
  members.live.includes(:project, :stats).group_by(&:project)
end

It seems that you expect there to be at most one active member linking each user to a project. If this is the case the following should work:

In member.rb:

scope :live, where(:join_code => nil, :activated => true)

In user.rb:

def live_projects_with_members
  members.live.includes(:project).group_by(&:project)
end

In your view:

<% current_user.live_projects_with_members.each do |project, members| %>
  <% member = members.first %>
  <li project_id="<%= project.id %>" class="<%= 'active' if project == @project %>">
      (Do something with that member record here)
  </li>
<% end %>

If you then want to add an extra join for your usage stats you can do this:

def live_projects_with_members
  members.live.includes(:project, :stats).group_by(&:project)
end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文