Класс Subquery

Класс Subquery применяется внутри выражений аннотации или фильтрации для встраивания подзапроса в основной запрос. Он принимает на вход QuerySet и позволяет использовать его результат в качестве значения для поля, агрегации или условия. Подзапросы выполняются в контексте внешнего запроса, что делает их мощным инструментом для решения сложных задач выборки данных.

Синтаксис

pythonSubquery(queryset, output_field=None, **extra)

Параметры:

Пример

Используем Subquery для получения количества комментариев к каждой статье:

pythonfrom django.db.models import Subquery, Count, OuterRef
from myapp.models import Article, Comment

subquery = Comment.objects.filter(article=OuterRef('pk')).values('article').annotate(
  total=Count('pk')
).values('total')

articles = Article.objects.annotate(comments_count=Subquery(subquery))

for article in articles:
  print(f"{article.title}: {article.comments_count}")

Результат выполнения кода:

python"article 1: 5"
"article 2: 3"
"article 3: 0"

Пример

Фильтрация статей по самому позднему комментарию с использованием Subquery и агрегации:

pythonfrom django.db.models import Subquery, Max, OuterRef
from myapp.models import Article, Comment
from datetime import datetime

subquery = Comment.objects.filter(
  article=OuterRef('pk')
).values('article').annotate(
  last_date=Max('created_at')
).values('last_date')

recent_articles = Article.objects.filter(
  id__in=Subquery(
    Comment.objects.filter(
      created_at__gte=datetime(2025, 1, 1)
    ).values('article')
  )
)

for article in recent_articles:
  print(article.title)

Результат выполнения кода:

python"article 1"
"article 3"

Пример

Сравнение значения поля с результатом подзапроса с помощью Subquery в фильтрации:

pythonfrom django.db.models import Subquery, F, OuterRef
from myapp.models import Product, Order

subquery = Order.objects.filter(
  product=OuterRef('pk')
).values('product').annotate(
  total_sum=Sum('quantity')
).values('total_sum')

products = Product.objects.annotate(
  ordered_count=Subquery(subquery)
).filter(
  ordered_count__gt=F('stock')
)

for product in products:
  print(f"{product.name}: {product.ordered_count}")

Результат выполнения кода:

python"product 1: 15"
"product 3: 8"

Смотрите также

  • класс OuterRef,
    который используется для ссылки на внешний запрос внутри подзапроса
  • класс Exists,
    который проверяет существование записей в подзапросе
  • метод annotate,
    который добавляет вычисляемые поля к каждой записи QuerySet
  • метод filter,
    который применяет условия фильтрации к QuerySet