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
![image-20240803101134856](https://img-blog.csdnimg.cn/img_convert/08c7802d3b1c53d5ac07f25754271c83.png)
* 2. 运行: python manage.py makemigrations 和 python manage.py migrate 来创建数据库表.
![image-20240806090952823](https://img-blog.csdnimg.cn/img_convert/a63c2d89f8b51f46547720494b7e2e39.png)
* 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',
},
}
}
![image-20240806022128257](https://img-blog.csdnimg.cn/img_convert/142404b1efbde3d1080045a3dca7626d.png)
* 4. 在urls.py文件中添加一个URL配置:
from django.urls import path
from index import views
urlpatterns = [
path('', views.my_view, name='my_view'),
]
![image-20240806091114597](https://img-blog.csdnimg.cn/img_convert/1306473fdfb4b057ce17d35a2b2914d0.png)
* 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}")
![image-20240806084125662](https://img-blog.csdnimg.cn/img_convert/3088fab88c3c65fa90edb1a94f0082c5.png)
* 6. 启动项目, 访问: 127.0.0.1:8000 , 查看index_mymodel表数据.
![image-20240806092114651](https://img-blog.csdnimg.cn/img_convert/44b1d720816ec8348e2b4e302fe8fe4c.png)
(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 MyModel
def 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)
![image-20240806094822239](https://img-blog.csdnimg.cn/img_convert/f8f30f7f9b9485c213f8378e880a6320.png)
* 2. 启动项目, 访问: 127.0.0.1:8000 , 查看index_mymodel表数据.
![image-20240806094151956](https://img-blog.csdnimg.cn/img_convert/6bdead918a26570297acabe78b2b25fc.png)
(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 transaction
def 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 MyModel
def my_view(request):
with transaction.atomic():
MyModel.objects.create(name='object 2')
raise Exception("测试事务回滚!")
![image-20240806101310126](https://img-blog.csdnimg.cn/img_convert/4ae4c455111217851cef62226a728d2b.png)
* 2. 启动项目, 访问: http://127.0.0.1:8000 , 测试事务处理.
![image-20240806101016526](https://img-blog.csdnimg.cn/img_convert/259dd42e8f860e55c68715cb72a59c34.png)
(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 MyModel
def 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()块中的异常被捕获.
![image-20240806102121548](https://img-blog.csdnimg.cn/img_convert/7a68379c3dcfb7e7a7d1fc5577b9f6bc.png)
(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 MyModel
def my_view(request):
with transaction.atomic():
try:
MyModel.objects.create(name='object 2')
raise Exception("测试事务回滚!")
except Exception as e:
print(f'异常信息{e}')
![image-20240806104347773](https://img-blog.csdnimg.cn/img_convert/6ee257dcfa1a864e20b334e46e49bb29.png)
* 2. 启动项目, 访问: http://127.0.0.1:8000 , 测试事务处理.
如果try中捕获了异常并且没有重新抛出, 那么事务将不会被回滚.
transaction.atomic块没捕获到异常会在结束时会自动提交事务.
![image-20240806104526059](https://img-blog.csdnimg.cn/img_convert/50a6a7ba78b537fb2aff57a3107e8a99.png)
(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
![image-20240806103858408](https://img-blog.csdnimg.cn/img_convert/3b6b97bf895588c0899863889b5f518d.png)
* 3. 修改views.py, 在except子句中抛出异常.
想要继续触发回滚, 只能继续抛出异常, 被with transaction.atomic()语句捕获异常, 从而触发回滚.
from django.db import transaction
from .models import MyModel
def 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)
![image-20240806110656724](https://img-blog.csdnimg.cn/img_convert/87ef3f6bf4457493311fc5b8b659aa3d.png)
* 4. 启动项目, 访问: http://127.0.0.1:8000 , 测试事务处理.
如果不想程序报错, 可在with transaction.atomic()块外层在套一个try, 让程序优雅的结束.
![image-20240806110734432](https://img-blog.csdnimg.cn/img_convert/93da7d102bfee55e04e5e04d09153eae.png)
(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
}
}
![image-20240803123054004](https://img-blog.csdnimg.cn/img_convert/4e3f6362d1b9cda880733b985df941c2.png)
* 2. 修改上面示例的视图函数:
from index.models import MyModel
def my_view(request):
MyModel.objects.create(name='object 3')
raise Exception("操作出错")
![image-20240806111911432](https://img-blog.csdnimg.cn/img_convert/59cceb3127b3c893458e864856ff3aaf.png)
* 3. 启动项目, 访问: http://127.0.0.1:8000 , 测试事务处理.
![image-20240806113359798](https://img-blog.csdnimg.cn/img_convert/74fcb134a4ed864a5afdb1ff8b4cb4b2.png)
(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("操作出错")
![image-20240806113710755](https://img-blog.csdnimg.cn/img_convert/6bf9a82f8088bc3d03538f738ee5a262.png)
* 5. 启动项目, 访问: http://127.0.0.1:8000 , 测试事务处理.
![image-20240806113839748](https://img-blog.csdnimg.cn/img_convert/3128cafb81c8981671ad8e517f74568e.png)
(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 MyModel
def my_view(request):
with transaction.atomic():
MyModel.objects.create(name="object 5")
@transaction.on_commit
def send_email_after_commit():
print("交易提交后发送的电子邮件.")
return HttpResponse("记录已创建, 交易提交后将发送电子邮件.")
![image-20240806141456153](https://img-blog.csdnimg.cn/img_convert/8aa8dbccfff84e6ce76aa6b5c60397b2.png)
* 2. 启动项目, 访问: http://127.0.0.1:8000 , 查看事务钩子.
![image-20240806141319932](https://img-blog.csdnimg.cn/img_convert/4e2b49df36829ff4985c98d50ff08830.png)
(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 MyModel
def 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 , 查看事务钩子的执行情况.
在没有提交事务的时候不会被触发钩子函数.
![image-20240806143210345](https://img-blog.csdnimg.cn/img_convert/06fba614a82c4780dacdfec794617859.png)
(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 , 查看保存点效果.
![image-20240806145752976](https://img-blog.csdnimg.cn/img_convert/d4f72c292496ca92de72d93f271364a1.png)
(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).
不管代码块正常还是异常结果都是释放保存点提交事务...
![image-20240806150159827](https://img-blog.csdnimg.cn/img_convert/a7087a718af07437b79a6fcce0d74fd7.png)
(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
拿不到保存点, 觉得好玩就去修改源码, 想办法在视图中获取这个保存点,,,
![image-20240806152827877](https://img-blog.csdnimg.cn/img_convert/2d246cfa1c3630c013f0d24232663ea8.png)