1. Django事务处理
事务 ( Transaction ) : 是一种将多个数据库操作组合成一个单一工作单元的机制 .
如果事务中的所有操作都成功完成 , 则这些更改将永久保存到数据库中 .
如果事务中的某个操作失败 , 则整个事务将回滚到事务开始前的状态 , 所有的更改都不会被保存到数据库中 .
这对于保持数据的一致性和完整性非常重要 .
大多数数据库系统默认处于自动提交模式下 ,
这意味着每个单独的数据库操作 ( 如 : INSERT , UPDATE , DELETE ) 都会立即被提交到数据库 , 成为永久更改 .
在这种模式下 , 不需要显式地开始和提交事务 , 因为每个操作都被视为一个独立的事务 . 自增ID与回滚 :
当事务中包含插入操作 , 并且这些插入操作分配了自增ID , 如果事务被回滚 , 则这些已分配的自增ID会被撤销 , 它们会 '重置' 回之前的值 .
这意味着 , 事务回滚 , 数据库的自增计数器 ( 或称为序列 ) 也会回退到之前的状态 .
Django默认情况下会开启事务 , 但这里的 '开启事务' 指的是Django在模型 ( Model ) 层面对数据库操作的自动管理 .
Django的默认事务行为是自动提交 ( autocommit ) 模式 , 即每当执行数据库写操作 ( 如save ( ) , delete ( ) 等 ) 时 ,
Django会自动将这些操作提交到数据库 , 这与很多数据库默认的自动提交事务设置相似 . 这意味着 , 如果在一个视图中执行了 : MyModel . objects . create ( name = 'object 1' ) , 那么这条记录会立即被写入数据库 ,
并且不会因为这个视图中的后续操作失败而自动回滚 .
以下是一个完整的示例 , 展示了Django ORM操作数据库的默认行为 :
* 1. 在的Django应用的models . py文件中定义一个简单的模型 .
from django. db import models class MyModel ( models. Model) : name = models. CharField( max_length= 100 ) def __str__ ( self) : return self. name
* 2. 运行 : python manage . py makemigrations 和 python manage . py migrate 来创建数据库表 .
* 3. 可以在配置文件开启ORM日志查看对数据库的操作命令 .
LOGGING = { 'version' : 1 , 'disable_existing_loggers' : False , 'handlers' : { 'console' : { 'level' : 'DEBUG' , 'class' : 'logging.StreamHandler' , } , } , 'loggers' : { 'django.db.backends' : { 'handlers' : [ 'console' ] , 'propagate' : True , 'level' : 'DEBUG' , } , }
}
* 4. 在urls . py文件中添加一个URL配置 :
from django. urls import path
from index import views urlpatterns = [ path( '' , views. my_view, name= 'my_view' ) ,
]
* 5. 编写一个视图函数 , 创建一个数据实例 , 然后抛出异常 .
from django. shortcuts import HttpResponse
from . models import MyModel def my_view ( request) : try : MyModel. objects. create( name= 'object 1' ) raise ValueError( "数据库出错了!" ) except ValueError as e: return HttpResponse( f" { e} " )
* 6. 启动项目 , 访问 : 127.0 .0 .1 : 8000 , 查看index_mymodel表数据 .
( 0.016 )
INSERT INTO "index_mymodel" ( "name" ) VALUES ( 'object 1' ) RETURNING "index_mymodel" . "id" ;
args= ( 'object 1' , ) ; alias= default
SQL语句是用于向数据库中插入一条新记录 , 并希望立即获取该记录的自增主键 ( ID ) .
这个语句特别适用于支持RETURNING子句的数据库系统 , 如PostgreSQL .
在SQLite中 , 从版本 3.35 开始也支持RETURNING子句 , 但在早期版本中则不支持 .
MySQL则使用不同的方法 ( 如LAST_INSERT_ID ( ) 函数 ) 来获取最后插入记录的自增ID .
2. transaction模块
2.1 模块介绍
在Django框架中 , transaction模块是用于管理数据库事务的关键组件 .
事务是确保数据库操作原子性 , 一致性 , 隔离性和持久性 ( ACID特性 ) 的重要机制 .
Django通过transaction模块提供了灵活的事务管理功能 , 允许开发者在应用程序中根据需要手动控制事务的边界 . 注意 : 虽然Django ORM提供了对事务的支持,但具体的事务行为 ( 如隔离级别 , 持久性等 ) 会根据使用的数据库后端而有所不同 .
Django的ORM通过django . db . transaction模块提供了对数据库事务的细粒度控制 .
默认情况下 , Django在每个HTTP请求结束时自动提交事务 , 这意味着在单个视图函数或中间件中执行的所有数据库操作 ( 如模型的保存 , 删除等 ) 都将作为单个事务处理 , 除非在代码中显式地进行了事务管理 . transaction模块的主要功能和用法包括 :
* 1. 手动提交和回滚 . 尽管Django鼓励使用transaction . atomic ( ) 进行事务管理 . 但在某些特殊情况下 , 开发者可能需要手动控制事务的提交和回滚 . 这可以通过调用transaction . commit ( ) 和transaction . rollback ( ) 函数来实现 , 但通常不推荐这样做 , 因为这样做可能会破坏Django的自动事务管理机制 .
* 2. 手动事务管理 . 适用于需要更细粒度控制事务的场景 . Django提供了transaction . atomic ( ) 上下文管理器 . 当代码块被transaction . atomic ( ) 装饰或包裹时 , Django会确保该代码块内的所有数据库操作都在同一个事务中执行 . 如果代码块内发生异常 , 事务将自动回滚 ; 否则 , 事务将自动提交 .
* 3. 自动事务管理 . Django的TransactionMiddleware中间件默认在每个HTTP请求开始时自动开启一个事务 , 并在请求结束时提交该事务 . 如果请求处理过程中发生异常 , 则事务会被回滚 . 这种机制简化了大多数Web应用中的事务管理 . 在Django 1.9 及之前版本中是自动加载的 , 但在后续版被移除 . 不过可以通过数据库配置的ATOMIC_REQUESTS属性开启这个功能 . 如果想要为某个特定的视图消这种自动事务行为 , 可以使用 @ transaction . non_atomic_requests装饰视图 .
* 4. 事务钩子 : Django还提供transaction . on_commit ( ) 函数 , 允许开发者注册在事务提交时执行的回调函数 . 这对于在事务提交后执行某些清理工作或异步任务非常有用 .
* 5. 保存点 ( Savepoints ) : 在一些复杂的场景中 , 开发者可能需要在事务中设置保存点 ( avepoints ) , 以便在发生错误时能够回滚到事务中的某个特定点 , 而不是整个事务 . 然而 , Django的transaction模块并不直接支持保存点操作 , 这通常需要通过数据库底层的API来实现 .
2.2 手动提交或回滚
transaction . set_autocommit ( ) 方法 : 用于设置自动提交模式的开启与关闭 .
transaction . rollback ( ) 方法 : 用于回滚当前事务 . 注意 :
* Django的默认事务行为是自动提交 ( autocommit ) 模式 , 可以使用transaction . set_autocommit ( False ) 关闭自动提交模式 .
* 一旦提交事务 , 数据将永久保存到数据库中 , 并且不能通过transaction . rollback ( ) 来回滚这些更改 .
* Django请求结束transaction . set_autocommit会被设置为True , 恢复自动提交 .
以下是一个使用MyModel模型手动控制事务提交和回滚的例子 :
* 1. 修改views . py文件 , 在视图任何适当的地方 , 可以手动控制事务 :
from django. shortcuts import HttpResponse
from django. db import transaction
from . models import MyModeldef my_view ( request) : try : transaction. set_autocommit( False ) MyModel. objects. create( name= 'object 2' ) raise Exception( "未知错误!" ) except Exception as e: transaction. rollback( ) return HttpResponse( "由于错误, 事务回滚: {}" . format ( e) ) finally : transaction. set_autocommit( True )
* 2. 启动项目 , 访问 : 127.0 .0 .1 : 8000 , 查看index_mymodel表数据 .
( 0.015 )
INSERT INTO "index_mymodel" ( "name" ) VALUES ( 'object 2' ) RETURNING "index_mymodel" . "id" ;
args= ( 'object 2' , ) ; alias= default
( 0.000 )
ROLLBACK ; args= None; alias= default
2.3 手动事务管理
transaction . atomic ( ) 是Django ORM提供的一个上下文管理器 ( context manager ) .
它用于确保数据库操作的原子性 , 当将数据库操作 ( 如插入 , 更新 , 删除等 ) 包裹在transaction . atomic ( ) 块中时 ,
这些操作要么全部成功 , 要么在发生错误时全部回滚 , 从而保持数据的一致性 .
总结 : transaction . atomic块如果程序正常退出 , 则自动提交事务 , 否则回滚 . 手动事务控制的两种使用方式如下 :
from django. db import transactiondef my_view ( request) : try : with transaction. atomic( ) : pass except Exception as e: raise return render( request, 'my_template.html' )
from django. db import transaction @transaction. atomic
def my_view ( request) : return render( request, 'my_template.html' )
* 1. 修改views . py文件 , 使用transaction . atomic ( ) 在视图中定义一个事务的边界 , 在边界内创建一个模型实例 , 并在发生异常时自动回滚事务 .
from django. db import transaction
from . models import MyModeldef my_view ( request) : with transaction. atomic( ) : MyModel. objects. create( name= 'object 2' ) raise Exception( "测试事务回滚!" )
* 2. 启动项目 , 访问 : http : / / 127.0 .0 .1 : 8000 , 测试事务处理 .
( 0.016 )
INSERT INTO "index_mymodel" ( "name" ) VALUES ( 'object 2' ) RETURNING "index_mymodel" . "id" ;
args= ( 'object 2' , ) ; alias= default
( 0.000 )
ROLLBACK ; args= None; alias= default
隔离级别 : 数据库事务的隔离级别决定了事务之间如何相互影响 .
不同的隔离级别 ( 如 : 读未提交 , 读已提交 , 可重复读 , 串行化 ) 会影响你看到的数据状态 .
例如 , 在高隔离级别下 ( 如可重复读或串行化 ) , 当插入数据时 , 其他用户可能暂时看不到这些新插入的数据 , 直到当前事务提交 .
* 3. 如果想要优雅的停止程序不出现异常 , 可以使用try语句捕获transaction . atomic ( ) 块中的异常 .
from django. shortcuts import HttpResponse
from django. db import transaction
from . models import MyModeldef my_view ( request) : try : with transaction. atomic( ) : MyModel. objects. create( name= 'object 2' ) raise Exception( "测试事务回滚!" ) except Exception as e: return HttpResponse( f"出现错误, 原因: { e} " )
* 4. 启动项目 , 访问 : http : / / 127.0 .0 .1 : 8000 , transaction . atomic ( ) 块中的异常被捕获 .
( 0.000 ) I
NSERT INTO "index_mymodel" ( "name" ) VALUES ( 'object 2' ) RETURNING "index_mymodel" . "id" ;
args= ( 'object 2' , ) ; alias= default
( 0.000 )
ROLLBACK ; args= None; alias= default
2.4 注意事项
try . . . except与with transaction . atomic ( ) 语句块一起工作的详细解释 :
在with transaction . atomic ( ) : 语句块中 , 如果发生异常会导致Python退出该块 , 这时Django会自动回滚事务 .
如果try . . . except块定义在with transaction . atomic块中 , 而数据库操作实是在try块中执行的 .
如果try中捕获了异常并且没有重新抛出 , 那么事务将不会被回滚 , 因为从Python的角度来看 , 异常已经被except块 '处理' 了 .
* 1. 修改views . py , 代码如下 :
from django. db import transaction
from . models import MyModeldef my_view ( request) : with transaction. atomic( ) : try : MyModel. objects. create( name= 'object 2' ) raise Exception( "测试事务回滚!" ) except Exception as e: print ( f'异常信息 { e} ' )
* 2. 启动项目 , 访问 : http : / / 127.0 .0 .1 : 8000 , 测试事务处理 . 如果try中捕获了异常并且没有重新抛出 , 那么事务将不会被回滚 . transaction . atomic块没捕获到异常会在结束时会自动提交事务 .
( 0.000 )
INSERT INTO "index_mymodel" ( "name" ) VALUES ( 'object 2' ) RETURNING "index_mymodel" . "id" ;
args= ( 'object 2' , ) ; alias= default
( 0.000 )
COMMIT ; args= None; alias= default
* 3. 修改views . py , 在except子句中抛出异常 . 想要继续触发回滚 , 只能继续抛出异常 , 被with transaction . atomic ( ) 语句捕获异常 , 从而触发回滚 .
from django. db import transaction
from . models import MyModeldef my_view ( request) : with transaction. atomic( ) : try : MyModel. objects. create( name= 'object 3' ) raise Exception( "测试事务回滚!" ) except Exception as e: print ( f'异常信息 { e} ' ) raise Exception( e)
* 4. 启动项目 , 访问 : http : / / 127.0 .0 .1 : 8000 , 测试事务处理 . 如果不想程序报错 , 可在with transaction . atomic ( ) 块外层在套一个try , 让程序优雅的结束 .
( 0.000 ) INSERT INTO "index_mymodel" ( "name" ) VALUES ( 'object 3' ) RETURNING "index_mymodel" . "id" ;
args= ( 'object 3' , ) ; alias= default
( 0.000 ) ROLLBACK ; args= None; alias= default
2.5 自动事务管理
在Django中 , ATOMIC_REQUESTS ( 原子性请求 ) 是一个数据库设置选项 ,
它会在每个HTTP请求开始时自动开启一个数据库事务 , 并在请求结束时提交或回滚该事务 .
* 1. 在配置文件的添加ATOMIC_REQUESTS ( 原子性请求 ) 属性 :
DATABASES = { 'default' : { 'ENGINE' : 'django.db.backends.sqlite3' , 'NAME' : BASE_DIR / 'db.sqlite3' , 'ATOMIC_REQUESTS' : True }
}
* 2. 修改上面示例的视图函数 :
from index. models import MyModeldef my_view ( request) : MyModel. objects. create( name= 'object 3' ) raise Exception( "操作出错" )
* 3. 启动项目 , 访问 : http : / / 127.0 .0 .1 : 8000 , 测试事务处理 .
( 0.000 )
INSERT INTO "index_mymodel" ( "name" ) VALUES ( 'object 3' ) RETURNING "index_mymodel" . "id" ;
args= ( 'object 3' , ) ; alias= default
( 0.000 ) ROLLBACK ; args= None; alias= default
* 4. 如果想要为某个特定的视图消这种自动事务行为 , 可以使用 @ transaction . non_atomic_requests装饰视图 .
from index. models import MyModel
from django. db import transaction@transaction. non_atomic_requests
def my_view ( request) : MyModel. objects. create( name= 'object 4' ) raise Exception( "操作出错" )
* 5. 启动项目 , 访问 : http : / / 127.0 .0 .1 : 8000 , 测试事务处理 .
( 0.000 )
INSERT INTO "index_mymodel" ( "name" ) VALUES ( 'object 4' ) RETURNING "index_mymodel" . "id" ;
args= ( 'object 3' , ) ; alias= default
2.6 事务钩子
transaction . on_commit ( ) 是Django提供的一个非常有用的函数 ,
它允许注册一个回调函数 , 该回调函数将在当前数据库事务成功提交后执行 .
这对于在事务完成后执行清理工作 , 发送通知 , 触发异步任务等场景特别有用 .
下面是一个使用transaction . on_commit ( ) 的例子 :
* 1. 在视图或任何数据库操作函数中 , 可以使用transaction . on_commit ( ) 来注册一个任务 , 它会在事务提交后执行 :
from django. http import HttpResponse
from django. db import transaction
from . models import MyModeldef my_view ( request) : with transaction. atomic( ) : MyModel. objects. create( name= "object 5" ) @transaction. on_commit def send_email_after_commit ( ) : print ( "交易提交后发送的电子邮件." ) return HttpResponse( "记录已创建, 交易提交后将发送电子邮件." )
* 2. 启动项目 , 访问 : http : / / 127.0 .0 .1 : 8000 , 查看事务钩子 .
( 0.000 ) INSERT INTO "index_mymodel" ( "name" ) VALUES ( 'object 5' ) RETURNING "index_mymodel" . "id" ;
args= ( 'object 5' , ) ; alias= default
( 0.016 ) COMMIT ; args= None; alias= default
* 3. 修改代码 , 在with transaction . atomic ( ) 块中手动抛出异常 , 事务边界块捕获到异常后会执行回滚操作 .
from django. db import transaction
from . models import MyModeldef my_view ( request) : with transaction. atomic( ) : MyModel. objects. create( name= "object 6" ) @transaction. on_commit def send_email_after_commit ( ) : print ( "交易提交后发送的电子邮件." ) raise ValueError( "数据库出错了!" )
* 4. 重新启动项目 , 访问 : http : / / 127.0 .0 .1 : 8000 , 查看事务钩子的执行情况 . 在没有提交事务的时候不会被触发钩子函数 .
( 0.016 ) INSERT INTO "index_mymodel" ( "name" ) VALUES ( 'object 6' ) RETURNING "index_mymodel" . "id" ;
args= ( 'object 6' , ) ; alias= default
( 0.000 ) ROLLBACK ; args= None; alias= default
2.7 保存点
在Django的数据库事务管理中 , transaction . savepoint ( ) 是一个用于在事务中创建保存点的函数 .
保存点允许在事务中设置一个点 , 之后可以回滚到这个点 , 而不是回滚整个事务 .
这对于复杂的数据库操作非常有用 , 尤其是当需要在事务的某个特定部分失败时能够恢复状态 , 而不是放弃整个事务 .
下面是一个使用ransaction . savepoint的例子 , 演示保存点的使用 .
* 1. 修改views . py文件中使用transaction . atomic ( ) 装饰器来装饰一个视图函数 , 为其开启事务功能 . 在该视图函数中创建一个保存点 , 并创建两个模型实例 , 最后在发生异常时能够回滚事务 .
from django. shortcuts import HttpResponse
from index. models import MyModel
from django. db import transaction
@transaction. atomic
def my_view ( request) : sid = transaction. savepoint( ) try : MyModel. objects. create( name= 'object 6' ) raise Exception( "需要回滚到保存点" ) except Exception as e: transaction. savepoint_rollback( sid) print ( f'操作失败, 原因为: { e} ' ) return HttpResponse( '保存点测试!' )
* 2. 启动项目 , 访问 : http : / / 127.0 .0 .1 : 8000 , 查看保存点效果 .
( 0.000 ) BEGIN ; args= None; alias= default
( 0.000 ) SAVEPOINT "s5148_x1" ; args= None; alias= default
( 0.016 ) INSERT INTO "index_mymodel" ( "name" ) VALUES ( 'object 6' ) RETURNING "index_mymodel" . "id" ; . . .
( 0.000 ) ROLLBACK TO SAVEPOINT "s5148_x1" ; args= None; alias= default
( 0.000 ) COMMIT ; args= None; alias= default
注意事项 : 在一个全局事务的上下文中工作时 , 再使用 @ transaction . atomic装饰器来包裹特定的代码块 ,
Django的ORM会在这个 @ transaction . atomic块开始时自动创建一个保存点 ( savepoint ) . 不管代码块正常还是异常结果都是释放保存点提交事务 . . .
( 0.000 ) BEGIN ; args= None; alias= default
( 0.000 ) SAVEPOINT "s8456_x1" ; args= None; alias= default
( 0.000 ) INSERT INTO "index_mymodel" ( "name" ) VALUES ( 'object 6' ) RETURNING "index_mymodel" . "id" ; . . .
( 0.000 ) RELEASE SAVEPOINT "s8456_x1" ; args= None; alias= default
( 0.000 ) COMMIT ; args= None; alias= default
拿不到保存点 , 觉得好玩就去修改源码 , 想办法在视图中获取这个保存点 , , ,