# DEP0009/ORM and backward-compatibility

**URL:** <https://forum.djangoproject.com/t/dep0009-orm-and-backward-compatibility/32175>\
**Category:** Async\
**Created:** [June 17, 2024, 7:41pm UTC](https://forum.djangoproject.com/t/dep0009-orm-and-backward-compatibility/32175 "2024-06-17T19:41:52Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![fcurella](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/fcurella/32/70_2.png) [@fcurella](https://forum.djangoproject.com/u/fcurella)\
**Post date:** [June 17, 2024, 7:41pm UTC](https://forum.djangoproject.com/t/dep0009-orm-and-backward-compatibility/32175/1 "2024-06-17T19:41:52Z")

</div>

I’m working on a PoC for DEP0009, and I a little unclear on the backward-compatibility approach.

Currently, it is possible to `await` an async method (eg `.aget()`) with a sync-only DatabaseWrapper, and it will call the correspondent synchronous method wrapped in a `sync_to_async`. In other words, currently this is possible:

```python3
from my_app.models import MyModel

my_obj = await MyModel.objects.aget(pk=1)

```

DEP0009 states:

> Backwards compatibility means we must let users access `connections` from any random code whenever they like, but we will only allow this for synchronous code; we will enforce that code is wrapped in a “connection context” if it is asynchronous, from day one.

My understanding is that this is the DEP’s intended behavior:

```python3
from my_app.models import MyModel

my_obj = await MyModel.objects.aget(pk=1)
### raises some NotInAsyncException

```

Wouldn’t that break backward-compatibility? Should PEP0009 be revised to include a migration plan?

---

<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 18, 2024, 6:55am UTC](https://forum.djangoproject.com/t/dep0009-orm-and-backward-compatibility/32175/2 "2024-06-18T06:55:18Z")

</div>

Hi @fcurella 👋

First off, thanks for picking this up. It’s an exciting next step! 🎁

> [@fcurella](#):
>
> …we will enforce that code is wrapped in a “connection context” if it is asynchronous, from day one.

So, I don’t know exactly what was in mind with a _“connection context”_ but, my understanding was that we already did this, in that if you try and make a (sync) DB call from an async context, without going via `sync_to_async()` you’ll get a `SynchronousOnlyOperation` error, as per the [Async safety section of the docs](https://docs.djangoproject.com/en/5.0/topics/async/#async-safety).

I’m not sure what more is needed or intended there? 🤔

If we have a genuinely async query path then that’s already covered — you can’t use `await` outside of an `async def` function — so again we’re good no?

Likely I’m missing something! 🙂

---

<div class="post-metadata">

**Author:** ![fcurella](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/fcurella/32/70_2.png) [@fcurella](https://forum.djangoproject.com/u/fcurella)\
**Post date:** [June 18, 2024, 4:45pm UTC](https://forum.djangoproject.com/t/dep0009-orm-and-backward-compatibility/32175/3 "2024-06-18T16:45:37Z")

</div>

Consider this example:

```python3
from my_app.models import MyModel

async def my_view(request):
    my_obj = await MyModel.objects.aget(pk=1)
    ...

```

Currently, such a view doesn’t raise any exception.

PEP009 defines an async contextmanager, `new_connection`. ORM calls are supposed to be allowed only inside the connection context that `new_connection` creates:

```python3
from django.db import new_connection
from my_app.models import MyModel

async def my_view(request):
    async with new_connection:
        my_obj = await MyModel.objects.aget(pk=1)
    ...

```

The ORM call in the first version of the view won’t have a connection context to work with, and will raise an error.

I guess we _could_ wrap async view with `new_connection` by default (haven’t look into it), but I’m wondering about other code that’s not necessarily inside a view (management commands come to mind)

---

<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 18, 2024, 5:50pm UTC](https://forum.djangoproject.com/t/dep0009-orm-and-backward-compatibility/32175/4 "2024-06-18T17:50:34Z")

</div>

And presumably we do still need something along this line because (if I’m not **just** inventing things) if the underlying code is async we’d need an async connection, yes?

Ok… thank you for your patience, I think I’m with you.

> [@fcurella](#):
>
> Wouldn’t that break backward-compatibility? Should PEP0009 be revised to include a migration plan?

Do you have any thought for a migration?  
It might be that we just have to add the break there. (That’s not optimal clearly…) 🤔

---

<div class="post-metadata">

**Author:** ![bigfootjon](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/bigfootjon/32/12068_2.png) [@bigfootjon](https://forum.djangoproject.com/u/bigfootjon)\
**Post date:** [June 18, 2024, 6:38pm UTC](https://forum.djangoproject.com/t/dep0009-orm-and-backward-compatibility/32175/5 "2024-06-18T18:38:24Z")

</div>

We could detect the situation and issue a warning before a later version throws an exception? That at least partially mitigates the pain of this change.

Quoting from the DEP:

> In an asynchronous world - where all coroutines run on the same underlying Python thread - this goes beyond being annoying to being outright dangerous. Without any extra safety, a user calling the ORM the way they do today would risk cross-thread pollution of the connection objects.

I’m not terribly familiar with how the ORM handles connections, but is another alternative to clean up the cross-thread pollution?

> [@fcurella](#):
>
> I guess we _could_ wrap async view with `new_connection` by default (haven’t look into it),

This idea might have some merit. As I understand it (currently afk) database connections are cleaned up at the end of every request anyway by a signal hook. There’s some nuance here around how “forever” connections can stick around but it might work.

---

<div class="post-metadata">

**Author:** ![fcurella](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/fcurella/32/70_2.png) [@fcurella](https://forum.djangoproject.com/u/fcurella)\
**Post date:** [June 18, 2024, 10:30pm UTC](https://forum.djangoproject.com/t/dep0009-orm-and-backward-compatibility/32175/6 "2024-06-18T22:30:47Z")

</div>

One option would to fall back to the current `sync_to_async` behavior when we don’t have an async connection, emitting a `PendingDeprecationWarning`.

Something like:

```python
class QuerySet:
    ....

    async def aexists(self):
        try:
            return await <async_implentation>
        except MissingAsyncConnection:
            warnings.warn("...")
            return await sync_to_async(self.exists)()

```

---

<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 19, 2024, 6:34am UTC](https://forum.djangoproject.com/t/dep0009-orm-and-backward-compatibility/32175/7 "2024-06-19T06:34:45Z")

</div>

That seems perfectly reasonable.

- Introduce context manager to get an async connection.
- Deprecate using async ORM methods without the context manager.

---

<div class="post-metadata">

**Author:** ![dougharris](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/dougharris/32/16538_2.png) [@dougharris](https://forum.djangoproject.com/u/dougharris)\
**Post date:** [July 6, 2024, 5:21pm UTC](https://forum.djangoproject.com/t/dep0009-orm-and-backward-compatibility/32175/8 "2024-07-06T17:21:19Z")

</div>

For the benefit of others, like me, who didn’t remember what DEP 0009 is, it’s a Django Enhancement Proposal (DEP) for Async-capable Django

Full doc at [deps/accepted/0009-async.rst at main · django/deps · GitHub](https://github.com/django/deps/blob/main/accepted/0009-async.rst)
