Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Транзакции Python: Django ORM

До сих пор нам встречался только код, работающий с одной сущностью за раз. В тех же примерах, когда создавались или изменялись несколько сущностей одновременно, никак не учитывалась ситуация, при которой посреди процесса обработки возникает ошибка и часть данных успевает перейти в новое состояние, а оставшиеся сущности остаются в старом. В таких случаях говорят: "нарушена консистентность".

В курсе по основам реляционных баз данных вы уже встречались с понятием транзакций и набором требований к транзакционным системам под названием ACID. Большинство СУБД, с которыми Django умеет работать, этим требованиям в той или иной форме соответствуют, а Django ORM предоставляет средства для управления транзакциями.

Тут стоит сразу отметить, что по умолчанию ORM использует режим "autocommit" и все запросы сразу применяются к БД. Об этой особенности стоит помнить и явно использовать транзакции в тех местах, где консистентность может быть нарушена.

Использование atomic

Для того, чтобы пометить фрагмент кода, как относящийся к одной транзакции, обычно используют менеджер контекста atomic():

from django.db import transaction


with transaction.atomic():
   # весь код в этом блоке выполнится в рамках транзакции
   do_more_stuff()

Умение менеджера контекста реагировать на ошибки, возникающие в его теле, здесь приходится весьма кстати. В результате при первой же ошибке транзакция отменяется.

Разработчики на Django часто хотят выполнить в транзакции весь код какой-либо view. При этом atomic() используют как декоратор:

from django.db import transaction


@transaction.atomic
def viewfunc(request):
    # Вся view работает в рамках транзакции
    do_stuff()

В модуле django.db.transaction присутствуют две функции: commit() и rollback(). Вызов первой применяет накопленные в рамках транзакции изменения к базе данных. Вызов второй отбрасывает эти изменения, как будто транзакции и вовсе не было. Когда atomic() ловит ошибки, выполняется rollback(), а если же выполнение обёрнутого кода завершилось успешно, то делается commit(). Поэтому думать об использовании этих функций обычно не приходится. Полезны эти функции могут быть тогда, когда в рамках блока менеджера контекста без возникновения ошибки становится понятно, что транзакция должна быть завершена так или иначе.

Точки сохранения

Параллельно с транзакциями существуют ещё и точки сохранения (savepoints). Они обычно используются в рамках транзакции и отмечают места, в которых текущее состояние считается консистентным. После создания точки сохранения, к запомненному там состоянию можно вернуться и отбросить таким образом изменения, произошедшие после создания savepoint. Точки сохранения могут быть созданы внутри транзакции и откат к некоторой savepoint не приводит к откату всей транзакции. Можно воспринимать savepoints как некий аналог Undo в редакторе, тогда как транзакция скорее похожа на сохранение всего файла или выход из редактора без сохранения.

Функция savepoint() из модуля django.db.transaction создаёт точку сохранения, возвращая её идентификатор ("savepoint id" или "sid").

Функция savepoint_commit(sid) сохраняет изменения, произошедшие с момента создания соответствующей savepoint. А функция savepoint_rollback(sid) откатывает изменения к тому состоянию, которые имела база на момент создания соответствующей savepoint. Тот факт, что при вызове упомянутых двух функций указывается "sid", говорит о том, что откатывать изменения можно к любой из ранее созданных точек сохранения (в пределах транзакции).

Вложенные транзакции

Если внутри кода, уже обёрнутого в вызов atomic(), в том или ином виде будет использован ещё один вызов atomic(), то ORM создаст точку сохранения, вместо ещё одной транзакции. Откат таких "вложенных транзакций" не откатывает внешнюю транзакцию.

Ограничения

Транзакции поддерживаются абсолютным большинством СУБД, среди тех, с которыми Django умеет работать. Но этого же нельзя сказать о savepoints. Если конкретная СУБД в принципе не поддерживает точки сохранения, то любой код, который их использует, будет сразу вносить изменения в БД, а откат работать перестанет. И если при явном использовании savepoint() и прочих специфичных функций программист обычно знает о том, что делает, то использование вложенных вызовов atomic() может неприятно удивить, если забыть об отсутствии savepoints в конкретной СУБД.

Из сказанного выше стоит сделать следующий вывод: один уровень транзакций работает везде и всегда (практически), а все виды вложенности требуют внимательного отслеживания того, с какими СУБД будет работать проект.


Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты.

Ошибки, сложный материал, вопросы >
Нашли опечатку или неточность?

Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.

Что-то не получается или материал кажется сложным?

Загляните в раздел «Обсуждение»:

  • задайте вопрос. Вы быстрее справитесь с трудностями и прокачаете навык постановки правильных вопросов, что пригодится и в учёбе, и в работе программистом;
  • расскажите о своих впечатлениях. Если курс слишком сложный, подробный отзыв поможет нам сделать его лучше;
  • изучите вопросы других учеников и ответы на них. Это база знаний, которой можно и нужно пользоваться.
Об обучении на Хекслете

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
900
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.

  • 120 курсов, 2000+ часов теории
  • 900 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы соглашаетесь c «Политикой конфиденциальности» и «Условиями оказания услуг»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы

С нуля до разработчика. Возвращаем деньги, если не удалось найти работу.

Иконка программы Python-разработчик
Профессия
Разработка веб-приложений на Django
1 декабря 8 месяцев

Используйте Хекслет по максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы соглашаетесь c «Политикой конфиденциальности» и «Условиями оказания услуг»

Изображение Тото

Задавайте вопросы, если хотите обсудить теорию или упражнения. Команда поддержки Хекслета и опытные участники сообщества помогут найти ответы и решить задачу