做DRF项目的时候遇到了一个问题:一次请求,要同时在数据库里的三张表里创建字段,且三个字段存在相互依赖关系(B的字段里有A的pk键值),且其中任何一个字段校验失败,三张表都要撤回写入。折腾了老半天,在学长提醒用Django事务去解决的时候,才意识的这个操作应该要符合原子性

数据库事务

看DJango文档

https://docs.djangoproject.com/zh-hans/3.2/topics/db/transactions/

image.png

完美契合需求

Django 提供了一个 API 控制数据库事务。

  • atomic(using=None, savepoint=True, durable=False)

    原子性是数据库事务的定义属性。 atomic 允许创建代码块来保证数据库的原子性。如果代码块成功创建,这个变动会提交到数据库。如果有异常,变动会回滚。

要避免在 atomic 内部捕捉异常!

当存在 atomic 块时, Django 查看它是否正常退出或存在异常来决定是提交还是正常回滚。如果你在 atomic 内部捕捉并且处理异常,你可以对 Django 隐藏问题代码。这会导致一些意外的行为。

打开事务会对数据库服务器有性能成本。尽量减少这种开销,要保持事务尽可能简短

提交后

哎,当时没看到,使用On_commit的话,代码效率还可以更高

image.png

但貌似这样的话,就不好撤销更改了

如果使用块中有异常抛出,所有在事务内的操作都会被撤销

保存点在事务中是标记物,它可以使得回滚部分乌市(机器翻译错误?),而不是所有事务。 SQLite, PostgreSQL, Oracle, 和 MySQL (当使用 InnoDB 存储引擎) 后端提供了保存点

  • savepoint(using=None)

    创建新的保存点。这标志着事务中已知处于“良好”状态的一个点。返回保存点ID (sid) 。

  • savepoint_commit(sid, using=None)

    释放保存点 sid 。自保存点被创建依赖执行的更改成为事务的一部分。

使用上述的两条就可以完成任务了,是不是很简单

我自己的实现:

https://github.com/wheel-w/bk-c-course/pull/40

ORM还是强,毕竟不同数据库保存点的使用都不一样,但ORM全部都统一了起来

附录

Django 中事务的使用

django中事务的使用