【问题标题】:Different queryset optimisation for list view and change view in Django AdminDjango Admin中列表视图和更改视图的不同查询集优化
【发布时间】:2017-02-24 13:04:25
【问题描述】:

可以覆盖管理模型的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)

示例管理模型:

@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

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

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

【问题讨论】:

    标签: django-admin


    【解决方案1】:

    实现此目的的最简单方法是使用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
    

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

    https://github.com/spookylukey/djangoadmintips/tree/master/queryset_optimization查看完整的演示应用程序

    【讨论】:

    • 谢谢! add 视图不使用 get_queryset 是有道理的...在我的情况下,tenants 的水平小部件导致系统中每个 User 的单独查询(4000 个用户 = 4000 个附加查询)所以prefetch_relatedselect_related 是相当优化的。那是因为Profile__str__ 方法依赖于User 模型中的一些信息。
    • 您提出的这种方法有多“hacky”?它不会在 Django 的下一个版本中中断吗?同时,我重写了 change_viewadd_viewchangelist_view 函数以提供不同的优化。
    • 要修复Profile__str__ 方法,您还可以将prefetch_related 调用添加到默认管理器get_queryset 方法中。
    • 关于 hackiness - 这取决于change_view 函数的名称,它不太可能改变。您还可以添加更多断言来检查方法名称是否是您期望的选项之一,因此如果它中断您就会知道。
    • 顺便说一句,如果它对你有用,你能投票/接受答案吗?
    猜你喜欢
    • 1970-01-01
    • 2011-10-12
    • 1970-01-01
    • 2011-10-04
    • 1970-01-01
    • 2011-01-15
    • 1970-01-01
    • 2019-08-23
    • 1970-01-01
    相关资源
    最近更新 更多