pytest-django默认不回滚事务,而是清空整个测试数据库;启用事务级回滚需显式配置--transactional-db或使用@pytest.mark.django_db(transaction=true)标记测试函数或类。

pytest-django 默认是否回滚数据库事务
默认不回滚,而是用 TransactionTestCase 语义——每次测试后清空整个测试数据库(通过 TRUNCATE 或重建表),开销大、速度慢。真正意义上的“事务级回滚”需要显式启用 --transactional-db 或配置 django_db_use_transaction fixture。
启用事务级回滚的两种可靠方式
必须满足两个前提:Django 数据库后端支持保存点(sqlite3、postgresql 支持;mysql 在 InnoDB 下支持,但需确认 autocommit=False);且测试函数或类明确声明依赖事务能力。
- 方式一:命令行启动时加
--transactional-db,所有测试自动使用事务 +rollback,无需改代码 - 方式二:在测试函数上加
@pytest.mark.django_db(transaction=True),仅对该测试启用事务回滚 - 方式三:在测试类上加
@pytest.mark.django_db(transaction=True),类内所有方法共享同一事务并在末尾回滚
注意:transaction=True 会禁用 pytest-django 的默认数据库重置逻辑,改用 savepoint + rollback,因此不能和 reset_sequences=True 同时使用(会报错 DatabaseError: cannot truncate a table referenced in a foreign key constraint)。
为什么 test_foo() 调用了 savepoint 但数据还在
常见错因是忘了标记 @pytest.mark.django_db,或标记了但没加 transaction=True。pytest-django 对未标记的测试完全跳过数据库初始化,此时 models.MyModel.objects.create() 实际写入的是开发库(极危险!)或触发 RuntimeError: Database access not allowed(取决于 django_db_blocker 配置)。
正确写法示例:
@pytest.mark.django_db(transaction=True)
def test_user_created_then_gone():
from myapp.models import User
User.objects.create(username="test")
assert User.objects.count() == 1
# 测试结束自动 rollback,下个测试看不到该用户
fixture 内部创建数据时的事务陷阱
自定义 fixture(如 @pytest.fixture)里调用 ORM 不会自动绑定到测试事务,除非显式接收 django_db_setup 或 db fixture,且测试本身启用了 transaction=True。否则 fixture 创建的数据会滞留,污染后续测试。
- 安全做法:把测试数据创建逻辑直接放在测试函数内,或封装为带
django_db标记的 fixture,并在调用处确保测试已启用事务 - 避免在
scope="session"或scope="module"fixture 中写入数据库——它们脱离单个测试事务生命周期 - 若必须复用数据,用
@pytest.fixture(autouse=True)+transaction=True组合,但要清楚它会在每个测试前建、后删
事务回滚不是魔法,它只作用于当前测试函数所绑定的那个数据库连接上的 savepoint;跨线程、跨进程、手动 connection.commit() 都会破坏它。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











