Метод assertNumQueries
Метод assertNumQueries применяется в тестах Django для проверки того, что выполняется ожидаемое количество SQL-запросов. Он принимает ожидаемое число запросов и вызываемый объект или блок кода, который будет выполнен в контексте подсчёта запросов. Метод полезен для контроля производительности базы данных и предотвращения регрессий.
Синтаксис
self.assertNumQueries(num, func, *args, **kwargs)
Параметры метода:
num- ожидаемое количество выполняемых SQL-запросовfunc- вызываемый объект (функция или метод), который будет выполнен*argsи**kwargs- аргументы, передаваемые вызываемому объекту
Также метод может использоваться как контекстный менеджер:
with self.assertNumQueries(num):
# блок кода, где подсчитываются запросы
Пример
Рассмотрим простой пример использования метода для проверки количества запросов при получении списка статей:
from django.test import TestCase
from myapp.models import Article, Category
class ArticleTest(TestCase):
@classmethod
def setUpTestData(cls):
category = Category.objects.create(name="Technology")
for i in range(3):
Article.objects.create(
title=f"Article {i}",
category=category
)
def test_article_queryset(self):
# Проверяем, что запрос выполняется один раз
with self.assertNumQueries(1):
articles = Article.objects.all()
res = list(articles)
Пример
Пример использования метода для обнаружения проблемы N+1 запросов при обращении к связанным полям:
from django.test import TestCase
from myapp.models import Article, Category
class ArticleTest(TestCase):
@classmethod
def setUpTestData(cls):
category = Category.objects.create(name="Technology")
for i in range(3):
Article.objects.create(
title=f"Article {i}",
category=category
)
def test_article_without_prefetch(self):
# Без prefetch_related будет N+1 запрос
# 1 запрос для статей + 3 запроса для категорий = 4 запроса
with self.assertNumQueries(4):
articles = Article.objects.all()
for article in articles:
print(article.category.name)
def test_article_with_prefetch(self):
# С prefetch_related будет всего 2 запроса
with self.assertNumQueries(2):
articles = Article.objects.prefetch_related("category")
for article in articles:
print(article.category.name)
Пример
Использование метода с функцией, выполняющей сложные запросы и агрегации:
from django.test import TestCase
from myapp.models import Article, Comment
from django.db.models import Count
class ArticleTest(TestCase):
@classmethod
def setUpTestData(cls):
article = Article.objects.create(
title="Test Article",
status="published"
)
for i in range(5):
Comment.objects.create(
article=article,
text=f"Comment {i}",
is_approved=i % 2 == 0
)
def test_article_with_annotate(self):
# Проверяем количество запросов при использовании аннотаций
def get_articles_with_count():
res = Article.objects.annotate(
comment_count=Count("comments", filter=Q(comments__is_approved=True))
).values("title", "comment_count")
return list(res)
# Ожидаем 1 запрос для аннотированной выборки
with self.assertNumQueries(1):
res = get_articles_with_count()
print(res)
Результат выполнения кода:
[{'title': 'Test Article', 'comment_count': 3}]
Смотрите также
-
метод
assertContains,
который проверяет наличие содержимого в ответе -
метод
assertRedirects,
который проверяет перенаправления в ответе -
метод
assertTemplateUsed,
который проверяет использование шаблона -
метод
setUpTestData,
который создаёт тестовые данные для класса