如何修复“在LogEntry.user中检测到潜在的不必要的紧急负载”

时间:2019-04-30 06:37:52

标签: python-3.x optimization query-optimization select-n-plus-1 django-2.2

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

models.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,))

admin.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,)

1 个答案:

答案 0 :(得分:1)

I found N+1 to rarely be happy with Django admin - either related entries (like user groups) are loaded lazily and it makes a N+1 problem in big listings, or they are prefetched() and then it's on single-user pages that an "unnecessary eager load" is detected.

The problem is that the same base ModelAdmin querysets are used for listing pages and for single-object pages, so avoiding both problems is not easy.

Until someone comes with an easy solution to create different "prefetch" querysets for different admin pages, I've whitelisted these models like so. Just check with the django debug toolbar that no N+1 requests happen on these admin pages.

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