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

Ответ

В 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

Ответ 18+ 🔞

А, слушай, смотри, тут про Django ORM, про эти запросы к базе. Ну, в общем, чтобы не ебать мозг себе и базе, они придумали такую хуйню — QuerySet. Это типа такая ленивая сука, понимаешь? Она до последнего нихуя не делает!

Представь: ты говоришь — «дай мне всех пользователей». А она тебе: «ага, щас, запомнила». И нихуя не лезет в базу! Пока ты реально не потребуешь данные — типа не начнёшь по ним итерироваться или срез делать — она просто тупо запоминает, что ты там нацепил фильтров.

Вот смотри, как это выглядит в коде, блядь:

from myapp.models import User

# Это просто объявление намерений, запрос ещё не летит!
all_users = User.objects.all()

Дальше ты начинаешь цеплять методы, как мартышка на лиане:

# Цепочка фильтров, сортировка — и всё ещё нихуя!
active_admins = User.objects.filter(is_active=True, role='admin').order_by('-date_joined')

Вот тут-то и кроется магия, ёпта! Запрос в базу улетит только в тот момент, когда ты реально попытаешься что-то с этим QuerySet сделать. Например, начнёшь циклом по нему проходиться:

for admin in active_admins:  # ОП! Вот сейчас-то и полетел SELECT в базу!
    print(admin.username)

Или срез возьмёшь, или в список превратишь — короче, когда припрет. Это и называется ленивая оценка (Lazy Evaluation). Очень удобная хуйня, потому что можно накрутить кучу условий, а база сработает один раз и сразу по делу.

А ещё там есть такие штуки, как annotate() и aggregate(). Это когда ты хочешь, чтобы база сама посчитала какую-нибудь хуйню, а не ты потом в Питоне циклы городил.

from django.db.models import Count

# Вот тут к каждому юзеру добавится поле post_count — количество его постов.
# Всё считается одним запросом, на стороне базы! Красота, блядь.
users_with_post_count = User.objects.annotate(post_count=Count('posts'))

И самое главное — если тебе интересно, в какой конкретно SQL-запрос превратилась твоя красивая цепочка методов, можно просто заглянуть под капот:

print(active_admins.query)

Выведет тебе всю эту SQL-порнографию, которую ORM сгенерировала. Прямо как есть: SELECT ... WHERE ... ORDER BY .... Пиздец, иногда смотришь и офигеваешь, какую простыню она накрутила из трёх строчек на Питоне.

Вот так вот, коротко и по делу. Главное — помни, что QuerySet ленивая, как мой кот в воскресенье. Не трогаешь — и она не шевелится.