4๐
I had a similar problem. The difference was, that I needed to use an atomic transaction as an asynchronous context manager. After reading, that few people wrote it is impossible in the current version of Django 4.2.4
. Started to investigate the code base myself.
The first thing I noticed, transaction.atomic()
does not implement logic itself. It is just returning Atomic
context decorator.
This decorator has 3 methods that are important to handle:
__init__
atomic() passes arguments to it, so we have to handle them.__enter__
and__exit__
which are important for context manager to work.
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)
For asynchronous context managers to work, we need to implement __aenter__
and __aexit__
. With this in mind, I came up with working solution:
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)
Which can be used as a standard async context manager:
async def test():
async with AsyncAtomicContextManager():
...
Then you can make a decorator from this:
def aatomic(fun, *args, **kwargs):
async def wrapper():
async with AsyncAtomicContextManager():
await fun(*args, **kwargs)
return wrapper
@aatomic
async def test():
...
- [Django]-Psycopg2.errors.ActiveSqlTransaction: CREATE DATABASE cannot run inside a transaction block
2๐
I found the documentation of Django 4.1 says below:
Transactions do not yet work in async mode. If you have a piece of code that needs transactions behavior, we recommend you write that piece as a single synchronous function and call it using sync_to_async().
So, @transaction.atomic()
cannot be used with async
functions in the order version Django 3.1.7 as well.
- [Django]-Django: Password protect photo url's?
- [Django]-Using Constants in Settings.py
- [Django]-Django cache_page โ prepopulate/pre-cache