You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django多子项目按域名动态加载urlpatterns问题求助

Django多子项目域名动态路由解决方案

方案一:自定义URLResolver实现动态路由

Django的URL解析系统支持自定义Resolver,我们可以通过它根据请求域名动态加载对应子项目的urlpatterns。

步骤1:编写自定义DomainURLResolver

在项目根目录创建resolvers.py:

from django.urls.resolvers import URLResolver
from django.conf import settings
from django.http import HttpRequest, HttpResponseNotFound
from django.urls import path

class DomainURLResolver(URLResolver):
    def resolve(self, path):
        # 从全局属性获取当前请求(由中间件注入)
        request = getattr(HttpRequest, '_current_request', None)
        if not request:
            raise ValueError("Request not captured, check middleware setup")
        
        # 统一转小写避免大小写问题
        domain = request.get_host().lower()
        # 从配置读取域名-子项目映射
        domain_mapping = getattr(settings, 'DOMAIN_SERVICE_MAPPING', {})
        target_urlconf = domain_mapping.get(domain)

        if not target_urlconf:
            # 处理未映射域名的情况
            def domain_not_found(request):
                return HttpResponseNotFound("Domain not mapped to any service")
            temp_urls = [path('', domain_not_found)]
            self.urlconf_module = type('TempURLs', (), {'urlpatterns': temp_urls})
        else:
            # 动态设置要加载的子项目urls
            self.urlconf_module = target_urlconf
        
        return super().resolve(path)

步骤2:编写请求捕获中间件

创建middleware.py,用于将当前请求临时存入全局属性,供Resolver调用:

class RequestCaptureMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        # 将请求绑定到HttpRequest类属性,供Resolver访问
        setattr(request.__class__, '_current_request', request)
        response = self.get_response(request)
        # 请求处理完成后清理属性,防止内存泄漏
        delattr(request.__class__, '_current_request')
        return response

步骤3:配置settings.py

在settings.py中注册中间件,并定义域名映射:

# 中间件列表最前面加入请求捕获中间件
MIDDLEWARE = [
    'your_project_name.middleware.RequestCaptureMiddleware',
    # 其他原有中间件...
]

# 域名与子项目urls的映射,可后续改为从数据库读取
DOMAIN_SERVICE_MAPPING = {
    'service1.domain.com': 'service1.urls',
    'domainforservice1.com': 'service1.urls',
    'service2.domain.com': 'service2.urls',
    'domainforservice2.com': 'service2.urls',
}

步骤4:根urls.py中使用自定义Resolver

from django.urls import path
from your_project_name.resolvers import DomainURLResolver
from django.conf import settings

urlpatterns = [
    # 全局通用路由(如admin)放在前面
    path('admin/', admin.site.urls),
    # 使用自定义Resolver处理域名路由
    DomainURLResolver(r'', 'service1.urls'),  # 这里的默认值会被动态替换
]

方案二:中间件直接转发请求到子项目

如果不想修改URL解析器,可以在中间件中直接根据域名调用对应子项目的URL分发逻辑。

编写路由中间件

from django.urls import get_resolver
from django.http import HttpResponseNotFound

class DomainRoutingMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        # 预定义域名映射,可改为从数据库查询
        self.domain_mapping = {
            'service1.domain.com': 'service1.urls',
            'domainforservice1.com': 'service1.urls',
            'service2.domain.com': 'service2.urls',
            'domainforservice2.com': 'service2.urls',
        }

    def __call__(self, request):
        domain = request.get_host().lower()
        target_urlconf = self.domain_mapping.get(domain)

        if not target_urlconf:
            return HttpResponseNotFound("Unsupported domain")
        
        # 使用子项目的urlconf解析请求路径
        resolver = get_resolver(target_urlconf)
        try:
            match = resolver.resolve(request.path_info)
            view, args, kwargs = match
            return view(request, *args, **kwargs)
        except:
            # 解析失败时交给全局路由处理
            return self.get_response(request)

注册中间件

将该中间件放在settings.py的MIDDLEWARE列表最前面,确保优先处理路由转发。


注意事项

  • 动态映射更新:如果需要动态修改域名与子项目的绑定,可将domain_mapping改为从数据库读取,并添加缓存逻辑避免重复查询。
  • 本地开发配置:在本地hosts文件中添加域名映射,例如127.0.0.1 service1.domain.com,才能正常测试。
  • 生产环境Host头:确保Web服务器(Nginx/Apache)正确传递Host请求头给Django,不要篡改该字段。
  • 异常处理:需添加合适的异常捕获逻辑,处理子项目路由解析失败的情况。

内容的提问来源于stack exchange,提问作者Ömer Ekmen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 04:26:01