提高Django管理员列表过滤器查询的性能

Cer*_*rin 5 python django django-admin

我正在围绕PostgreSQL数据仓库应用程序开发Django管理员包装,该应用程序具有一些具有数百万条记录的表。

无需任何列表过滤器的admin changelist页面,一秒钟即可加载,但是如果我在admin的列表中包含某些列list_filters,则加载速度非常慢,加载可能需要30秒到一分钟。

检查数据库,我看到了几个查询,例如:

SELECT DISTINCT "warehouse_data"."filter_field1" FROM "warehouse_data" ORDER BY "warehouse_data"."filter_field1" ASC;
Run Code Online (Sandbox Code Playgroud)

每一个只需要3-5秒,但由于有十几个,所以这些加起来。所有字段都已建立索引,因此我不确定如何进一步加快它们的速度。我如何改善管理员绩效?我如何插入Django的缓存机制来缓存这些列表过滤器的实际查询?

Aar*_*ron 3

正如你所观察到的;缓慢的原因是 django 编译唯一值列表,以便它可以在侧边栏中显示它们。

在幕后,这需要对数据库进行全表扫描,当表非常大时,这是昂贵的。如果您将此字段用作 list_filter;唯一值的数量很可能很小,并且您可以自己更有效地生成唯一值列表(假设您知道这些值来自哪里)。为此,您可以定义自定义 list_filter。

来自文档(为了简洁而压缩):

list_filter 应该是元素列表或元组,其中每个元素应该是以下类型之一:

  • 字段名称
  • 继承自 django.contrib.admin.SimpleListFilter 的类
from datetime import date
from django.contrib import admin
from django.utils.translation import gettext_lazy as _

class DecadeBornListFilter(admin.SimpleListFilter):
    title = _('decade born')
    parameter_name = 'decade'

    def lookups(self, request, model_admin):
        return (
            ('80s', _('in the eighties')),
            ('90s', _('in the nineties')),
        )

    def queryset(self, request, queryset):
        # Compare the requested value (either '80s' or '90s')
        # to decide how to filter the queryset.
        if self.value() == '80s':
            return queryset.filter(birthday__gte=date(1980, 1, 1),
                                    birthday__lte=date(1989, 12, 31))
        if self.value() == '90s':
            return queryset.filter(birthday__gte=date(1990, 1, 1),
                                    birthday__lte=date(1999, 12, 31))

class PersonAdmin(admin.ModelAdmin):
    list_filter = (DecadeBornListFilter,)
Run Code Online (Sandbox Code Playgroud)