Как в Django ORM выполнять сложные запросы, не прибегая к raw SQL

Ответ

Django ORM предоставляет мощные инструменты для построения сложных и оптимизированных запросов к базе данных напрямую из Python-кода. Это повышает читаемость, безопасность и переносимость кода между разными СУБД.

Основные инструменты:

  • F() выражения: Позволяют ссылаться на значение поля модели непосредственно в запросе. Это дает возможность выполнять операции на уровне базы данных, избегая извлечения данных в Python.

    • Пример: Найти товары, у которых количество на складе меньше, чем заказанное количество.
      
      from django.db.models import F

    Product.objects.filter(stock_count__lt=F('ordered_count'))

  • Q() объекты: Используются для построения сложных логических условий с использованием операторов И (&), ИЛИ (|) и НЕ (~).

    • Пример: Найти пользователей, которые либо активны, либо заходили в последний месяц.
      
      from django.db.models import Q
      from django.utils import timezone
      from datetime import timedelta

    one_month_ago = timezone.now() - timedelta(days=30) User.objects.filter(Q(is_active=True) | Q(last_login__gte=one_month_ago))

  • Subquery: Позволяет встраивать один QuerySet в другой в качестве подзапроса. Часто используется для аннотаций.

    • Пример: Для каждого поста аннотировать текст последнего комментария.
      
      from django.db.models import OuterRef, Subquery

    latest_comment = Comment.objects.filter(post=OuterRef('pk')).order_by('-created_at') Post.objects.annotate( last_comment_text=Subquery(latest_comment.values('text')[:1]) )

  • Exists: Более эффективный способ проверить существование связанных записей, чем count() > 0, так как он использует EXISTS на уровне SQL.

    • Пример: Найти все посты, у которых есть хотя бы один комментарий.
      
      from django.db.models import Exists, OuterRef

    comments = Comment.objects.filter(post=OuterRef('pk')) Post.objects.annotate(has_comments=Exists(comments)).filter(has_comments=True)