如何避免 N+1 查询并在活动管理表单上执行预加载
How to avoid N+1 Query and Perform Eager Loading on Active Admin Form
我遇到了 N+1 query problem in an Active Admin (Formtastic) 表格。加载对应于 belongs_to
关联的 select 输入时会发生查询。关联模型上的 display_name
引用了另一个 belongs_to
关联。以下是模型关系:
:user
|-- belongs_to :alum
|-- belongs_to :graduation_class
|-- year
以下是模型的相关部分:
app/models/user.rb
class User < ApplicationRecord
...
belongs_to :alumn, optional: true, touch: true
...
end
app/models/alumn.rb
class Alumn < ApplicationRecord
belongs_to :graduation_class, touch: true
delegate :year, to: :graduation_class
has_many :users
...
def display_name
"#{year}: #{last_name}, #{first_name} #{middle_name}"
end
...
end
这是相关的活动管理员class:
app/admin/user.rb
ActiveAdmin.register User do
...
includes :alumn, alumn: :graduation_class
...
form do |f|
f.inputs do
f.input :alumn # This is causing the N+1 query
...
end
end
...
end
f.input :alumn
select 字段的生成导致对 graduation_class
的 N+1 查询。这是因为 Formtastic 通过调用 alumn.display_name
生成 select 选项,而 alumn.display_name
又调用关联的 graduation_class
上的 year
方法。
我的问题是,如何以这种形式预先加载 graduation_class
? Active Admin class 中的 includes :alumn, alumn: :graduation_class
似乎不起作用。
更新:
从服务器日志可以看出,正在加载GraduationClass
,但仍然没有消除N+1查询:
GraduationClass Load (0.6ms) SELECT "graduation_classes".* FROM "graduation_classes"
嵌套包含仅通过使用哈希来完成。
includes({:alumn => :graduation_class})
如果构建 Formtastic select 输入生成不需要的查询,请尝试使用 pluck 仅获取构建列表所需的字段。
我最终通过在 admin
字段上构建自定义集合解决了这个问题。这是相关代码:
app/admin/user.rb
ActiveAdmin.register User do
...
includes :alumn, alumn: :graduation_class
...
form do |f|
f.inputs do
f.input :alumn, as: :select,
collection: Alumn.includes(:graduation_class).where(...)
.collect { |a| [ a.display_name, a.id ] }
...
end
end
...
end
它仍然会导致额外的查询,但速度要快得多。
摘自https://rubyinrails.com/2018/02/20/rails-activeadmin-n-1-query-optimization/
将此添加到控制器块中的 Active Admin 资源
ActiveAdmin.register(MyResource) do
# ...
controller do
# ...
def scoped_collection
super.includes whatever: :you_want
end
end
end
我遇到了 N+1 query problem in an Active Admin (Formtastic) 表格。加载对应于 belongs_to
关联的 select 输入时会发生查询。关联模型上的 display_name
引用了另一个 belongs_to
关联。以下是模型关系:
:user
|-- belongs_to :alum
|-- belongs_to :graduation_class
|-- year
以下是模型的相关部分:
app/models/user.rb
class User < ApplicationRecord
...
belongs_to :alumn, optional: true, touch: true
...
end
app/models/alumn.rb
class Alumn < ApplicationRecord
belongs_to :graduation_class, touch: true
delegate :year, to: :graduation_class
has_many :users
...
def display_name
"#{year}: #{last_name}, #{first_name} #{middle_name}"
end
...
end
这是相关的活动管理员class:
app/admin/user.rb
ActiveAdmin.register User do
...
includes :alumn, alumn: :graduation_class
...
form do |f|
f.inputs do
f.input :alumn # This is causing the N+1 query
...
end
end
...
end
f.input :alumn
select 字段的生成导致对 graduation_class
的 N+1 查询。这是因为 Formtastic 通过调用 alumn.display_name
生成 select 选项,而 alumn.display_name
又调用关联的 graduation_class
上的 year
方法。
我的问题是,如何以这种形式预先加载 graduation_class
? Active Admin class 中的 includes :alumn, alumn: :graduation_class
似乎不起作用。
更新:
从服务器日志可以看出,正在加载GraduationClass
,但仍然没有消除N+1查询:
GraduationClass Load (0.6ms) SELECT "graduation_classes".* FROM "graduation_classes"
嵌套包含仅通过使用哈希来完成。
includes({:alumn => :graduation_class})
如果构建 Formtastic select 输入生成不需要的查询,请尝试使用 pluck 仅获取构建列表所需的字段。
我最终通过在 admin
字段上构建自定义集合解决了这个问题。这是相关代码:
app/admin/user.rb
ActiveAdmin.register User do
...
includes :alumn, alumn: :graduation_class
...
form do |f|
f.inputs do
f.input :alumn, as: :select,
collection: Alumn.includes(:graduation_class).where(...)
.collect { |a| [ a.display_name, a.id ] }
...
end
end
...
end
它仍然会导致额外的查询,但速度要快得多。
摘自https://rubyinrails.com/2018/02/20/rails-activeadmin-n-1-query-optimization/
将此添加到控制器块中的 Active Admin 资源
ActiveAdmin.register(MyResource) do
# ...
controller do
# ...
def scoped_collection
super.includes whatever: :you_want
end
end
end