如何在Django异步函数中正确使用transaction事务?
Django 3.1.7 异步函数中正确使用事务的方案
核心解决方案
由于Django 3.1.x的@transaction.atomic仅支持同步上下文,无法直接在异步函数中使用,正确做法是将需要事务包裹的逻辑写为同步函数,用sync_to_async包装后再在异步视图中调用。
代码示例
from django.db import transaction from asgiref.sync import sync_to_async from django.http import HttpResponse # 1. 定义带事务的同步函数 @transaction.atomic def test_sync(): for _ in range(3): print("Test") # 此处可添加数据库操作,例如: # MyModel.objects.create(name="sample") # 2. 将同步函数包装为异步可调用对象 test_async = sync_to_async(test_sync) # 3. 异步视图中调用包装后的函数 async def call_test(request): await test_async() return HttpResponse("Call_test")
也可使用装饰器链式写法(注意顺序:@sync_to_async必须放在@transaction.atomic上方):
@sync_to_async @transaction.atomic def test_sync(): for _ in range(3): print("Test") async def call_test(request): await test_sync() return HttpResponse("Call_test")
错误原因分析
直接给异步函数加
@transaction.atomic:@transaction.atomic内部依赖同步上下文专属API,在异步环境中调用会触发SynchronousOnlyOperation错误,这是Django对同步操作的安全限制。@sync_to_async放在@transaction.atomic下方(给异步函数加):
装饰器执行顺序为从下往上,相当于先给异步函数套事务装饰器,再转成同步函数——事务逻辑已在异步上下文执行,依然触发同步操作限制。@sync_to_async放在@transaction.atomic上方(给异步函数加):
相当于把异步函数转成同步可调用对象,但用await调用同步函数会误将其视为协程,导致"coroutine was never awaited"警告——同步函数无需await,且原函数为异步类型,转同步后调用方式完全错误。
额外说明
Django 4.0及以上版本提供了async transaction.atomic原生支持,但3.1.7属于旧版本,只能通过sync_to_async包裹同步事务函数的方式解决。
内容的提问来源于stack exchange,提问作者Super Kai - Kazuya Ito
相关产品推荐
相关产品推荐

