Django Admin中列表视图和更改视图的不同查询集优化

int*_*ist 4 django-admin

get_queryset管理模型的方法可以被覆盖;我用它来选择/预取OneToOneFields 或ManyToManyFields 的对象。但是,我的模型的列表视图仅显示简明信息,而更改视图包含更多对象。ManyToManyField如果无论如何都不会显示这些关系,那么在列表视图中预取关系是没有意义的。

样品型号:

class Location(TimeStampedModel):
    owner = models.ForeignKey('Profile', on_delete=models.CASCADE)
    postcode = models.CharField("postcode", max_length=11, blank=True)
    tenants = models.ManyToManyField('Profile', blank=True)
Run Code Online (Sandbox Code Playgroud)

示例管理模型:

@admin.register(Location)
class LocationAdmin(admin.ModelAdmin):
    list_display = ('owner', 'postcode')
    fields = ('owner', 'postcode', 'tenants')
    filter_horizontal = ('tenants',)

    def get_queryset(self, request):
        qs = super(LocationAdmin, self).get_queryset(request).select_related('owner__user')
        qs = qs.prefetch_related('tenants')
        return qs
Run Code Online (Sandbox Code Playgroud)

是否可以为模型列表视图返回的查询集和同一模型的更改/添加视图返回的查询集定义不同的优化?

也就是说,在上面的示例管理模型中,该qs.prefetch_related('tenants')行将仅与更改/添加视图相关?

spo*_*key 5

实现此目的的最简单方法是使用request.resolver_match可用于确定您正在执行的视图的属性。以下内容有点笨拙/脆弱(它本质上是使用一些内部结构),但有效:

class LocationAdmin(admin.ModelAdmin):
    list_display = ['owner', 'postcode']
    fields = ['owner', 'postcode', 'tenants']
    filter_horizontal = ['tenants']

    def get_queryset(self, request):
        qs = super(LocationAdmin, self).get_queryset(request)
        qs = qs.select_related('owner__user')
        if request.resolver_match.func.__name__ == 'change_view':
            qs = qs.prefetch_related('tenants')
        return qs
Run Code Online (Sandbox Code Playgroud)

您还应该考虑是否需要它以及它是否真的有效。更改视图仅显示一个主要对象,这意味着对象列表的 N+1 问题通常不适用。此外,内联查询以及外键和多对多字段的小部件可能不会使用您提供的查询集get_queryset。对于这种情况,使用 Django 1.10.2 进行测试,该prefetch_related调用并未减少“更改”视图执行的查询次数。“添加”视图根本不使用该get_queryset方法。

https://bitbucket.org/spookylukey/djangoadmintips/src/default/queryset_optimization/?at=default查看完整的演示应用程序