При стандартных менеджерах будет два SELECT. Первый цикл получает статьи и авторов через JOIN, второй заново загружает статьи; чтение author_id не требует отдельного обращения к Author.
Подсчёт запросов к базе данных при использовании Django ORM
При стандартных менеджерах будет два SELECT. Первый цикл получает статьи и авторов через JOIN, второй заново загружает статьи; чтение author_id не требует отдельного обращения к Author.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
Определите, сколько запросов к базе данных будет выполнено при выполнении следующего кода:
class Author(models.Model):
first_name = models.CharField(max_length=128)
last_name = models.CharField(max_length=128)
is_active = models.BooleanField()
class Article(models.Model):
author = models.ForeignKey(Author, on_delete=models.CASCADE)
name = models.CharField(max_length=64)
is_private = models.BooleanField()
views = models.IntegerField()
articles = Article.objects.all()
private_articles = articles.filter(is_private=True)
public_articles = articles.filter(is_private=False)
articles = Article.objects.select_related('author').all()
for article in articles:
print(article.author.first_name, article.author.last_name)
articles = Article.objects.all()
for a in articles:
print(a.author_id)
Ответ
При стандартных менеджерах будет два SELECT. Первый цикл получает статьи и авторов через JOIN, второй заново загружает статьи; чтение author_id не требует отдельного обращения к Author.
Начальные all и два filter ленивы и не вычисляются, поэтому запросов не создают. Затем переменная articles заменяется новой выборкой с select_related('author'): её обход — один SELECT с данными авторов.
Следующее Article.objects.all() — новый QuerySet без кеша предыдущего. Его обход выполняет второй SELECT. author_id уже хранится в строке Article, поэтому N+1 не возникает. Даже пустые результаты потребуют этих двух запросов, если оба цикла достигаются.