# DEP 0009: Async-capable Django (Discussion about connection for #35629)

**URL:** <https://forum.djangoproject.com/t/dep-0009-async-capable-django-discussion-about-connection-for-35629/40824>\
**Category:** Async\
**Created:** [May 8, 2025, 12:16am UTC](https://forum.djangoproject.com/t/dep-0009-async-capable-django-discussion-about-connection-for-35629/40824 "2025-05-08T00:16:41Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![Arfey](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/arfey/32/32504_2.png) [@Arfey](https://forum.djangoproject.com/u/Arfey)\
**Post date:** [May 8, 2025, 12:16am UTC](https://forum.djangoproject.com/t/dep-0009-async-capable-django-discussion-about-connection-for-35629/40824/1 "2025-05-08T00:16:41Z")

</div>

**DEP 0009: Async-capable Django (Discussion about async connection for #35629)**

sources:

- original mr - [Fixed #35629 -- Added support for async database connections and cursors. by fcurella · Pull Request #18408 · django/django · GitHub](https://github.com/django/django/pull/18408)
- experimental mr - [represented alternative for AsyncConnectionHandler by Arfey · Pull Request #1 · Arfey/django · GitHub](https://github.com/Arfey/django/pull/1)

Hi there 🖐 During a code review, we’ve faced a pool of problematic topics which I think we need to discuss before going forward.

topics:

- Global connection
- Different connection classes

**Global connection**

[https://github.com/django/deps/blob/main/accepted/0009-async.rst](https://github.com/django/deps/blob/main/accepted/0009-async.rst)

In **DEP 0009, a** global connection is described as something we need to eliminate in the future and replace with an explicit `new_connections` context manager.

```python
async def get_authors(pattern):
    async with db.new_connections():
        return [
            author.name
            async for author in Authors.objects.all()
        ]

```

Each `new_connections` generates a new independent database connection. In the current implementation, this is the single way to create a new connection.

it solves problem:

- Add the ability to create an independent connection and, as a result, execute queries in parallel
- Add a pretty approach to creating and closing a connection (without call `close_old_connections`)

The main problem for me in this feature is that this is not an optional ability for performance optimisation, it’s a necessity that u have to use everywhere.

As a result, I don’t understand how to solve problems that are represented below

- How to use it during the request/response cycle?

Right now, if u need to fetch a user inside middleware, Django creates a new connection and after reusing it inside a view and logic functions. If we don’t need to fetch the user, then we create it inside the view. If we don’t need a connection inside a view, we will not create a connection at all.

With the current solution we can generate a separate connection for each place when we need it but this approach produce a lot of connection’s calls which obviously are not for free. Also, we can generate a middleware

```python
async def db(get_response):

    async def middleware(request):
        async with db.new_connections():
	          response = await get_response(request)

        return response

    return middleware

```

But we will create a new connection for each request (with the current implementation) even if we don’t require it. Also, we will have a performance penalty for the async api with this middleware due to `async_to_sync` for each request event if we don’t need an async connection here.

- How to write utility functions?

I have a function to create a notification.

```python
async def create_notification(user_id, msg):
    async with db.new_connections():
        await Notification.objects.create(user_id=user_id, msg=msg)

```

I use it everywhere. And at some moment, I got requirements to send a notification during the user creation process.

```python
async def create_user(name):
    async with transaction.async_atomic():
        user = await User.objects.create(name=name)

```

As u can see I need to have the same connection here and as result i need to remove `new_connections` from `create_notification`, and wrap this function everywhere with `new_connections`.

```python
async def create_notification(user_id, msg):
    await Notification.objects.create(user_id=user_id, msg=msg)

async def create_user(name):
    async with transaction.async_atomic():
        user = await User.objects.create(name=name)
        await create_notification(user.id, 'hello!')

```

If I need to use `create_user` inside another transaction, I need to do it over and over

**Solution:**

Keep the global connection variable with lazy initialization for async connection.

```python
# global
async_connections = AsyncConnectionHandler()

async def foo():
    connection = async_connections[DEFAULT_DB_ALIAS]

    async with connection.cursor() as cursor:
        res = await cursor.execute("select 1")
        return await res.fetchone()

```

It works well and doesn’t have problems described above (You can check it in my experimental MR). + signal handler with `close_old_connections`. I think, we will cover 90% use cases with this approach. For the rest of them, we can implement a context manager (but as an optional ability).

```python
async def foo():
    async with async_connections[DEFAULT_DB_ALIAS] as connection:
        async with connection.cursor() as cursor:
            res = await cursor.execute("select 1")
            return await res.fetchone()

```

We can add ` __enter__ / __exit__ ` for `AsyncConnectionHandler` and `ConnectionHandler` and remove `new_connections` at all (but it is just a detail)

What do u think? 🙂

**Different connection classes**

In the current implementation, async and sync connections are different and don’t have any relations (if u create a transaction inside async, u can’t see these changes inside a sync connection). But right now we have the same class, and if I create an async connection, I have access to the sync method (yes, with errors, but I have 😅).

As a result, we have a lot of a (aclose, aexecute, etc). I don’t see any sense in doing it. I prefer to follow the Python database API ([PEP 249 – Python Database API Specification v2.0 | peps.python.org](https://peps.python.org/pep-0249/#id19))

Maybe someone has an understanding of why this makes sense? (I tested different classes, and u can check it in experimental MR)

---

<div class="post-metadata">

**Author:** ![carltongibson](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/carltongibson/32/267_2.png) [@carltongibson](https://forum.djangoproject.com/u/carltongibson)\
**Post date:** [May 8, 2025, 7:16am UTC](https://forum.djangoproject.com/t/dep-0009-async-capable-django-discussion-about-connection-for-35629/40824/2 "2025-05-08T07:16:44Z")

</div>

Hi @Arfey — thanks for posting. I’ve seen your various comments, reviews, and suggestions on this topic. I don’t have an immediate response for you — I need to think it though — but wanted to reply now just to say that I will be looking through it all over the coming period.

Did you link to the discussion here on the PR, so that others there know to discuss here?

---

<div class="post-metadata">

**Author:** ![Arfey](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/arfey/32/32504_2.png) [@Arfey](https://forum.djangoproject.com/u/Arfey)\
**Post date:** [May 8, 2025, 9:16am UTC](https://forum.djangoproject.com/t/dep-0009-async-capable-django-discussion-about-connection-for-35629/40824/3 "2025-05-08T09:16:31Z")

</div>

Got it, thank you. I’ll add a link to the discussion.

If you have any further ideas (like your previous suggestion regarding `yield`), please feel free to share them. I’ll do my best to implement them quickly in the experimental branch. If everything works well, we can then apply the changes to the original merge request.

Also, if you notice any issues or potential weaknesses in the experimental branch, I would really appreciate your feedback. I’ll try to refine the solution accordingly.

---

<div class="post-metadata">

**Author:** ![Arfey](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/arfey/32/32504_2.png) [@Arfey](https://forum.djangoproject.com/u/Arfey)\
**Post date:** [June 10, 2025, 4:33pm UTC](https://forum.djangoproject.com/t/dep-0009-async-capable-django-discussion-about-connection-for-35629/40824/4 "2025-06-10T16:33:41Z")

</div>

Hi 🖐

I’m having trouble finding the right place to add tests for `AsyncDatabaseWrapper`. The existing tests for `DatabaseWrapper` cover only part of the functionality, and Postgres tests focus on that backend specifically.

To avoid duplicating test code, I think there should be a single place for async wrapper tests. Is there a recommended way to organize these tests?

Thanks for your help!

---

<div class="post-metadata">

**Author:** ![carltongibson](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/carltongibson/32/267_2.png) [@carltongibson](https://forum.djangoproject.com/u/carltongibson)\
**Post date:** [June 10, 2025, 7:01pm UTC](https://forum.djangoproject.com/t/dep-0009-async-capable-django-discussion-about-connection-for-35629/40824/5 "2025-06-10T19:01:36Z")

</div>

Hey @Arfey.

> is there a single place…?

I don’t think there is. There’s not going to be one right answer here. If I wanted to keep it obvious I’d probably start a new file, at least to begin. (We can always move them on review)

That’s all very high-level on my part. Without looking at what you’re doing exactly I can’t really say more. And I still didn’t get to it, having been occupied elsewhere 🤹‍♀️

---

<div class="post-metadata">

**Author:** ![Arfey](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/arfey/32/32504_2.png) [@Arfey](https://forum.djangoproject.com/u/Arfey)\
**Post date:** [June 18, 2025, 10:54pm UTC](https://forum.djangoproject.com/t/dep-0009-async-capable-django-discussion-about-connection-for-35629/40824/6 "2025-06-18T22:54:52Z")

</div>

FYI I’ve moved the work into this merge request.

> <https://github.com/django/django/pull/19576>
>
> \#### Trac ticket number
> 
> 
> \[ticket-35629\](https://code.djangoproject.com/ticke…t/35629)
> 
> \*\*Implement native asynchronous database connection:\*\*
> 
> \- Provide an abstract asynchronous connection handler
> 
> \> \`BaseAsyncConnectionHandler\` – Abstract base class that defines the core logic for managing asynchronous connections (any connection)
> 
> with the ability to create a completely new connection for parallel query execution
> 
> \`\`\`python
> async def load():
> async with connections.independent\_connection():
> await fetch\_data(connections\['default'\])
>         
> await asyncio.gather(load(), load(), load())
> \`\`\`
> 
> \- Provide an abstract asynchronous database connection handler
> 
> \> \`AsyncBaseDatabaseWrapper \` – Abstract base class providing shared logic for asynchronous database wrappers
> \`AsyncCursorDebugWrapper\` – Debug wrapper for AsyncCursor to log and trace query execution
> \`AsyncCursorWrapper\` – Wraps an async cursor to ensure Django compatibility and enable extensions.
> 
> \- Implement PostgreSQL-specific asynchronous connection
> 
> \> \`AsyncConnectionHandler\` – Manages asynchronous database connections
> \`AsyncDatabaseWrapper\` – Wraps the async connection, integrating with Django's database backend interface
> \`AsyncCursor\` – Provides asynchronous execution of database queries
> \`AsyncServerBindingCursor\` – Handles server-side binding for asynchronous cursors
> 
> \- Add transaction support for the asynchronous connection
> 
> \`\`\`python
> async with transaction.async\_atomic(using='default'):
> ...
> \`\`\`
> 
> \#### Checklist
> \- \[x\] This PR targets the \`main\` branch. 
> \- \[\] The commit message is written in past tense, mentions the ticket number, and ends with a period.
> \- \[\] I have checked the "Has patch" ticket flag in the Trac system.
> \- \[\] I have added or updated relevant tests.
> \- \[\] I have added or updated relevant docs, including release notes if applicable.
> \- \[\] I have attached screenshots in both light and dark modes for any UI changes.
