如何在django-tenants项目中独立部署DRF API至专属域名?
基于django-tenants的独立域名DRF API配置方案
一、核心配置步骤
1. 域名路由分离
使用django-hosts实现不同域名绑定不同路由配置,让api.example.com指向独立的API路由:
- 安装依赖:
pip install django-hosts - 在项目
settings.py中添加配置:INSTALLED_APPS = [ # 其他应用 'django_hosts', 'rest_framework', ] MIDDLEWARE = [ 'django_hosts.middleware.HostsRequestMiddleware', # 放在最前面 # 其他中间件(包括django-tenants的中间件) 'django_hosts.middleware.HostsResponseMiddleware', # 放在最后 ] ROOT_HOSTCONF = 'your_project.hosts' DEFAULT_HOST = 'tenant' # 租户子域名的默认路由 PARENT_HOST = 'example.com' - 创建项目根目录下的
hosts.py:from django_hosts import patterns, host host_patterns = patterns('', # 绑定api.example.com到api_urls路由 host(r'api', 'your_project.api_urls', name='api'), # 绑定租户子域名(如tenant1.example.com)到tenant_urls路由 host(r'(?P<tenant>[\w-]+)', 'your_project.tenant_urls', name='tenant'), ) - 分别创建
api_urls.py(存放DRF API路由)和tenant_urls.py(存放租户应用路由):api_urls.py示例:from django.urls import path from rest_framework import routers from .api_views import YourModelViewSet router = routers.DefaultRouter() router.register(r'your-model', YourModelViewSet) urlpatterns = router.urls
2. 跳过租户中间件处理
修改django-tenants的租户识别中间件,让api.example.com跳过自动租户识别:
- 自定义中间件继承
TenantMainMiddleware:from tenant.middleware.main import TenantMainMiddleware class CustomTenantMiddleware(TenantMainMiddleware): def process_request(self, request): if request.get_host() == 'api.example.com': # 跳过自动租户识别,留空让API自行处理 request.tenant = None return None # 其他域名走原有租户识别逻辑 return super().process_request(request) - 在
settings.py中替换原有中间件:MIDDLEWARE = [ 'django_hosts.middleware.HostsRequestMiddleware', # 替换为自定义中间件 'your_project.middleware.CustomTenantMiddleware', # 其他中间件 'django_hosts.middleware.HostsResponseMiddleware', ]
3. API手动识别租户
由于API不在租户子域名下,需要通过其他方式识别租户,常用两种方案:
方案A:请求头传递租户标识
添加API专用中间件,从请求头获取租户信息并切换数据库连接:
from django.http import HttpResponseBadRequest from tenant.models import Tenant from tenant_schemas.utils import connection class APITenantMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): if request.get_host() == 'api.example.com': # 从请求头获取租户域名或ID tenant_domain = request.META.get('HTTP_X_TENANT_DOMAIN') if not tenant_domain: return HttpResponseBadRequest("Missing X-Tenant-Domain header") try: tenant = Tenant.objects.get(domain_url=tenant_domain) # 切换到租户的数据库schema connection.set_tenant(tenant) request.tenant = tenant except Tenant.DoesNotExist: return HttpResponseBadRequest("Invalid tenant domain") response = self.get_response(request) return response
将该中间件添加到settings.py的MIDDLEWARE中,放在CustomTenantMiddleware之后。
方案B:认证Token关联租户
扩展DRF的Token模型,让Token与租户绑定,认证后自动识别租户:
from rest_framework.authtoken.models import Token from django.db import models from tenant.models import Tenant class TenantToken(Token): tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE, related_name='tokens')
在DRF的认证视图或自定义认证类中,通过Token获取关联的租户并切换连接。
二、业内常见实践
- 独立域名+请求头识别:最主流的方案,实现简单,客户端只需在请求时携带
X-Tenant-Domain或X-Tenant-ID头,适合中小规模多租户系统。 - API网关路由:通过Nginx、Kong等网关,根据请求头或路径前缀将请求转发到对应租户的逻辑层,适合高流量、复杂架构的系统,可实现负载均衡和权限统一管控。
- 共享租户隔离:创建一个专门的"公共租户"存放API服务,但需严格区分公共数据与租户数据,容易引发数据混淆,仅适合特定场景。
- API独立部署:将DRF API作为单独服务部署,与租户应用分离,通过内部接口调用租户服务,适合API流量大、需要独立扩容的场景,但会增加运维成本。
内容的提问来源于stack exchange,提问作者Crisp Apples
相关产品推荐
相关产品推荐

