Django 仅预取相关模型的最新对象

标签 django orm

考虑以下模型:

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() 执行 =

我也尝试过以下但没有成功:

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

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

最佳答案

您可以使用一些自定义代码来做到这一点:

from django.db.models import OuterRef, Subquery


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

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 在幕后所做的:相反,它会获取与这些作者相关的所有书籍,然后为每个对象创建一个对象将相关的 Book 对象包装到一个集合中。

关于Django 仅预取相关模型的最新对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74181348/

相关文章:

java - Hibernate、Collections 和 Session.refresh(...) 的 Stackoverflowerror/Unresolved Object Exception

java - 单向 @OneToMany 无法按预期与 @EmbeddedId 配合使用

c# - 传统 sql 方法 VS ORM

python - 在 django 中使用适当的值渲染为 Html

Django 管理搜索/过滤功能作为页表

python - sudo pip 安装 django

php - CodeIgniter、models、ORM,这个怎么处理?

django - 我可以在主键为 AutoFields 的模型上使用 bulk_create 而不会导致数据不一致吗?

python - 使用python social auth通过访问 token 注册用户

Python+SQLAlchemy : Getting mapper class from InstrumentedList?