Как выполняется чистый SQL в Django, например через execute
Что проверяют
Знаете ли вы оба пути к чистому SQL в Django и умеете ли передавать параметры так, чтобы не открыть SQL-инъекцию. Следом обычно спрашивают, когда raw() лучше курсора и как чистый SQL живёт с транзакциями.
Как отвечать
В Django два уровня:
Model.objects.raw(sql, params): запрос возвращает экземпляры модели.RawQuerySetленивый и выполняется при итерации. В выборке обязан быть первичный ключ, иначе Django выбросит исключение, а недостающие поля подгрузятся отдельными запросами при обращении.filter()и другие методы QuerySet к нему не цепляются;connection.cursor(): полный доступ, подходит дляUPDATE,DELETE, агрегатов без модели. Курсор берут вwith, чтобы он закрылся, и вызываютcursor.execute(sql, [value]). Для другой базы изDATABASESестьconnections["reporting"].
Параметры передаются вторым аргументом списком, плейсхолдер всегда %s, даже для SQLite с его родным ?: Django переводит сам. Кавычки вокруг %s не ставят, драйвер экранирует значение. Литеральный %, например в LIKE, при передаче параметров удваивают до %%.
fetchall() возвращает кортежи. Для словарей берут имена колонок из cursor.description и собирают dict(zip(columns, row)).
Django по умолчанию работает в автокоммите, каждый execute фиксируется сразу. Связанные запросы оборачивают в transaction.atomic(), курсор внутри участвует в той же транзакции. Промежуточный вариант между ORM и курсором: выражение RawSQL в annotate().
Где спотыкаются
Собирают запрос f-строкой: cursor.execute(f"... WHERE name = '{name}'"). Это прямая SQL-инъекция, и интервьюер ждёт именно этого поворота.
Пытаются параметризовать имя таблицы или колонки. Параметром передаются только значения, идентификаторы берут из белого списка и при необходимости экранируют через connection.ops.quote_name().
Забывают, что чистый SQL обходит логику моделей: не вызывается save(), не приходят сигналы pre_save и post_save, не срабатывает auto_now. Массовый UPDATE через курсор не тронет updated_at, если оно держится на auto_now.