高级 Rails 活动记录查询
Advanced Rails Active Record Query
呼叫Active Record查询专家!我在调试 N+1 查询时遇到问题。这是我的模型和导致问题的页面的摘要。 我很想找到一种为此页面创建查询的好方法,并找出缓存此页面的最佳方法。非常感谢任何帮助!!
class Brief < ApplicationRecord
belongs_to :account
has_one :user, through: :account
has_many :submissions
class Account < ApplicationRecord
belongs_to :user, touch: true
has_many :briefs
end
class Submission < ApplicationRecord
belongs_to :brief, touch: true
belongs_to :user
我有一个@briefs 页面,我希望在其中显示每个简报:
- 简短的账户名 -> brief.account.name
- current_user -> brief.submissions.where(user: current_user).status 的 Brief 提交状态 ** 请注意,如果用户尚未创建,这可能 return nil提交简报
class BriefsController < BaseController
def index
@briefs = Brief.includes([:account, :submissions]).order(expiration_date: :desc).live
end
class Brief < ApplicationRecord
### CLASS METHODS
def self.live
Brief.all.select { |brief| brief.active? == true }
end
目前这是我缓存页面的方式:
live_briefs.html.erb
<% cache @briefs, expires_in: 1.hour do %>
<% @briefs.each do |brief| %>
<% cache brief do %>
<%= render 'live_briefs/components/brief', brief: brief %>
<% end %>
<% end %>
<% end %>
_.brief.html.erb
<%= brief.account.name %>
<%= live_brief_submission_status(brief, current_user) %>
brief_helper.rb(这是导致添加查询的原因)
def live_brief_submission_status(brief, user)
submission = brief.submissions.where(user: user).first
if submission.present?
case submission.status
when "approved"
tag.div "Approved", class: "inline-flex items-center px-3 py-0.5 rounded-full text-sm font-medium leading-5 bg-indigo-100 text-indigo-800"
when "invited"
tag.div "Invited", class: "inline-flex items-center px-3 py-0.5 rounded-full text-sm font-medium leading-5 bg-green-400 text-white"
when "pending"
tag.div "Pending", class: "inline-flex items-center px-3 py-0.5 rounded-full text-sm font-medium leading-5 bg-gray-100 text-gray-800"
when "declined"
tag.div "Declined", class: "inline-flex items-center px-3 py-0.5 rounded-full text-sm font-medium leading-5 bg-red-100 text-red-800"
else
end
else
tag.div "Apply Now!", class: "inline-flex items-center pl-3 py-0.5 rounded-full text-sm font-medium leading-5 text-gray-800"
end
end
这是我加载页面时的输出:
Started GET "/briefs/live" for ::1 at 2020-06-01 15:34:11 -0700
Processing by LiveBriefsController#index as HTML
User Load (5.9ms) SELECT "users".* FROM "users" WHERE "users"."id" = ORDER BY "users"."id" ASC LIMIT [["id", 1], ["LIMIT", 1]]
Brief Load (10.8ms) SELECT "briefs".* FROM "briefs" ORDER BY "briefs"."expiration_date" DESC
↳ app/models/brief.rb:32:in `live'
Account Load (7.5ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."id" IN (, , , , , , , , , ) [["id", 10], ["id", 4], ["id", 9], ["id", 3], ["id", 6], ["id", 5], ["id", 7], ["id", 2], ["id", 1], ["id", 8]]
↳ app/models/brief.rb:32:in `live'
Submission Load (46.7ms) SELECT "submissions".* FROM "submissions" WHERE "submissions"."brief_id" IN (, , , , , , , , , , , , , , , , , , , , , , , , , , , , , ) [["brief_id", 27], ["brief_id", 9], ["brief_id", 21], ["brief_id", 22], ["brief_id", 10], ["brief_id", 29], ["brief_id", 24], ["brief_id", 3], ["brief_id", 1], ["brief_id", 6], ["brief_id", 25], ["brief_id", 7], ["brief_id", 2], ["brief_id", 23], ["brief_id", 5], ["brief_id", 28], ["brief_id", 26], ["brief_id", 30], ["brief_id", 8], ["brief_id", 4], ["brief_id", 14], ["brief_id", 17], ["brief_id", 16], ["brief_id", 11], ["brief_id", 13], ["brief_id", 20], ["brief_id", 12], ["brief_id", 15], ["brief_id", 19], ["brief_id", 18]]
↳ app/models/brief.rb:32:in `live'
Rendering layouts/application.html.erb
Rendering live_briefs/index.html.erb within layouts/application
Rendered live_briefs/components/_title.html.erb (Duration: 0.1ms | Allocations: 55)
Submission Load (1.2ms) SELECT "submissions".* FROM "submissions" WHERE "submissions"."brief_id" = AND "submissions"."user_id" = ORDER BY "submissions"."id" ASC LIMIT [["brief_id", 9], ["user_id", 1], ["LIMIT", 1]]
↳ app/helpers/brief_helper.rb:20:in `live_brief_submission_status'
Rendered live_briefs/components/_brief.html.erb (Duration: 32.0ms | Allocations: 3088)
Submission Load (3.8ms) SELECT "submissions".* FROM "submissions" WHERE "submissions"."brief_id" = AND "submissions"."user_id" = ORDER BY "submissions"."id" ASC LIMIT [["brief_id", 10], ["user_id", 1], ["LIMIT", 1]]
↳ app/helpers/brief_helper.rb:20:in `live_brief_submission_status'
Rendered live_briefs/components/_brief.html.erb (Duration: 11.7ms | Allocations: 2119)
Submission Load (27.0ms) SELECT "submissions".* FROM "submissions" WHERE "submissions"."brief_id" = AND "submissions"."user_id" = ORDER BY "submissions"."id" ASC LIMIT [["brief_id", 3], ["user_id", 1], ["LIMIT", 1]]
↳ app/helpers/brief_helper.rb:20:in `live_brief_submission_status'
Rendered live_briefs/components/_brief.html.erb (Duration: 49.2ms | Allocations: 2028)
Submission Load (26.0ms) SELECT "submissions".* FROM "submissions" WHERE "submissions"."brief_id" = AND "submissions"."user_id" = ORDER BY "submissions"."id" ASC LIMIT [["brief_id", 1], ["user_id", 1], ["LIMIT", 1]]
↳ app/helpers/brief_helper.rb:20:in `live_brief_submission_status'
Rendered live_briefs/components/_brief.html.erb (Duration: 34.2ms | Allocations: 2034)
Submission Load (0.7ms) SELECT "submissions".* FROM "submissions" WHERE "submissions"."brief_id" = AND "submissions"."user_id" = ORDER BY "submissions"."id" ASC LIMIT [["brief_id", 6], ["user_id", 1], ["LIMIT", 1]]
↳ app/helpers/brief_helper.rb:20:in `live_brief_submission_status'
Rendered live_briefs/components/_brief.html.erb (Duration: 8.2ms | Allocations: 2028)
Submission Load (0.9ms) SELECT "submissions".* FROM "submissions" WHERE "submissions"."brief_id" = AND "submissions"."user_id" = ORDER BY "submissions"."id" ASC LIMIT [["brief_id", 7], ["user_id", 1], ["LIMIT", 1]]
↳ app/helpers/brief_helper.rb:20:in `live_brief_submission_status'
Rendered live_briefs/components/_brief.html.erb (Duration: 12.0ms | Allocations: 2028)
Submission Load (12.5ms) SELECT "submissions".* FROM "submissions" WHERE "submissions"."brief_id" = AND "submissions"."user_id" = ORDER BY "submissions"."id" ASC LIMIT [["brief_id", 2], ["user_id", 1], ["LIMIT", 1]]
↳ app/helpers/brief_helper.rb:20:in `live_brief_submission_status'
Rendered live_briefs/components/_brief.html.erb (Duration: 18.2ms | Allocations: 2027)
Submission Load (136.9ms) SELECT "submissions".* FROM "submissions" WHERE "submissions"."brief_id" = AND "submissions"."user_id" = ORDER BY "submissions"."id" ASC LIMIT [["brief_id", 5], ["user_id", 1], ["LIMIT", 1]]
↳ app/helpers/brief_helper.rb:20:in `live_brief_submission_status'
Rendered live_briefs/components/_brief.html.erb (Duration: 157.4ms | Allocations: 2405)
Submission Load (168.4ms) SELECT "submissions".* FROM "submissions" WHERE "submissions"."brief_id" = AND "submissions"."user_id" = ORDER BY "submissions"."id" ASC LIMIT [["brief_id", 8], ["user_id", 1], ["LIMIT", 1]]
↳ app/helpers/brief_helper.rb:20:in `live_brief_submission_status'
Rendered live_briefs/components/_brief.html.erb (Duration: 177.6ms | Allocations: 2035)
Submission Load (31.7ms) SELECT "submissions".* FROM "submissions" WHERE "submissions"."brief_id" = AND "submissions"."user_id" = ORDER BY "submissions"."id" ASC LIMIT [["brief_id", 4], ["user_id", 1], ["LIMIT", 1]]
↳ app/helpers/brief_helper.rb:20:in `live_brief_submission_status'
Rendered live_briefs/components/_brief.html.erb (Duration: 39.5ms | Allocations: 2029)
Rendered live_briefs/index.html.erb within layouts/application (Duration: 581.8ms | Allocations: 23083)
[Webpacker] Everything's up-to-date. Nothing to do
Rendered layouts/_sidebar.html.erb (Duration: 9.3ms | Allocations: 1433)
Rendered layouts/_notifications.html.erb (Duration: 1.8ms | Allocations: 86)
Rendered application/_css_overrides.html.erb (Duration: 0.2ms | Allocations: 56)
Rendered layouts/application.html.erb (Duration: 631.4ms | Allocations: 28281)
Completed 200 OK in 994ms (Views: 227.1ms | ActiveRecord: 484.8ms | Allocations: 93326)
首先通过在自身上调用它来确保该方法是可链接的,以便您可以将它添加到现有关系中:
class Brief < ApplicationRecord
def self.live
where(active: true)
end
end
然后确保在控制器中预先加载所需的数据:
@briefs = Brief.live
.eager_load(submissions: :user)
.order(expiration_date: :desc)
不要使用始终创建查询的 .where
,而是使用可枚举的方法来遍历加载到内存中的关联:
def live_brief_submission_status(brief, user)
submission = brief.submissions.detect {|s| s.user = user }
if submission.present?
case submission.status
when "approved"
tag.div "Approved", class: "inline-flex items-center px-3 py-0.5 rounded-full text-sm font-medium leading-5 bg-indigo-100 text-indigo-800"
when "invited"
tag.div "Invited", class: "inline-flex items-center px-3 py-0.5 rounded-full text-sm font-medium leading-5 bg-green-400 text-white"
when "pending"
tag.div "Pending", class: "inline-flex items-center px-3 py-0.5 rounded-full text-sm font-medium leading-5 bg-gray-100 text-gray-800"
when "declined"
tag.div "Declined", class: "inline-flex items-center px-3 py-0.5 rounded-full text-sm font-medium leading-5 bg-red-100 text-red-800"
end
else
tag.div "Apply Now!", class: "inline-flex items-center pl-3 py-0.5 rounded-full text-sm font-medium leading-5 text-gray-800"
end
end
但实际上应该从辅助方法中重构该查询,以降低其循环复杂度并重新分配职责。
呼叫Active Record查询专家!我在调试 N+1 查询时遇到问题。这是我的模型和导致问题的页面的摘要。 我很想找到一种为此页面创建查询的好方法,并找出缓存此页面的最佳方法。非常感谢任何帮助!!
class Brief < ApplicationRecord
belongs_to :account
has_one :user, through: :account
has_many :submissions
class Account < ApplicationRecord
belongs_to :user, touch: true
has_many :briefs
end
class Submission < ApplicationRecord
belongs_to :brief, touch: true
belongs_to :user
我有一个@briefs 页面,我希望在其中显示每个简报:
- 简短的账户名 -> brief.account.name
- current_user -> brief.submissions.where(user: current_user).status 的 Brief 提交状态 ** 请注意,如果用户尚未创建,这可能 return nil提交简报
class BriefsController < BaseController
def index
@briefs = Brief.includes([:account, :submissions]).order(expiration_date: :desc).live
end
class Brief < ApplicationRecord
### CLASS METHODS
def self.live
Brief.all.select { |brief| brief.active? == true }
end
目前这是我缓存页面的方式:
live_briefs.html.erb
<% cache @briefs, expires_in: 1.hour do %>
<% @briefs.each do |brief| %>
<% cache brief do %>
<%= render 'live_briefs/components/brief', brief: brief %>
<% end %>
<% end %>
<% end %>
_.brief.html.erb
<%= brief.account.name %>
<%= live_brief_submission_status(brief, current_user) %>
brief_helper.rb(这是导致添加查询的原因)
def live_brief_submission_status(brief, user)
submission = brief.submissions.where(user: user).first
if submission.present?
case submission.status
when "approved"
tag.div "Approved", class: "inline-flex items-center px-3 py-0.5 rounded-full text-sm font-medium leading-5 bg-indigo-100 text-indigo-800"
when "invited"
tag.div "Invited", class: "inline-flex items-center px-3 py-0.5 rounded-full text-sm font-medium leading-5 bg-green-400 text-white"
when "pending"
tag.div "Pending", class: "inline-flex items-center px-3 py-0.5 rounded-full text-sm font-medium leading-5 bg-gray-100 text-gray-800"
when "declined"
tag.div "Declined", class: "inline-flex items-center px-3 py-0.5 rounded-full text-sm font-medium leading-5 bg-red-100 text-red-800"
else
end
else
tag.div "Apply Now!", class: "inline-flex items-center pl-3 py-0.5 rounded-full text-sm font-medium leading-5 text-gray-800"
end
end
这是我加载页面时的输出:
Started GET "/briefs/live" for ::1 at 2020-06-01 15:34:11 -0700
Processing by LiveBriefsController#index as HTML
User Load (5.9ms) SELECT "users".* FROM "users" WHERE "users"."id" = ORDER BY "users"."id" ASC LIMIT [["id", 1], ["LIMIT", 1]]
Brief Load (10.8ms) SELECT "briefs".* FROM "briefs" ORDER BY "briefs"."expiration_date" DESC
↳ app/models/brief.rb:32:in `live'
Account Load (7.5ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."id" IN (, , , , , , , , , ) [["id", 10], ["id", 4], ["id", 9], ["id", 3], ["id", 6], ["id", 5], ["id", 7], ["id", 2], ["id", 1], ["id", 8]]
↳ app/models/brief.rb:32:in `live'
Submission Load (46.7ms) SELECT "submissions".* FROM "submissions" WHERE "submissions"."brief_id" IN (, , , , , , , , , , , , , , , , , , , , , , , , , , , , , ) [["brief_id", 27], ["brief_id", 9], ["brief_id", 21], ["brief_id", 22], ["brief_id", 10], ["brief_id", 29], ["brief_id", 24], ["brief_id", 3], ["brief_id", 1], ["brief_id", 6], ["brief_id", 25], ["brief_id", 7], ["brief_id", 2], ["brief_id", 23], ["brief_id", 5], ["brief_id", 28], ["brief_id", 26], ["brief_id", 30], ["brief_id", 8], ["brief_id", 4], ["brief_id", 14], ["brief_id", 17], ["brief_id", 16], ["brief_id", 11], ["brief_id", 13], ["brief_id", 20], ["brief_id", 12], ["brief_id", 15], ["brief_id", 19], ["brief_id", 18]]
↳ app/models/brief.rb:32:in `live'
Rendering layouts/application.html.erb
Rendering live_briefs/index.html.erb within layouts/application
Rendered live_briefs/components/_title.html.erb (Duration: 0.1ms | Allocations: 55)
Submission Load (1.2ms) SELECT "submissions".* FROM "submissions" WHERE "submissions"."brief_id" = AND "submissions"."user_id" = ORDER BY "submissions"."id" ASC LIMIT [["brief_id", 9], ["user_id", 1], ["LIMIT", 1]]
↳ app/helpers/brief_helper.rb:20:in `live_brief_submission_status'
Rendered live_briefs/components/_brief.html.erb (Duration: 32.0ms | Allocations: 3088)
Submission Load (3.8ms) SELECT "submissions".* FROM "submissions" WHERE "submissions"."brief_id" = AND "submissions"."user_id" = ORDER BY "submissions"."id" ASC LIMIT [["brief_id", 10], ["user_id", 1], ["LIMIT", 1]]
↳ app/helpers/brief_helper.rb:20:in `live_brief_submission_status'
Rendered live_briefs/components/_brief.html.erb (Duration: 11.7ms | Allocations: 2119)
Submission Load (27.0ms) SELECT "submissions".* FROM "submissions" WHERE "submissions"."brief_id" = AND "submissions"."user_id" = ORDER BY "submissions"."id" ASC LIMIT [["brief_id", 3], ["user_id", 1], ["LIMIT", 1]]
↳ app/helpers/brief_helper.rb:20:in `live_brief_submission_status'
Rendered live_briefs/components/_brief.html.erb (Duration: 49.2ms | Allocations: 2028)
Submission Load (26.0ms) SELECT "submissions".* FROM "submissions" WHERE "submissions"."brief_id" = AND "submissions"."user_id" = ORDER BY "submissions"."id" ASC LIMIT [["brief_id", 1], ["user_id", 1], ["LIMIT", 1]]
↳ app/helpers/brief_helper.rb:20:in `live_brief_submission_status'
Rendered live_briefs/components/_brief.html.erb (Duration: 34.2ms | Allocations: 2034)
Submission Load (0.7ms) SELECT "submissions".* FROM "submissions" WHERE "submissions"."brief_id" = AND "submissions"."user_id" = ORDER BY "submissions"."id" ASC LIMIT [["brief_id", 6], ["user_id", 1], ["LIMIT", 1]]
↳ app/helpers/brief_helper.rb:20:in `live_brief_submission_status'
Rendered live_briefs/components/_brief.html.erb (Duration: 8.2ms | Allocations: 2028)
Submission Load (0.9ms) SELECT "submissions".* FROM "submissions" WHERE "submissions"."brief_id" = AND "submissions"."user_id" = ORDER BY "submissions"."id" ASC LIMIT [["brief_id", 7], ["user_id", 1], ["LIMIT", 1]]
↳ app/helpers/brief_helper.rb:20:in `live_brief_submission_status'
Rendered live_briefs/components/_brief.html.erb (Duration: 12.0ms | Allocations: 2028)
Submission Load (12.5ms) SELECT "submissions".* FROM "submissions" WHERE "submissions"."brief_id" = AND "submissions"."user_id" = ORDER BY "submissions"."id" ASC LIMIT [["brief_id", 2], ["user_id", 1], ["LIMIT", 1]]
↳ app/helpers/brief_helper.rb:20:in `live_brief_submission_status'
Rendered live_briefs/components/_brief.html.erb (Duration: 18.2ms | Allocations: 2027)
Submission Load (136.9ms) SELECT "submissions".* FROM "submissions" WHERE "submissions"."brief_id" = AND "submissions"."user_id" = ORDER BY "submissions"."id" ASC LIMIT [["brief_id", 5], ["user_id", 1], ["LIMIT", 1]]
↳ app/helpers/brief_helper.rb:20:in `live_brief_submission_status'
Rendered live_briefs/components/_brief.html.erb (Duration: 157.4ms | Allocations: 2405)
Submission Load (168.4ms) SELECT "submissions".* FROM "submissions" WHERE "submissions"."brief_id" = AND "submissions"."user_id" = ORDER BY "submissions"."id" ASC LIMIT [["brief_id", 8], ["user_id", 1], ["LIMIT", 1]]
↳ app/helpers/brief_helper.rb:20:in `live_brief_submission_status'
Rendered live_briefs/components/_brief.html.erb (Duration: 177.6ms | Allocations: 2035)
Submission Load (31.7ms) SELECT "submissions".* FROM "submissions" WHERE "submissions"."brief_id" = AND "submissions"."user_id" = ORDER BY "submissions"."id" ASC LIMIT [["brief_id", 4], ["user_id", 1], ["LIMIT", 1]]
↳ app/helpers/brief_helper.rb:20:in `live_brief_submission_status'
Rendered live_briefs/components/_brief.html.erb (Duration: 39.5ms | Allocations: 2029)
Rendered live_briefs/index.html.erb within layouts/application (Duration: 581.8ms | Allocations: 23083)
[Webpacker] Everything's up-to-date. Nothing to do
Rendered layouts/_sidebar.html.erb (Duration: 9.3ms | Allocations: 1433)
Rendered layouts/_notifications.html.erb (Duration: 1.8ms | Allocations: 86)
Rendered application/_css_overrides.html.erb (Duration: 0.2ms | Allocations: 56)
Rendered layouts/application.html.erb (Duration: 631.4ms | Allocations: 28281)
Completed 200 OK in 994ms (Views: 227.1ms | ActiveRecord: 484.8ms | Allocations: 93326)
首先通过在自身上调用它来确保该方法是可链接的,以便您可以将它添加到现有关系中:
class Brief < ApplicationRecord
def self.live
where(active: true)
end
end
然后确保在控制器中预先加载所需的数据:
@briefs = Brief.live
.eager_load(submissions: :user)
.order(expiration_date: :desc)
不要使用始终创建查询的 .where
,而是使用可枚举的方法来遍历加载到内存中的关联:
def live_brief_submission_status(brief, user)
submission = brief.submissions.detect {|s| s.user = user }
if submission.present?
case submission.status
when "approved"
tag.div "Approved", class: "inline-flex items-center px-3 py-0.5 rounded-full text-sm font-medium leading-5 bg-indigo-100 text-indigo-800"
when "invited"
tag.div "Invited", class: "inline-flex items-center px-3 py-0.5 rounded-full text-sm font-medium leading-5 bg-green-400 text-white"
when "pending"
tag.div "Pending", class: "inline-flex items-center px-3 py-0.5 rounded-full text-sm font-medium leading-5 bg-gray-100 text-gray-800"
when "declined"
tag.div "Declined", class: "inline-flex items-center px-3 py-0.5 rounded-full text-sm font-medium leading-5 bg-red-100 text-red-800"
end
else
tag.div "Apply Now!", class: "inline-flex items-center pl-3 py-0.5 rounded-full text-sm font-medium leading-5 text-gray-800"
end
end
但实际上应该从辅助方法中重构该查询,以降低其循环复杂度并重新分配职责。