糟糕的Django管理员性能

时间:2018-08-28 11:11:13

标签: python django django-rest-framework django-admin

我正在开发一个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 '-' 

所有MachineLocationProject都是数据库中的表。

django-debug-toolbar中查询后,我发现了一件可怕的事情。 该屏幕需要 287 sql查询!是的,超过两百个!

有什么我可以做的事情来减少这段时间吗?

编辑: 多亏了Bruno,我删除了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 '-'

在查询中创建了一个不错的INNER JOINs,但是性能比没有使用并仅使用obj.machine.location.project.project_name

差15%

我在做什么错了?

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%以上。可以进一步改善吗?

1 个答案:

答案 0 :(得分:2)

首先,避免完全无用的查询-这:

console.log(JSON.stringify(currentFlagObject['src']))

由于Device.objects.get(pk=obj.pk) 已经您要查找的obj实例而变得毫无用处。

然后override your DeviceAdmin.get_queryset method来正确使用select_relatedprefetch_related,这样您只需要最少数量的查询。