如何在活动管理表单

时间:2017-11-01 23:40:29

标签: ruby-on-rails activeadmin formtastic

我在N+1 query problemActive Admin)表单中遇到Formtastic。加载对应于belongs_to关联的选择输入时,会发生查询。关联模型上的display_name引用了另一个belongs_to关联。以下是模型关系:

:user 
   |-- belongs_to :alum 
                     |-- belongs_to :graduation_class
                                                   |-- year

以下是模型的相关部分:

应用/模型/ user.rb

class User < ApplicationRecord
  ...
  belongs_to :alumn, optional: true, touch: true
  ...
end

应用/模型/ 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类:

应用/管理/ 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来生成选择选项,year会调用相关graduation_class上的graduation_class方法。

我的问题是,如何以这种形式加载includes :alumn, alumn: :graduation_class

表示Active Admin类中的GraduationClass似乎不起作用。

更新

我可以从服务器日志中看到,正在加载GraduationClass Load (0.6ms) SELECT "graduation_classes".* FROM "graduation_classes" ,但它仍然没有消除N + 1查询:

"""Testing the video recording settings"""
import datetime as dt
import os
from random import randint
import subprocess
import sys

import picamera


class Camera(object):
    """Camera device"""
    def __init__(self):
        self.device = None  # type: picamera.PiCamera
        self.stream = None  # type: picamera.PiCameraCircularIO

    def initialize_camera(self):
        """Initializes the camera to 1280x720 and start recording video to a circular buffer"""
        print "initializing camera"
        self.device = picamera.PiCamera()
        # set the camera's resolution (1280, 720)
        self.device.resolution = (1280, 720)
        # set the camera's framerate
        self.device.framerate = 24
        self.stream = picamera.PiCameraCircularIO(self.device, seconds=8)
        self.device.start_recording(self.stream, format='h264')
        return

    def stop_camera(self):
        """Stop the recording and turn the camera off"""
        if self.device is not None:
            if self.device.recording:
                self.device.stop_recording()
            self.device.close()
        if self.stream is not None:
            self.stream = None
        return

    def capture_video(self):
        """Store the video from the circular buffer to disk"""
        try:
            if self.device is None:
                # get the camera set up
                self.initialize_camera()
            video_name = dt.datetime.now().strftime(
                '%m-%d-%Y_%H:%M:%S').replace(':', '-') + '.h264'
            video_path = "/home/pi"
            # copy the buffer to disk
            print os.path.join(video_path, video_name)
            self.stream.copy_to(os.path.join(video_path, video_name), seconds=6)
            # add the MP4 wrapper to the h264 file using the MP4Box program
            mp4_command = "MP4Box -add '{0}' '{1}.mp4' -fps 24".format(
                os.path.join(video_path, video_name),
                os.path.join(video_path, os.path.splitext(os.path.basename(video_name))[0]))
            subprocess.call([mp4_command], shell=True)
            # remove the original h264 file
            os.remove(os.path.join(video_path, video_name))
        except BaseException as err:
            print 'error: {0} on line {1}\r'.format(err, sys.exc_info()[-1].tb_lineno)
        return


def main():
    """Main program"""
    camera = Camera()
    camera.initialize_camera()
    camera.device.wait_recording(timeout=randint(9, 20))
    camera.capture_video()
    camera.stop_camera()
    return


if __name__ == '__main__':
    main()
    sys.exit(0)

4 个答案:

答案 0 :(得分:2)

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

它仍会导致额外的查询,但速度要快得多。

答案 1 :(得分:1)

嵌套包含只是通过使用哈希来完成的。

apply

答案 2 :(得分:1)

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

答案 3 :(得分:0)

提取自 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