Я занимаюсь разработкой проекта Django, в котором уже собрано много реальных данных, чтобы я мог увидеть его производительность.
Производительность нескольких списков DjangoAdmin просто ужасна.
У меня есть один список администраторов, давайте назовем его devices
. В этом списке я получаю дополнительную информацию для каждой строки, эти поля связаны с другими таблицами и связаны через FK / PK / M2N.
Список содержит около 500 записей, и загрузка этого экрана занимает, согласно django-debug-toolbar
, около 6,5 секунд, что невыносимо.
Этот админ класс
@admin.register(Device)
class DeviceAdmin(admin.ModelAdmin):
list_select_related = True
list_display = ('id', 'name', 'project', 'location', 'machine', 'type', 'last_maintenance_log')
inlines = [CommentInline, TestLogInline]
def project(self, obj):
try:
return Device.objects.get(pk=obj.pk).machine.location.project.project_name
except AttributeError:
return '-'
def location(self, obj):
try:
return Device.objects.get(pk=obj.pk).machine.location.name
except AttributeError:
return '-'
def last_maintenance_log(self, obj):
try:
log = AdminLog.objects.filter(object_id=obj.pk).latest('time')
return '{} | {}'.format(log.time.strftime("%d/%m/%Y, %-I:%M %p"), log.title)
except AttributeError:
return '-'
Все Machine
, Location
и Project
являются таблицами в базе данных.
Изучив запросы в django-debug-toolbar
, я обнаружил нечто ужасное.
Этот экран требует 287 sql запросов! Да, более двух сотен!
Что я могу сделать, чтобы сократить это время до чего-то разумного?
EDIT:
Благодаря Бруно я удалил Device.objects.get(pk=obj.id)
(поскольку это было действительно избыточно, я полностью упустил это из виду.
Так везде я ставлю obj.
например obj.machine.location.project.project_name
Только это само по себе снижает скорость и количество запросов до половины, пока все хорошо.
У меня нет проблем со слиянием obj
подхода и select_related
подхода. Это мой текущий код, который хуже, чем только obj
подход.
def project(self, obj):
try:
Device.objects.select_related('machine__location__project').get(id=obj.pk).machine.location.project.project_name
except AttributeError:
return '-'
Что создает хорошие ВНУТРЕННИЕ СОЕДИНЕНИЯ в запросах, но производительность примерно на 15% ниже, чем без него, и используется только obj.machine.location.project.project_name
Что я делаю не так?
EDIT2:
Лучшая производительность, которую я получил, с этим кодом:
@admin.register(Device)
class DeviceAdmin(admin.ModelAdmin):
list_select_related = True
save_as = True
form = DeviceForm
list_display = ('id', 'name', 'project', 'location', 'machine', 'type', 'last_maintenance_log')
inlines = [CommentInline, TestLogInline]
def project(self, obj):
try:
return obj.machine.location.project.project_name
except AttributeError:
return '-'
def location(self, obj):
try:
return obj.machine.location.name
except AttributeError:
return '-'
def last_maintenance_log(self, obj):
try:
log = AdminLog.objects.filter(object_id=obj.pk).latest('time')
return '{} | {}'.format(log.time.strftime("%d/%m/%Y, %-I:%M %p"), log.title)
except AttributeError:
return '-'
def get_queryset(self, request):
return Device.objects.select_related('machine__location__project').all()
Это привело к уменьшению количества запросов до 104 (с почти 300) и сокращению времени более чем на 50%. Можно ли это еще улучшить?