我在N+1 query problem(Active 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
?
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)
答案 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