django-tenants: request view resolution is broken when using an ASGI servers with subfolder tenants
This initially came up while trying to setup django-tenants in SubFolder mode with an existing project that also uses django channels.
Accessing a tenant url would lead to the following error while processing any request under a tenant subpath :
Traceback (most recent call last):
File "/usr/local/lib/python3.9/site-packages/asgiref/sync.py", line 472, in thread_handler
raise exc_info[1]
File "/usr/local/lib/python3.9/site-packages/django/core/handlers/exception.py", line 42, in inner
response = await get_response(request)
File "/usr/local/lib/python3.9/site-packages/django/core/handlers/base.py", line 235, in _get_response_async
callback, callback_args, callback_kwargs = self.resolve_request(request)
File "/usr/local/lib/python3.9/site-packages/django/core/handlers/base.py", line 313, in resolve_request
resolver_match = resolver.resolve(request.path_info)
File "/usr/local/lib/python3.9/site-packages/django/urls/resolvers.py", line 651, in resolve
sub_match = pattern.resolve(new_path)
File "/usr/local/lib/python3.9/site-packages/django/urls/resolvers.py", line 646, in resolve
match = self.pattern.match(path)
File "/code/examples/tenant_subfolder_tutorial/../../django_tenants/urlresolvers.py", line 50, in match
tenant_prefix = self.tenant_prefix
File "/code/examples/tenant_subfolder_tutorial/../../django_tenants/urlresolvers.py", line 33, in tenant_prefix
domain=connection.tenant.domain_subfolder,
Exception Type: AttributeError at /clients/test/
Exception Value: 'FakeTenant' object has no attribute 'domain_subfolder'
I was able to track this down and reproduce the issue with the example tenant_subfolder_tutorial. As soon as the project is served behind an ASGI server, the previous error occurs when accessing a subfolder tenant.
Steps to reproduce :
- Setup the example project with a tenant
- Setup the tenant_subfolder_tutorial example project and run it
- Create one public tenant, then one
testtenant, with domain =test - Head over to http://localhost:8088/clients/test/ and verify the project is properly operating

- Enable ASGI and demonstrate the issue
- Add uvicorn in requirements and create an asgi.py file (or apply https://github.com/ey3ball/django-tenants/commit/e5cc6fa57ed9c35cd5d3cad104d0c6dc90275031)
- Start project through asgi server with
gunicorn tenant_subfolder_tutorial.asgi:application --bind 0.0.0.0:8088 -k uvicorn.workers.UvicornWorker(or apply https://github.com/django-tenants/django-tenants/commit/31ed2eb81590b2d0c4327afa53e0cbfc94ccda45 and run it with compose) - Attempt to access http://localhost:8088/clients/test/
- Request fails with FakeTenant backtrace above

Preliminary analysis :
- in django_tenants/middleware/subfolder.py: the connection object is used to set a tenant
- It is later accessed by https://github.com/ey3ball/django-tenants/blob/master/django_tenants/urlresolvers.py#L33
- However at that point instead of getting the connection information we though we saved in the first step, we get a default FakeTenant object that defaults to the public tenant (https://github.com/ey3ball/django-tenants/blob/master/django_tenants/postgresql_backend/base.py#L187)
- Because domain_subfolder is not set on this object a backtrace ensues
At this moment I think this boils down to the fact that we can’t rely on the connection object staying the same during the whole processing of the request. In WSGI mode I guess we can rely on 1 request = 1 connection because there will be one thread dedicated to servicing each request. In ASGI mode it seems this does not hold anymore.
Although this came up with the SubFolder Middleware, this might be a more general issue of pinning data on the Connection object in django_tenants middlewares
About this issue
- Original URL
- State: open
- Created 2 years ago
- Reactions: 2
- Comments: 17 (6 by maintainers)
Commits related to this issue
- generate per-tenant urlconf in sub-folder middleware This is an attempt to fix django-tenants/django-tenants#820 by generating a fixed urlconf in the middleware, we remove the need to perform IO dur... — committed to ey3ball/django-tenants by fvallee-fbx 2 years ago
- generate per-tenant urlconf in sub-folder middleware This fixes django-tenants/django-tenants#820 until a better method is found. The root cause is that information stored on connections is not avai... — committed to ey3ball/django-tenants by fvallee-fbx 2 years ago
- generate per-tenant urlconf in sub-folder middleware This fixes django-tenants/django-tenants#820 until a better method is found. The root cause is that information stored on connections is not avai... — committed to ey3ball/django-tenants by fvallee-fbx 2 years ago
- generate per-tenant urlconf in sub-folder middleware This fixes django-tenants/django-tenants#820 until a better method is found. The root cause is that information stored on connections is not avai... — committed to fvallee-fbx/django-tenants by fvallee-fbx 2 years ago
👋 @lorinkoz (initial contributor of the subfolder code) in case you have some insights
I’m trying to find the right way to fix this, currently the domain object is looked up during the path matching due to tenant_prefix. Which is the root cause of the crash.
I’m thinking that since the urlconf is set during process_request in TenantSubfolderMiddleware, creating tenant specific urlconfs and setting them at this point would fix the issue.
However it looks like this is something you wanted to avoid considering we have a single urlconf with dynamic resolution at the moment.
I never really had the opportunity of coming back to this. I’ll rebase my branch and try to get this ball rolling again. May take a few weeks before I get the bandwidth to throw in some tests
The solution is either to transition to django-pgschemas which has a modernized approach with this issue fixed. With django-tenants as far as I know the only solution is to pull my fixes here : https://github.com/django-tenants/django-tenants/pull/822
I haven’t heard back about integrating it so far. I can rebase on the latest master and have another go at it
Per the comment above I don’t think importing connection into tenant_prefix would work because of the Local(thread_critical=True), relying directly on asgiref,local seems cleaner since we have control over the sharing flags instead of relying on whatever magic the connection class performs
I’ve tried the approach above (WIP patch here : https://github.com/ey3ball/django-tenants/commit/b995f77f1b4f0a4d77f945a913aecbe1a58a6de3), this seem to confirm what I’ve discovered so far, with this patch I can successfully acces the homepage of my tenant in subfolder mode with an asgi frontend.
Some more test required, I guess my approach might not support reverse() at the moment since I’m not actually registering the resulting urlconf module anywhere