0 added
0 removed
Original
2026-01-01
Modified
2026-02-26
1
<p>До сих пор нам встречался только код, работающий с одной сущностью за раз. В тех же примерах, когда создавались или изменялись несколько сущностей одновременно, никак не учитывалась ситуация, при которой посреди процесса обработки возникает ошибка и часть данных успевает перейти в новое состояние, а оставшиеся сущности остаются в старом. В таких случаях говорят: "нарушена консистентность".</p>
1
<p>До сих пор нам встречался только код, работающий с одной сущностью за раз. В тех же примерах, когда создавались или изменялись несколько сущностей одновременно, никак не учитывалась ситуация, при которой посреди процесса обработки возникает ошибка и часть данных успевает перейти в новое состояние, а оставшиеся сущности остаются в старом. В таких случаях говорят: "нарушена консистентность".</p>
2
<p>В курсе по<a>основам реляционных баз данных</a>вы уже встречались с понятием<em>транзакций</em>и набором требований к транзакционным системам под названием ACID. Большинство СУБД, с которыми Django умеет работать, этим требованиям в той или иной форме соответствуют, а Django ORM предоставляет средства для управления транзакциями.</p>
2
<p>В курсе по<a>основам реляционных баз данных</a>вы уже встречались с понятием<em>транзакций</em>и набором требований к транзакционным системам под названием ACID. Большинство СУБД, с которыми Django умеет работать, этим требованиям в той или иной форме соответствуют, а Django ORM предоставляет средства для управления транзакциями.</p>
3
<p>Тут стоит сразу отметить, что по умолчанию ORM использует режим "autocommit" и все запросы<em>сразу применяются к БД</em>. Об этой особенности стоит помнить и явно использовать транзакции в тех местах, где консистентность может быть нарушена.</p>
3
<p>Тут стоит сразу отметить, что по умолчанию ORM использует режим "autocommit" и все запросы<em>сразу применяются к БД</em>. Об этой особенности стоит помнить и явно использовать транзакции в тех местах, где консистентность может быть нарушена.</p>
4
<h2>Использование atomic</h2>
4
<h2>Использование atomic</h2>
5
<p>Для того, чтобы пометить фрагмент кода, как относящийся к одной транзакции, обычно используют<em>менеджер контекста</em><a>atomic()</a>:</p>
5
<p>Для того, чтобы пометить фрагмент кода, как относящийся к одной транзакции, обычно используют<em>менеджер контекста</em><a>atomic()</a>:</p>
6
<p>Умение менеджера контекста реагировать на ошибки, возникающие в его теле, здесь приходится весьма кстати. В результате при первой же ошибке вся транзакция отменяется. Так мы можем выполнять опасные операции, защищая базу данных от неконсистентности.</p>
6
<p>Умение менеджера контекста реагировать на ошибки, возникающие в его теле, здесь приходится весьма кстати. В результате при первой же ошибке вся транзакция отменяется. Так мы можем выполнять опасные операции, защищая базу данных от неконсистентности.</p>
7
<p>Разработчики на Django часто хотят выполнить в транзакции весь код какой-либо view. При этом atomic() используют как декоратор:</p>
7
<p>Разработчики на Django часто хотят выполнить в транзакции весь код какой-либо view. При этом atomic() используют как декоратор:</p>
8
<p>В модуле django.db.transaction присутствуют две функции: commit() и rollback(). Вызов первой применяет накопленные в рамках транзакции изменения к базе данных. Вызов второй отбрасывает эти изменения, как будто транзакции и вовсе не было. Когда atomic() ловит ошибки, выполняется rollback(), а если же выполнение обёрнутого кода завершилось успешно, то делается commit(). Поэтому думать об использовании этих функций обычно не приходится. Полезны эти функции могут быть тогда, когда в рамках блока менеджера контекста без возникновения ошибки становится понятно, что транзакция должна быть завершена так или иначе:</p>
8
<p>В модуле django.db.transaction присутствуют две функции: commit() и rollback(). Вызов первой применяет накопленные в рамках транзакции изменения к базе данных. Вызов второй отбрасывает эти изменения, как будто транзакции и вовсе не было. Когда atomic() ловит ошибки, выполняется rollback(), а если же выполнение обёрнутого кода завершилось успешно, то делается commit(). Поэтому думать об использовании этих функций обычно не приходится. Полезны эти функции могут быть тогда, когда в рамках блока менеджера контекста без возникновения ошибки становится понятно, что транзакция должна быть завершена так или иначе:</p>
9
<h2>Точки сохранения</h2>
9
<h2>Точки сохранения</h2>
10
<p>Параллельно с транзакциями существуют ещё и<em>точки сохранения</em>(<em>savepoints</em>). Они обычно используются в рамках транзакции и отмечают места, в которых текущее состояние считается консистентным. После создания точки сохранения, к запомненному там состоянию можно вернуться и отбросить таким образом изменения, произошедшие после создания savepoint. Точки сохранения могут быть созданы внутри транзакции и откат к некоторой savepoint не приводит к откату всей транзакции. Можно воспринимать savepoints как некий аналог Undo в редакторе, тогда как транзакция скорее похожа на сохранение всего файла или выход из редактора без сохранения.</p>
10
<p>Параллельно с транзакциями существуют ещё и<em>точки сохранения</em>(<em>savepoints</em>). Они обычно используются в рамках транзакции и отмечают места, в которых текущее состояние считается консистентным. После создания точки сохранения, к запомненному там состоянию можно вернуться и отбросить таким образом изменения, произошедшие после создания savepoint. Точки сохранения могут быть созданы внутри транзакции и откат к некоторой savepoint не приводит к откату всей транзакции. Можно воспринимать savepoints как некий аналог Undo в редакторе, тогда как транзакция скорее похожа на сохранение всего файла или выход из редактора без сохранения.</p>
11
<p>Функция savepoint() из модуля django.db.transaction создает точку сохранения, возвращая её идентификатор ("savepoint id" или "sid").</p>
11
<p>Функция savepoint() из модуля django.db.transaction создает точку сохранения, возвращая её идентификатор ("savepoint id" или "sid").</p>
12
<p>Функция savepoint_commit(sid) сохраняет изменения, произошедшие с момента создания соответствующей savepoint. А функция savepoint_rollback(sid) откатывает изменения к тому состоянию, которые имела база на момент создания соответствующей savepoint. Тот факт, что при вызове упомянутых двух функций указывается "sid", говорит о том, что откатывать изменения можно<em>к любой</em>из ранее созданных точек сохранения (в пределах транзакции).</p>
12
<p>Функция savepoint_commit(sid) сохраняет изменения, произошедшие с момента создания соответствующей savepoint. А функция savepoint_rollback(sid) откатывает изменения к тому состоянию, которые имела база на момент создания соответствующей savepoint. Тот факт, что при вызове упомянутых двух функций указывается "sid", говорит о том, что откатывать изменения можно<em>к любой</em>из ранее созданных точек сохранения (в пределах транзакции).</p>
13
<h2>Вложенные транзакции</h2>
13
<h2>Вложенные транзакции</h2>
14
<p>Если внутри кода, уже обёрнутого в вызов atomic(), в том или ином виде будет использован ещё один вызов atomic(), то ORM создаст точку сохранения, вместо ещё одной транзакции. Откат таких "вложенных транзакций" не откатывает внешнюю транзакцию.</p>
14
<p>Если внутри кода, уже обёрнутого в вызов atomic(), в том или ином виде будет использован ещё один вызов atomic(), то ORM создаст точку сохранения, вместо ещё одной транзакции. Откат таких "вложенных транзакций" не откатывает внешнюю транзакцию.</p>
15
<h2>Ограничения</h2>
15
<h2>Ограничения</h2>
16
<p>Транзакции поддерживаются абсолютным большинством СУБД, среди тех, с которыми Django умеет работать. Но этого же нельзя сказать о savepoints. Если конкретная СУБД в принципе не поддерживает точки сохранения, то любой код, который их использует, будет сразу вносить изменения в БД, а откат работать перестанет. И если при явном использовании savepoint() и прочих специфичных функций программист обычно знает о том, что делает, то использование вложенных вызовов atomic() может неприятно удивить, если забыть об отсутствии savepoints в конкретной СУБД.</p>
16
<p>Транзакции поддерживаются абсолютным большинством СУБД, среди тех, с которыми Django умеет работать. Но этого же нельзя сказать о savepoints. Если конкретная СУБД в принципе не поддерживает точки сохранения, то любой код, который их использует, будет сразу вносить изменения в БД, а откат работать перестанет. И если при явном использовании savepoint() и прочих специфичных функций программист обычно знает о том, что делает, то использование вложенных вызовов atomic() может неприятно удивить, если забыть об отсутствии savepoints в конкретной СУБД.</p>
17
<p>Из сказанного выше стоит сделать следующий вывод: один уровень транзакций работает везде и всегда (практически), а все виды вложенности требуют внимательного отслеживания того, с какими СУБД будет работать проект.</p>
17
<p>Из сказанного выше стоит сделать следующий вывод: один уровень транзакций работает везде и всегда (практически), а все виды вложенности требуют внимательного отслеживания того, с какими СУБД будет работать проект.</p>