请考虑以下模型:

class Author(models.Model):
    name = models.CharField(max_length=100)

class Book(models.Model):
    author = models.ForeignKey(Author, related_name="books", on_delete=models.CASCADE)
    title = models.CharField(max_length=100)
    created_at = models.DateTimeField(auto_now_add=True)

和以下代码:

queryset = Author.objects.all()
for author in queryset:
    print(author.name)
    print(author.books.latest("created_at").title)

如上所述,将导致N+1个查询. 我试着通过预取books来修复它,如下所示:

queryset = Author.objects.prefetch_related('books')

然而,这并不能解决N+1问题. 我认为原因是预取的结果是SELECT * FROM book WHERE author_id IN (1,2,...),这与调用.Latest()所执行的查询不同,即SELECT * FROM book WHERE author_id = 1 ORDER BY created_at DESC LIMIT 1.预取操作执行IN,.Latest()执行=操作.

我还try 了以下几种方法,但都没有成功:

queryset = Author.objects.prefetch_related(Prefetch('books', queryset=Book.objects.order_by("-created_at")))

在使用.Latest()时,为了避免N+1个 Select ,预取应该是什么样子?

推荐答案

您可以使用一些定制代码来实现这一点:

from django.db.models import OuterRef, Subquery


authors = Author.objects.annotate(
    last_book_id=Subquery(
        Book.objects.filter(author_id=OuterRef('pk')).order_by('-created_at').values('pk')[:1]
    )
)

author_dict = {author.pk: author for author in authors}

last_books = Book.objects.filter(
    pk__in=[author.last_book_id for author in authors if author.last_book_id is not None]
)

for book in last_books:
    author_dict[book.author_id].last_book = book

如果存在具有最后一本书的作者的至少一本书,则authors中的Author个对象将具有额外的属性last_book.

这里的逻辑在某种程度上就是Django的.prefetch_related在幕后所做的事情:相反,它将获取与这些作者相关的所有书籍,然后 for each 对象创建对象一个对象,在其中它将相关的Book个对象包装到一个集合中.

Django相关问答推荐

Django动态页面.为什么我的代码不工作?

Django ORM Groupby

如何在Django REST框架中实现ForeignKey搜索

Django allauth 社交帐户注册中邮箱的模板变量

无法使用 django-filters 进行过滤

基于令牌的身份验证如何工作?

如何在 Django 模板视图中显示我的数据?

基于模型多选字段在模板django中显示结果

Django 模型 Select - 只允许管理界面上的特定转换

django 创建多种类型用户的最佳方法

验证 Django 模型对象的正确方法?

如何从 django 自定义中间件类返回 rest_framework.response 对象?

具有基于类的视图的 cache_page

在 django admin 中创建对象时如何自动插入当前用户?

在 Django 中注册用户的最佳方法

如何查询名称包含python列表中任何单词的模型?

显式 cursor.close() 的必要性

基于 User-Agent 更改 Django 模板

Django 模型方法 - create_or_update

它是如何工作的,Django INSTALLED_APPS 的命名约定?