【问题标题】:How to optimize count of queries in Django admin for custom fields from another model?如何优化 Django admin 中针对来自另一个模型的自定义字段的查询计数?
【发布时间】:2020-06-03 09:10:31
【问题描述】:

我对自定义 list_display 字段“余额”的 SQL 查询优化有疑问。 用于获取对每一行执行的帐户余额查询。有什么方法可以避免它并使计数查询固定。 我尝试使用prefetch_related,但这没有任何效果。

class UserAdmin(admin.ModelAdmin):
    form = UserAdminForm
    fields = [
        'username',
        'password',
        'last_name',
        'first_name',
        'middle_name',
        'groups'
    ]
    list_display = [
        'username_',
        'person_name',
        'balance',
    ]

    def balance(self, obj):
        balance = obj.person.customer.customeraccount_set.first().account.balance
        return balance
    balance.allow_tags = True
    balance.short_description = 'Balance'


    def get_queryset(self, request):
        qs = super().get_queryset(request).prefetch_related(
            Prefetch('person__customer__customeraccount_set__account', queryset=CustomerAccount.objects.all()),
            'person', 'person__customer', 'person__customer__customeraccount_set',
        ).filter(groups__name='водитель')
        ordering = self.get_ordering(request)
        if ordering:
            qs = qs.order_by(*ordering)
        return qs

调试工具栏的屏幕:

【问题讨论】:

  • 您没有预取客户帐户模型。您需要预取它以避免 N+1 查询。
  • 现在我预取了客户帐户模型,但这不起作用。请查看更新的代码。

标签: django django-models django-admin django-orm sql-optimization


【解决方案1】:

您不应该预取整个集合来显示其中的单个项目。这也可能导致性能不佳。而是使用 Subquery 从该集合中获取单个属性。

from django.db.models import Subquery, OuterRef

class UserAdmin(...):
    ...

    def balance(self, obj):
        balance = obj.customer_account_balance
        return balance
    balance.allow_tags = True
    balance.short_description = 'Balance'


    def get_queryset(self, request):
        qs = super().get_queryset(request).select_related(
            'person', 'person__customer',
        ).filter(groups__name='водитель').annotate(
            customer_account_balance=Subquery(
                CustomerAccount.objects.filter(
                    user_id=OuterRef('id')
                ).order_by(
                    'id' # I don't know how you want to select the balance, but this is where you'd order it.
                ).values('balance')[:1]
            )
        )
        ordering = self.get_ordering(request)
        if ordering:
            qs = qs.order_by(*ordering)
        return qs

【讨论】:

    猜你喜欢
    • 2014-09-19
    • 2011-05-09
    • 2021-03-27
    • 1970-01-01
    • 1970-01-01
    • 2017-12-30
    • 2014-06-10
    • 2021-01-31
    • 1970-01-01
    相关资源
    最近更新 更多