【问题标题】:How do you optimise Django Auth queries?你如何优化 Django Auth 查询?
【发布时间】:2023-03-25 09:15:01
【问题描述】:

我已经使用 Django Rest Framework 将用户模型从 django.contrib.auth.models 转换为 API。 当我在 API 中执行 User.objects.all() 查询时,每次调用 API 时都会遇到数百个这样的查询:

SELECT `django_content_type`.`id`,
  `django_content_type`.`app_label`,
  `django_content_type`.`model`
FROM `django_content_type` 
WHERE `django_content_type`.`id` = 2

因此用户 API 很慢。

尝试使用 prefetch_related 优化那些我结束了这个查询:

User.objects.all().prefetch_related('user_permissions__content_type__id')

但它会产生错误:

'user_permissions__content_type__id' does not resolve to an item that supports 
prefetching - this is an invalid parameter to prefetch_related().

那么如何将查询计数从数百减少到我通常可以将 django rest 框架优化到的五六个呢?

为了记录,这是我的完整代码(为相关性而删节):

from rest_framework import viewsets
from django.contrib.auth.models import User

class UserViewSet(viewsets.ModelViewSet):
  model = User
  filter_fields = ('username',)

  def get_queryset(self):
    if self.request.user.is_staff:
        return User.objects.all().prefetch_related('user_permissions__content_type__id')

注意:this similar question 不同,因为它不是指内置的身份验证模型。这是我正在尝试使用的内置身份验证模型。

【问题讨论】:

  • 你有没有试过在最后删除__id?我认为这应该按原样工作。
  • 删除 __id 会阻止它崩溃,但它不会进行必要的预取。我仍然有数百个查询。在我看来,内置的 django auth 用户模型根本无法像其他模型那样预取。
  • 我的问题与在 Django Rest Framework 中优化数据库查询不同,因为该问题中的 OP 使用手动用户模型,而我正在尝试使用内置用户模型。这是我无法预取的内置模型。我自己的手卷模型很容易预取。

标签: django django-rest-framework django-authentication


【解决方案1】:

如果您使用的是自定义用户模型,则需要通过从 UserAdmin 继承来设置您的管理模型。否则,您将生成大量查询并大大降低管理面板的速度。

这是一个例子:

from django.contrib.auth.admin import UserAdmin


@admin.register(models.AuthUser)
class AuthUserAdmin(UserAdmin):
    list_display = ['date_joined', 'username', 'email', 'is_staff', 'is_superuser']
    list_filter = ('is_staff', 'is_superuser')
    ordering = ['-username']
    search_fields = ['username', ]
    read_only_exclude = ('', )

【讨论】:

    【解决方案2】:

    可以使用formfield_for_manytomany(https://docs.djangoproject.com/en/2.2/ref/contrib/admin/#django.contrib.admin.ModelAdmin.formfield_for_manytomany),查询所有权限,选择相关的content_type。

    from django.contrib import admin
    from django.contrib.auth.models import User, Permission
    
    @admin.register(User)
    class UserAdmin(admin.ModelAdmin):
        def formfield_for_manytomany(self, db_field, request, **kwargs):
            if db_field.name == 'user_permissions':
                kwargs['queryset'] = Permission.objects.all().select_related('content_type')
            return super().formfield_for_manytomany(db_field, request, **kwargs)
    

    【讨论】:

      猜你喜欢
      • 2021-08-24
      • 1970-01-01
      • 2011-10-08
      • 2012-03-19
      • 1970-01-01
      • 2023-03-30
      • 2018-04-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多