【问题标题】:How to avoid N+1 Query and Perform Eager Loading on Active Admin Form如何避免 N+1 查询并在 Active Admin Form 上执行 Eager Loading
【发布时间】:2017-11-01 23:40:29
【问题描述】:

我正在以Active Admin (Formtastic) 的形式遇到N+1 query problem。加载与belongs_to 关联对应的选择输入时会发生查询。关联模型上的 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

这是相关的 Active Admin 类:

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 选择字段的生成导致对 graduation_class 的 N+1 查询。这是因为Formtastic 通过调用alumn.display_name 生成选择选项,而alumn.display_name 又调用关联graduation_class 上的year 方法。

我的问题是,我怎样才能以这种形式急切加载graduation_class? Active Admin 类中的includes :alumn, alumn: :graduation_class 似乎不起作用。

更新:

我可以从服务器日志中看到,GraduationClass 正在加载,但它仍然没有消除 N+1 查询:

GraduationClass Load (0.6ms)  SELECT "graduation_classes".* FROM "graduation_classes"

【问题讨论】:

    标签: ruby-on-rails activeadmin formtastic


    【解决方案1】:

    我终于通过在 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
    

    它仍然会产生额外的查询,但速度要快得多。

    【讨论】:

      【解决方案2】:

      嵌套包含仅通过使用哈希来完成。

      includes({:alumn => :graduation_class})
      

      【讨论】:

      • 我试过 `includes :alumn, { :alumn => :graduation_class }` 没用。
      • 没有第一个 :alumn。只有哈希
      • 那也没用。我可以从服务器日志中看到 GraduationClass 正在加载,但由于某种原因,它没有转换为表单。
      【解决方案3】:

      如果构建 Formtastic 选择输入会生成不需要的查询,请尝试使用 pluck 仅获取构建列表所需的字段。

      【讨论】:

        【解决方案4】:

        摘自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
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-05-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多