选择相关
select_related 是一个可选的性能提升器,通过它进一步访问 Queryset 中的 foreign_keys 属性不会影响数据库。
Design philosophies
这也是存在 select_related() QuerySet 方法的原因。对于选择“每个相关对象”的常见情况,这是一个可选的性能提升器。
Django official doc
返回一个 QuerySet,它将“遵循”外键关系,在执行查询时选择其他相关对象数据。这是一个性能提升器,它会导致单个更复杂的查询,但意味着以后使用外键关系将不需要数据库查询。
正如定义中所指出的,只允许在 foreign_key 关系中使用select_related。忽略此规则将面临以下例外:
In [21]: print(Book.objects.select_related('name').all().query)
FieldError: Non-relational field given in select_related: 'name'. Choices are: author
让我们通过一个例子来深入了解它:
这是我的models.py。 (和提问的一样)
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return self.name
__repr__ = __str__
class Book(models.Model):
name = models.CharField(max_length=50)
author = models.ForeignKey(Author, related_name='books', on_delete=models.DO_NOTHING)
def __str__(self):
return self.name
__repr__ = __str__
- 使用
relect_related 助推器获取所有书籍及其作者:
In [25]: print(Book.objects.select_related('author').all().explain(verbose=True, analyze=True))
Hash Join (cost=328.50..548.39 rows=11000 width=54) (actual time=3.124..8.013 rows=11000 loops=1)
Output: library_book.id, library_book.name, library_book.author_id, library_author.id, library_author.name
Inner Unique: true
Hash Cond: (library_book.author_id = library_author.id)
-> Seq Scan on public.library_book (cost=0.00..191.00 rows=11000 width=29) (actual time=0.008..1.190 rows=11000 loops=1)
Output: library_book.id, library_book.name, library_book.author_id
-> Hash (cost=191.00..191.00 rows=11000 width=25) (actual time=3.086..3.086 rows=11000 loops=1)
Output: library_author.id, library_author.name
Buckets: 16384 Batches: 1 Memory Usage: 741kB
-> Seq Scan on public.library_author (cost=0.00..191.00 rows=11000 width=25) (actual time=0.007..1.239 rows=11000 loops=1)
Output: library_author.id, library_author.name
Planning Time: 0.234 ms
Execution Time: 8.562 ms
In [26]: print(Book.objects.select_related('author').all().query)
SELECT "library_book"."id", "library_book"."name", "library_book"."author_id", "library_author"."id", "library_author"."name" FROM "library_book" INNER JOIN "library_author" ON ("library_book"."author_id" = "library_author"."id")
如您所见,使用 select_related 会导致在提供的外键上出现INNER JOIN(这里是author)。
执行时间其中的时间:
是 8.562 毫秒
另一方面:
- 在不使用 relect_related 助推器的情况下获取所有书籍及其作者:
In [31]: print(Book.objects.all().explain(verbose=True, analyze=True))
Seq Scan on public.library_book (cost=0.00..191.00 rows=11000 width=29) (actual time=0.017..1.349 rows=11000 loops=1)
Output: id, name, author_id
Planning Time: 1.135 ms
Execution Time: 2.536 ms
In [32]: print(Book.objects.all().query)
SELECT "library_book"."id", "library_book"."name", "library_book"."author_id" FROM "library_book
如您所见,这只是对仅包含 author_id 的图书模型的简单 SELECT 查询。在这种情况下,执行时间为 2.536 毫秒。
如 Django 中提到的doc:
进一步访问外键属性将导致数据库再次受到攻击:(CUZ 我们还没有)
In [33]: books = Book.objects.all()
In [34]: for book in books:
...: print(book.author) # Hit the database
另请参阅 QuerySet API 参考中的 Database access optimization 和 explain()
Django Database Caching:
Django 带有一个强大的缓存系统,可以让您保存动态页面,因此不必为每个请求计算它们。为方便起见,Django 提供了不同级别的缓存粒度:您可以缓存特定视图的输出,可以仅缓存难以生成的部分,也可以缓存整个站点。
Django 也适用于“下游”缓存,例如 Squid 和基于浏览器的缓存。这些是您不能直接控制的缓存类型,但您可以(通过 HTTP 标头)向它们提供有关应缓存站点的哪些部分以及如何缓存的提示。
您应该阅读这些文档以找出最适合您的文档。
PS1:有关规划器及其工作原理的更多信息,请参阅Why Planing time and Execution time are so different Postgres? 和Using EXPLAIN)