# Allow 'Database Router' to override default \`connection\`

**URL:** <https://forum.djangoproject.com/t/allow-database-router-to-override-default-connection/34529>\
**Category:** ORM\
**Created:** [September 5, 2024, 7:31pm UTC](https://forum.djangoproject.com/t/allow-database-router-to-override-default-connection/34529 "2024-09-05T19:31:24Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![rockisch](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/rockisch/32/23730_2.png) [@rockisch](https://forum.djangoproject.com/u/rockisch)\
**Post date:** [September 5, 2024, 7:31pm UTC](https://forum.djangoproject.com/t/allow-database-router-to-override-default-connection/34529/1 "2024-09-05T19:31:24Z")

</div>

Hey,

Recently I had to use the [Database Router](https://docs.djangoproject.com/en/5.1/topics/db/multi-db/#database-routers) feature, but ended up a bit surprised when I noticed that there is no way to override what DB is used when calling `connection.cursor()`! I get that the current API’s `db_for_read` and `db_for_write` doesn’t make sense for cursors, but adding a separate `db_for_cursor` or `db_for_connection` would easily fix the issue (in a backwards compatible way!).

I’m thinking about opening a feature request, but wanted to gather some thoughts before doing so. For reference, my use case is that I implemented a context manager for swapping what DB the Database Router uses (similar to [this](https://github.com/ambitioninc/django-dynamic-db-router)), so I can write code like this:

```python
with context_db("replica"):
    user = User.objects.first()
    with connection.cursor() as cursor:
        cursor.execute(...)

```

Afaik there are 2 ‘supported’ ways to ensure this works as expected:

1. Update all usages of `connection` to `connections[db_name]`
2. Create a custom DB engine that does the swap on the egine level

I feel like 1 will make the calling code needlessly complex, since I would need to fetch `db_name` from something like a `ContextVar`. 2 would kinda work, and is probably the route I’ll be following for now, but I feel like this kind of change more sense in a ‘Database Router’ than on a ‘Database Engine’ context.

Thoughts?

---

<div class="post-metadata">

**Author:** ![adamchainz](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/adamchainz/32/26_2.png) [@adamchainz](https://forum.djangoproject.com/u/adamchainz)\
**Post date:** [September 5, 2024, 9:27pm UTC](https://forum.djangoproject.com/t/allow-database-router-to-override-default-connection/34529/2 "2024-09-05T21:27:44Z")

</div>

I think this is outside the design goal of database routers. My understanding is that database routers are for routing the model layer - queries from `QuerySet`s. If you’re using the `connection` object, it’s your responsibility to do routing.

If you have a lot of pre-existing code that uses `connection` directly, yes, that will need a batch update. Hopefully, a choice regex find-and-replace would help. In general, it’s rare for Django projects to use `connection` directly, rather relying only on models and querysets.

---

<div class="post-metadata">

**Author:** ![rockisch](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/rockisch/32/23730_2.png) [@rockisch](https://forum.djangoproject.com/u/rockisch)\
**Post date:** [September 9, 2024, 7:21pm UTC](https://forum.djangoproject.com/t/allow-database-router-to-override-default-connection/34529/3 "2024-09-09T19:21:33Z")

</div>

Well, in that case there’s no need to implement the feature through routers. Maybe implementing it as a context manager, in the same way I was planning to implement it? That is:

```py
with connection.override("replica"):
    ... # 'connection.cursor()' will use 'replica'

```

I still find that having a global way to choose what DB will be used by default should be a common enough feature

> In general, it’s rare for Django projects to use `connection` directly

Although I don’t have any data to back it up… I do find that hard to believe. There’s just too much stuff that’s outside the scope of an ORM, but that is still relevant in any decently sized application (DB locks, isolation levels, DB-specific queries that don’t translate well into a ORM model like recursive queries) that requires a cursor to be executed.

---

<div class="post-metadata">

**Author:** ![rockisch](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/rockisch/32/23730_2.png) [@rockisch](https://forum.djangoproject.com/u/rockisch)\
**Post date:** [September 9, 2024, 7:25pm UTC](https://forum.djangoproject.com/t/allow-database-router-to-override-default-connection/34529/4 "2024-09-09T19:25:39Z")

</div>

Also, when looking at my codebase, I just noticed that there are some 3rd party applications that most likely use `connection.cursor` under the hood, so I don’t think the ‘replace every `connection.cursor`’ approach would work.

---

<div class="post-metadata">

**Author:** ![adamchainz](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/adamchainz/32/26_2.png) [@adamchainz](https://forum.djangoproject.com/u/adamchainz)\
**Post date:** [September 9, 2024, 7:33pm UTC](https://forum.djangoproject.com/t/allow-database-router-to-override-default-connection/34529/5 "2024-09-09T19:33:42Z")

</div>

> [@rockisch](#):
>
> Although I don’t have any data to back it up… I do find that hard to believe.

I’ve looked at ~100 projects that were deployed to production. I would guess about \<10% used `connection` directly. Most Django projects don’t need the advanced SQL features in your list. I think you’ll be in the top 1% of Django users if you know how to use those features!

> [@rockisch](#):
>
> Also, when looking at my codebase, I just noticed that there are some 3rd party applications that most likely use `connection.cursor` under the hood, so I don’t think the ‘replace every `connection.cursor`’ approach would work.

Yes, there is that. I even maintain one package that uses extensive direct connection (django-mysql). But it consistently provides a `using` parameter, which can at least be used with custom routing.

If we add something to Django core, it would probably be too unsafe to actually swap `connection`. I think it’s too risky, with regards to connection management, threads, and so on. I think we’d want a different object, like `routed_connection`, which third-party packages would need to interact with. So maybe not such a win…?

---

<div class="post-metadata">

**Author:** ![rockisch](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/rockisch/32/23730_2.png) [@rockisch](https://forum.djangoproject.com/u/rockisch)\
**Post date:** [September 9, 2024, 8:22pm UTC](https://forum.djangoproject.com/t/allow-database-router-to-override-default-connection/34529/6 "2024-09-09T20:22:21Z")

</div>

> [@adamchainz](#):
>
> I think we’d want a different object, like `routed_connection`, which third-party packages would need to interact with. So maybe not such a win…?

Yeah, that wouldn’t be that helpful xD

> [@adamchainz](#):
>
> If we add something to Django core, it would probably be too unsafe to actually swap `connection`. I think it’s too risky, with regards to connection management, threads, and so on.

It would just change the current proxy to fetch a different connection when getting a `cursor`. As it stands, `connection` is defined as a `ConnectionProxy` initialized with `alias="default"`:

```py
class ConnectionProxy:
    """Proxy for accessing a connection object's attributes."""

    def __init__ (self, connections, alias):
        self. __dict__ ['_connections'] = connections
        self. __dict__ ['_alias'] = alias

    def __getattr__ (self, item):
        return getattr(self._connections[self._alias], item)

    ... # Other wrapper stuff that uses 'self._connections[self._alias]'

```

The only change that would need to be made is to fetch `alias` from a `ContextVar`, and modify that `ContextVar` when the context manager is called. `ContextVar`s are async-safe, so no issue there.

Worst that can happen is that some piece of code accesses the connection directly with `connections["default"]`, or that stores a reference to the cursor for a long time. Still all that would happen is that it would use the default DB instead of the overwritten one, so that’s not going to break anything.

It also wouldn’t impact users not using the feature at all.

> [@adamchainz](#):
>
> I think you’ll be in the top 1% of Django users if you know how to use those features!

We do! 😳 (the codebase in question is for my company, so I’m not the only dev)

We also use a lot of Postgres specific stuff, and have done some DB magic in the past to handle zero-downtime model migrations in deploys with multiple servers.

And damn. I get that some of this stuff is niche, but I really though more people managed their isolation levels properly… probably tons of bugs waiting to be discovered 😄

---

<div class="post-metadata">

**Author:** ![chris48s](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/chris48s/32/26547_2.png) [@chris48s](https://forum.djangoproject.com/u/chris48s)\
**Post date:** [January 28, 2025, 5:09pm UTC](https://forum.djangoproject.com/t/allow-database-router-to-override-default-connection/34529/7 "2025-01-28T17:09:05Z")

</div>

While I agree that using `connection.cursor()` directly is both discouraged and relatively uncommon/advanced, using multiple DB Connections is also relatively uncommon/advanced. They are both features you turn to when your app has DB requirements beyond the common case. I’d wager that although both groups are quite small, the overlap in the venn diagram:

- “has an app with \>1 DB connections” and
- “uses `connection.cursor()`”

might be quite a big overlap.

---

<div class="post-metadata">

**Author:** ![rockisch](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/rockisch/32/23730_2.png) [@rockisch](https://forum.djangoproject.com/u/rockisch)\
**Post date:** [June 23, 2025, 5:35pm UTC](https://forum.djangoproject.com/t/allow-database-router-to-override-default-connection/34529/8 "2025-06-23T17:35:05Z")

</div>

Just as an update, we recently deployed the approach I mentioned before in production, and it has been working fine for our use cases. Here’s a gist showing what we are doing:

> <https://gist.github.com/rockisch/96425b6aaa406dcf4fb86df92715fe1e>

I still believe this should be treated by Django, since as @chris48s mentioned, yeah if you’re using multiple DBs for queries you’re probably using more advanced features.

I also found another post requesting a similar feature:

> [@Force queries in a context to execute in the named database](https://forum.djangoproject.com/t/force-queries-in-a-context-to-execute-in-the-named-database/37754/8):
>
> Hey, I actually opened a ticket about this a while ago: We actually recently deployed a ‘monkey-patch’ to do this kind of stuff in our project: If you’re only interested on overriding ORM queries, you could probably still it down to overriding the [database router](https://docs.djangoproject.com/en/5.2/topics/db/multi-db/#database-routers) part: import contextvars from contextlib import contextmanager \_dbname\_var = contextvars.ContextVar("dbname") \_readonly\_var = contextvars.ContextVar("readonly") @contextmanager def db\_override(dbname, readonly=False): dbnam…
