Django Tenants子文件夹模式租户切换与登录路由问题求助
Django-Tenants URL前缀切换Schema失效&404问题解决方案
1. 优先排查中间件配置与顺序
django-tenants的中间件顺序直接影响schema切换逻辑,必须保证TenantMiddleware(或自定义租户中间件)在最靠前的位置(仅需放在SecurityMiddleware这类基础中间件之后)。
在settings.py中配置:
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', # 自定义租户中间件要放在这里,默认TenantMiddleware同理 'your_project.middleware.TenantPrefixMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', # ...其他中间件 ]
同时确认核心配置项:
# 公共schema的路由配置(处理/login/这类无需租户的路由) PUBLIC_SCHEMA_URLCONF = 'your_project.urls_public' # 租户schema的路由配置(必须包含authorization_app的路由) TENANT_SCHEMA_URLCONF = 'your_project.urls_tenant'
2. 自定义URL前缀中间件的正确实现
自定义前缀识别中间件要确保正确获取租户、切换schema、处理路径重写,以下是可直接复用的示例:
# your_project/middleware.py from django.http import Http404 from django_tenants.middleware.base import BaseTenantMiddleware from django_tenants.utils import get_tenant_model class TenantPrefixMiddleware(BaseTenantMiddleware): def get_tenant(self, request): # 从URL路径提取前缀,比如/client/test2f0d3775/... path_parts = request.path.strip('/').split('/') if len(path_parts) < 2 or path_parts[0] != 'client': # 非租户路径,返回公共schema return get_tenant_model().objects.get(schema_name='public') tenant_id = path_parts[1] try: # 根据tenant_id查询租户(假设你的Tenant模型有tenant_id字段) return get_tenant_model().objects.get(tenant_id=tenant_id) except get_tenant_model().DoesNotExist: raise Http404("租户不存在") def process_request(self, request): tenant = self.get_tenant(request) # 切换到对应schema self.set_tenant(request, tenant) # 重写request.path,去掉前缀部分,让租户路由能匹配 # 比如把/client/test2f0d3775/tenant/login/ 改成 /tenant/login/ request.path = '/' + '/'.join(request.path.strip('/').split('/')[2:]) return super().process_request(request)
关键注意点:必须重写request.path去掉前缀,否则租户的路由无法匹配(这是你出现404的核心原因之一)。
3. 路由分层配置
把公共路由和租户路由完全分离,避免冲突:
公共路由(urls_public.py)
只处理不需要租户的请求,比如登录入口:
from django.urls import path from your_app.views import public_login_view urlpatterns = [ path('login/', public_login_view, name='public_login'), ]
租户路由(urls_tenant.py)
包含authorization_app的所有路由,注意不要重复加client/<tenant_id>前缀:
from django.urls import path, include from authorization_app import urls as auth_urls urlpatterns = [ # 这里的路径对应去掉前缀后的部分,比如/tenant/login/ path('tenant/login/', include(auth_urls)), # ...其他租户专属路由 ]
4. 修正重定向逻辑
重定向路径/client/tenant_id/tenant/login/没问题,但要确保:
- tenant_id是租户模型中存在的有效字段(比如不是schema_name而是自定义的tenant_id)
- 重定向时使用reverse生成正确路径,避免硬编码:
# 公共登录视图中重定向的代码示例 from django.shortcuts import redirect, reverse from django_tenants.utils import get_tenant_model def public_login_view(request): if request.method == 'POST': email = request.POST.get('email') # 根据邮箱查询对应的租户 tenant = get_tenant_model().objects.get(user__email=email) # 生成租户登录路径 login_url = reverse('tenant_login') # 对应租户urls里的name # 拼接前缀和登录路径 redirect_path = f'/client/{tenant.tenant_id}{login_url}' return redirect(redirect_path) # ...GET请求逻辑
5. 验证Schema切换的正确方式
不要用shell测试请求路径下的schema(shell是独立环境,不会走中间件),直接在中间件或视图中打印验证:
# 在自定义中间件的process_request中添加 print(f"当前Schema: {request.tenant.schema_name}") # 或者在租户视图中添加 from django.db import connection print(f"当前Schema: {connection.schema_name}")
启动服务后,访问/client/test2f0d3775/tenant/login/,查看控制台输出,确认schema是否切换成功。
6. 子域名模式Cookie丢失的临时解决
如果后续还要用子域名模式,Cookie丢失是因为domain配置问题,在settings.py中添加:
# 允许所有子域名共享Cookie,注意前面的点 SESSION_COOKIE_DOMAIN = ".127.0.0.1" # 开发环境,生产环境改成你的主域名 CSRF_COOKIE_DOMAIN = ".127.0.0.1"
内容的提问来源于stack exchange,提问作者user25066324
相关产品推荐
相关产品推荐

