Жадная загрузка select_related в Django
Пусть у нас есть категории и посты,
связанные через ForeignKey:
categories
- id
- name
posts
- id
- title
- category_id
Связанные объекты в Django по
умолчанию загружаются лениво.
Это значит, что SQL-запрос к категории
выполняется в момент обращения
к post.category.
Давайте получим все посты и в цикле выведем название категории:
from main.models import Post
posts = Post.objects.all()
for post in posts:
print(post.title, post.category.name)
На каждый пост Django сделает
отдельный запрос к таблице категорий.
Если постов 100, получится
101 запрос: один на список
постов и еще 100 на категории.
Это называют проблемой N+1.
Для решения используют метод
select_related. Он заранее
подгружает связанный объект через
JOIN. Метод подходит для
ForeignKey и
OneToOneField:
posts = Post.objects.select_related('category')
for post in posts:
print(post.title, post.category.name)
Теперь Django сделает один запрос с JOIN, а в цикле лишних обращений к базе не будет.
То же работает для связи один к одному. Пусть посты связаны с миниатюрами:
posts
- id
- title
thumbnails
- id
- path
- alt
- post_id
Подгрузим миниатюры заранее:
posts = Post.objects.select_related('thumbnail')
for post in posts:
print(post.title, post.thumbnail.path)
Получите все города вместе
со странами через
select_related.
Получите всех работников вместе
с контактами через
select_related.
Расскажите, в чем проблема N+1
и как select_related
ее решает.