首页 > 解决方案 > 如何修复“在 `LogEntry.user` 上检测到潜在的不必要的急切负载”

问题描述

我正在使用插件nplusonehttps://github.com/jmcarp/nplusone)创建一个新的 Django 2.2 项目。当我尝试继续 /admin URL 时,我收到下一个错误“检测到潜在的不必要的急切负载LogEntry.user”当我在 DB 中至少有 1 条记录时会出现此问题。

模型.py

class Post(models.Model):
    author = models.ForeignKey(User,
                               on_delete=models.CASCADE)
    title = models.CharField(max_length=200)
    created_date = models.DateTimeField(auto_now_add=True)
    body = models.TextField()
    slug = models.SlugField(max_length=100, unique=True)
    preview_image = models.ImageField(upload_to='images', blank=True)

    class Meta:
        verbose_name = 'Post'
        verbose_name_plural = 'Posts'
        ordering = ['-id']

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse('post', args=(self.slug,))

管理员.py

admin.site.register(Post, PostAdmin)

调试 SQL

SELECT "django_session"."session_key", "django_session"."session_data", "django_session"."expire_date" FROM "django_session" WHERE ("django_session"."expire_date" > '2019-04-30 06:11:28.376554' AND "django_session"."session_key" = 'hbwpegj1vpvkv5wdkbqr38ty55s3cm38'); args=('2019-04-30 06:11:28.376554', 'hbwpegj1vpvkv5wdkbqr38ty55s3cm38')
SELECT "auth_user"."id", "auth_user"."password", "auth_user"."last_login", "auth_user"."is_superuser", "auth_user"."username", "auth_user"."first_name", "auth_user"."last_name", "auth_user"."email", "auth_user"."is_staff", "auth_user"."is_active", "auth_user"."date_joined" FROM "auth_user" WHERE "auth_user"."id" = 1; args=(1,)
SELECT "django_admin_log"."id", "django_admin_log"."action_time", "django_admin_log"."user_id", "django_admin_log"."content_type_id", "django_admin_log"."object_id", "django_admin_log"."object_repr", "django_admin_log"."action_flag", "django_admin_log"."change_message", "auth_user"."id", "auth_user"."password", "auth_user"."last_login", "auth_user"."is_superuser", "auth_user"."username", "auth_user"."first_name", "auth_user"."last_name", "auth_user"."email", "auth_user"."is_staff", "auth_user"."is_active", "auth_user"."date_joined", "django_content_type"."id", "django_content_type"."app_label", "django_content_type"."model" FROM "django_admin_log" INNER JOIN "auth_user" ON ("django_admin_log"."user_id" = "auth_user"."id") LEFT OUTER JOIN "django_content_type" ON ("django_admin_log"."content_type_id" = "django_content_type"."id") WHERE "django_admin_log"."user_id" = 1 ORDER BY "django_admin_log"."action_time" DESC  LIMIT 10; args=(1,)

标签: pythondjangooptimizationquery-optimizationselect-n-plus-1

解决方案


我发现 N+1 很少对 Django 管理员感到满意 - 要么相关条目(如用户组)被延迟加载,它在大列表中产生 N+1 问题,要么它们被 prefetched() 然后它在单用户页面上检测到“不必要的急切负载”。

问题是相同的基本 ModelAdmin 查询集用于列表页面和单对象页面,因此避免这两个问题并不容易。

直到有人提供了一个简单的解决方案来为不同的管理页面创建不同的“预取”查询集,我已经像这样将这些模型列入白名单。只需使用 django 调试工具栏检查这些管理页面上没有发生 N+1 请求。

NPLUSONE_WHITELIST = [
    {"model": "admin.LogEntry", "field": "user"},
    {"model": "accounts.User", "field": "groups"},
]

推荐阅读