> Как выполняется чистый SQL в Django, например через execute (Python)
Уровень: senior · Роль: backend · Язык: Python · Категория: Технические вопросы
Компании: Стилсофт
Стек: Python
> Пример ответа
Короткий ответ
Чистый SQL в Django выполняется через django.db.connection - объект соединения с БД. Для одного запроса используется connection.cursor() и метод execute(), для нескольких - executemany(). Результат читается через fetchone(), fetchall() или fetchmany(). Важно: после выполнения запроса, изменяющего данные, нужно вызвать transaction.commit() или использовать with transaction.atomic(). Для raw SQL по моделям есть Model.objects.raw().
Подробное объяснение
connection - это прокси к активному соединению с базой данных, настроенному в settings.DATABASES. Доступ к нему - через from django.db import connection. Курсор создаётся вызовом connection.cursor(), после чего можно выполнять SQL.
Основные методы курсора:
execute(sql, params=None)- выполняет один запрос. Параметры передаются как список или кортеж, Django сам подставит их с экранированием (через%sдля большинства БД,%для SQLite).executemany(sql, param_list)- выполняет один запрос многократно с разными параметрами.fetchone(),fetchall(),fetchmany(size)- чтение результатов.rowcount- количество затронутых строк (для INSERT/UPDATE/DELETE).
Важные нюансы:
- Параметры нельзя подставлять через f-строки или конкатенацию - это риск SQL-инъекций. Только через
%sв SQL и список параметров. - Для запросов на запись (INSERT, UPDATE, DELETE) нужно управлять транзакцией. По умолчанию Django в autocommit-режиме, но если используется
ATOMIC_REQUESTSили ручнойtransaction.atomic(), коммит произойдёт при выходе из блока. - Для чтения данных из моделей лучше использовать
Model.objects.raw('SELECT ...')- он возвращает QuerySet с инстансами моделей и поддерживает ленивую загрузку. - Если нужно выполнить SQL в другом соединении (например, для реплики), используется
connections['replica'].cursor().
На практике
Типичные сценарии:
- Сложные агрегации, которые не выражаются через ORM (оконные функции, CTE, специфичный синтаксис БД).
- Массовые вставки или обновления, где ORM слишком медленный.
- Вызов хранимых процедур.
- Миграции данных или разовые скрипты.
Рекомендации:
- Всегда использовать параметризацию - даже для констант.
- Для чтения использовать
fetchall()с осторожностью: при больших результатах -fetchmany()или итерация по курсору. - Обернуть запросы на запись в
transaction.atomic()- это гарантирует откат при ошибке. - После
executeдля SELECT курсор можно итерировать напрямую:for row in cursor:. - Для отладки можно включить
connection.queries(вDEBUG=True), но это память, поэтому в проде не использовать.
Пример кода
PYTHONfrom django.db import connection, transaction# Простой SELECTwith connection.cursor() as cursor:cursor.execute("SELECT id, name FROM users WHERE age > %s", [18])rows = cursor.fetchall()for row in rows:print(row[0], row[1])# INSERT с транзакциейwith transaction.atomic():with connection.cursor() as cursor:cursor.execute("INSERT INTO orders (user_id, total) VALUES (%s, %s)",[user_id, total])order_id = cursor.lastrowid# executemany для массовой вставкиdata = [(1, 100), (2, 200), (3, 300)]with connection.cursor() as cursor:cursor.executemany("INSERT INTO order_items (order_id, price) VALUES (%s, %s)",data)# raw() для моделейfrom myapp.models import Userusers = User.objects.raw("SELECT * FROM myapp_user WHERE age > %s", [18])for user in users:print(user.name)
Как отвечать на собеседовании
Начни с connection.cursor(), затем покажи параметризацию и работу с транзакциями. Упомяни raw() как альтернативу для моделей. Подчеркни безопасность: никогда не подставлять значения напрямую в строку SQL. Если спросят про производительность - скажи, что executemany эффективнее цикла из execute, но для очень больших объёмов лучше использовать bulk-операции ORM или COPY (для PostgreSQL). Добавь, что для чтения больших результатов стоит использовать fetchmany или итерацию по курсору, чтобы не держать всё в памяти.
Что проверяет интервьюер
- Понимание, что
connection- это не глобальный синглтон, а прокси к текущему соединению. - Знание разницы между
executeиexecutemany. - Умение работать с транзакциями: когда нужен явный commit, когда
atomic. - Осознание рисков SQL-инъекций и правильной параметризации.
- Понимание, когда чистый SQL оправдан, а когда лучше ORM.
- Знание
raw()и его ограничений (например, нельзя использовать для JOIN с другими моделями без доп. полей).
Типичные ошибки
- Конкатенация строк в SQL:
f"SELECT * FROM users WHERE id = {user_id}"- критическая уязвимость. - Забывают про
transaction.atomic()для запросов на запись - при ошибке данные частично изменятся. - Используют
fetchall()для огромных выборок - память переполняется. - Путают
%sс?- для разных БД синтаксис плейсхолдеров разный (Django сам подставляет, но в raw SQL нужно писать%s). - Вызывают
cursor.executeбезwith- курсор не закрывается, соединение не возвращается в пул. - Думают, что
raw()возвращает обычный QuerySet - на самом деле он не поддерживаетfilter(),order_by()и другие методы ORM.
> Похожие задачи по Python
Для чего используются миксины в Django
На каком этапе лучше проверять токен в Django
Сколько SQL-запросов выполняется при использовании join в Django ORM
Работаете ли вы с Python и Go
> Похожие задачи по backend
Для чего используются миксины в Django
На каком этапе лучше проверять токен в Django
Сколько SQL-запросов выполняется при использовании join в Django ORM
Зачем приводить координату к float при делении?
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью