我有三个表:users
,members
,projects
。中间是一个连接表,表示其他两个表之间的has-many-through;它有一些感兴趣的属性,包括join_code
和activated
。
更广泛:
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)中执行此操作?
答案 0 :(得分:2)
我可能在这里有一些答案,即我写的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 %>
在视图中获取成员记录的表达式非常难看,但select
是Array
方法,而不是查询,除了上面显示的两个以外,没有额外的数据库命中出现在Rails服务器的输出。因此我想我的n + 1问题已经解决了。
答案 1 :(得分:1)
您似乎希望最多只有一个活动成员将每个用户链接到一个项目。如果是这种情况,则以下情况应该起作用:
在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
答案 2 :(得分:0)
在live_members
类上添加名为Project
的关联。
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
在live_projects
类上添加名为User
的关联。
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