Как в Django ORM формируются запросы к базе данных?

«Как в Django ORM формируются запросы к базе данных?» — вопрос из категории Django, который задают на 10% собеседований Python Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В Django запросы к базе данных строятся с помощью ORM (Object-Relational Mapping), который позволяет работать с данными как с Python-объектами. Ключевым элементом является QuerySet — объект, представляющий собой "ленивую" коллекцию записей из базы данных.

Основной принцип — ленивая оценка (Lazy Evaluation): Запрос к базе данных не выполняется до тех пор, пока данные из QuerySet не потребуются явно. Это позволяет эффективно строить сложные запросы, не нагружая базу данных на каждом шаге.

Процесс построения запроса:

  1. Получение менеджера модели: Начальная точка для любого запроса — менеджер objects.

    from myapp.models import User
    
    # Создает базовый QuerySet, который выберет все записи
    all_users = User.objects.all()
  2. Фильтрация и chaining (цепочки методов): Методы filter(), exclude(), order_by() и другие возвращают новый QuerySet, позволяя объединять их в цепочки.

    # На этом этапе SQL-запрос еще НЕ выполнен
    active_admins = User.objects.filter(is_active=True, role='admin').order_by('-date_joined')
  3. Выполнение запроса (Evaluation): QuerySet обращается к базе данных только при попытке получить доступ к данным:

    • Итерация: for user in active_admins:
    • Срезы: active_admins[:5]
    • Вызов len(), list(), bool()
  4. Агрегация и аннотация: Для вычислений на стороне БД используются aggregate() и annotate().

    from django.db.models import Count
    
    # Добавить к каждому пользователю поле с количеством его постов
    users_with_post_count = User.objects.annotate(post_count=Count('posts'))

Чтобы увидеть, какой SQL-запрос будет сгенерирован, можно использовать свойство query:

print(active_admins.query)
# Вывод: SELECT ... FROM "myapp_user" WHERE ("myapp_user"."is_active" = True AND "myapp_user"."role" = 'admin') ORDER BY "myapp_user"."date_joined" DESC