如何在 Django 中使用带有"async"函数的事务?



async def call_test(request):调用async def test():时,如下所示(我使用Django==3.1.7)):

async def test():
for _ in range(0, 3):
print("Test")
async def call_test(request):
await test() # Here
return HttpResponse("Call_test")

在控制台显示正确的结果没有错误:

Test
Test
Test

但是,当我把@transaction.atomic()放在async def test():上时,如下所示:

@transaction.atomic # Here
async def test():
for _ in range(0, 3):
print("Test")
# ...

出现以下错误:

django.core.exceptions。SynchronousOnlyOperation:你不能从异步上下文中调用它——使用线程或sync_to_async。

所以,我把@sync_to_async放在@transaction.atomic()下面,如下所示:
@transaction.atomic
@sync_to_async # Here
async def test():
for _ in range(0, 3):
print("Test")
# ...

但是,出现了下面相同的错误:

django.core.exceptions。SynchronousOnlyOperation:你不能调用从异步上下文中-使用线程或sync_to_async。

所以,我把@sync_to_async放在@transaction.atomic()上,如下所示:

@sync_to_async # Here
@transaction.atomic
async def test():
for _ in range(0, 3):
print("Test")
# ...

但是,出现以下错误:

RuntimeWarning:协程'test'从未等待过handle = None #发生异常时需要打破循环。RuntimeWarning:启用tracemalloc以获取对象分配跟踪

那么,我如何在Django中使用async函数?

我也遇到过类似的问题。不同之处在于,我需要使用原子事务作为异步上下文管理器。看完后,很少有人写在Django4.2.4的当前版本中这是不可能的。开始自己研究代码库。

我注意到的第一件事,transaction.atomic()不实现逻辑本身。它只是返回Atomic上下文装饰器。这个装饰器有3个需要处理的重要方法:

  • __init__atomic()传递参数给它,所以我们必须处理它们。
  • __enter____exit__对于上下文管理器的工作很重要。
class Atomic(ContextDecorator):
...
def __init__(self, using, savepoint, durable):
...
def __enter__(self):
...
def __exit__(self, exc_type, exc_value, traceback):
...
def atomic(using=None, savepoint=True, durable=False):
# Bare decorator: @atomic -- although the first argument is called
# `using`, it's actually the function being decorated.
if callable(using):
return Atomic(DEFAULT_DB_ALIAS, savepoint, durable)(using)
# Decorator: @atomic(...) or context manager: with atomic(...): ...
else:
return Atomic(using, savepoint, durable)

异步上下文管理器工作,我们需要实现__aenter____aexit__。考虑到这一点,我想出了一个可行的解决方案:

from django.db.transaction import Atomic
from asgiref.sync import sync_to_async
class AsyncAtomicContextManager(Atomic):
def __init__(self, using=None, savepoint=True, durable=False):
super().__init__(using, savepoint, durable)
async def __aenter__(self):
await sync_to_async(super().__enter__)()
return self
async def __aexit__(self, exc_type, exc_value, traceback):
await sync_to_async(super().__exit__)(exc_type, exc_value, traceback)

可以用作标准的异步上下文管理器:

async def test():
async with AsyncAtomicContextManager():
...

然后你可以这样做:

def aatomic(fun, *args, **kwargs):
async def wrapper():
async with AsyncAtomicContextManager():
await fun(*args, **kwargs)
return wrapper
@aatomic
async def test():
...

我发现Django 4.1的文档如下:

事务还不能在异步模式下工作。如果您有一段代码需要事务行为,我们建议您将这段代码编写为单个同步函数,并使用sync_to_async()调用它。

因此,@transaction.atomic()不能与async函数在order版本Django 3.1.7中使用。。

最新更新