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

Django多域名站点路由:如何在urls.py中检测请求域名?

解决方案:Django多域名动态路由到子应用

Great question! Let's break this down clearly because your initial approach has a key limitation, and there are several solid ways to solve this without modifying your database.

First: Why your initial urls.py approach won't work

Django parses urls.py once at project startup—not on every request. The request object doesn't exist when urls.py is loaded, so checking request.domain there will throw an error immediately. You need a way to route dynamically per-request, which means using middleware or custom URL resolvers instead.


Option 1: Middleware to Rewrite Request Path (Simplest Approach)

This is the easiest method if you want to avoid overcomplicating things. The idea is to use a middleware that checks the request's domain, then modifies the request path to include a prefix for your target sub-app. Django's standard URL router will then handle the rest.

Step 1: Create the Middleware

Create middleware.py in your main app:

import re
from django.utils.deprecation import MiddlewareMixin

class DomainRoutingMiddleware(MiddlewareMixin):
    # Map your domains to sub-app prefixes (no database changes needed!)
    DOMAIN_TO_APP = {
        "name1.yourdomain.com": "app1",
        "name2.yourdomain.com": "app2",
        # Add more domains as needed
    }
    DEFAULT_APP = "app3"

    def process_request(self, request):
        # Clean the host (remove port numbers like :8000 for local dev)
        host = re.sub(r":\d+$", "", request.get_host().lower())
        
        # Get the target app prefix
        app_prefix = self.DOMAIN_TO_APP.get(host, self.DEFAULT_APP)
        
        # Rewrite the request path to include the app prefix (if not already present)
        if not request.path.startswith(f"/{app_prefix}/"):
            request.path = f"/{app_prefix}{request.path}"

Step 2: Update urls.py

Modify your main urls.py to include each sub-app under its prefix:

from django.urls import path, include

urlpatterns = [
    path("app1/", include("app1.urls")),
    path("app2/", include("app2.urls")),
    path("app3/", include("app3.urls")),
]

Step 3: Enable the Middleware

Add the middleware to settings.py (place it early in the list, before any middleware that depends on the path):

MIDDLEWARE = [
    "yourapp.middleware.DomainRoutingMiddleware",  # Add this line
    # ... rest of your middleware
]

Now when a user visits name1.yourdomain.com, the middleware rewrites the path to /app1/ behind the scenes, and Django routes it to app1's URLs. The user never sees the prefix in their browser.


Option 2: Custom URL Resolver (More Flexible)

If you want to avoid path rewriting, you can create a custom URL resolver that picks the right sub-app's URL patterns based on the request domain. This requires using thread-local storage to pass the domain to the resolver.

Step 1: Middleware to Store the Domain

Create a middleware to save the current request's domain to thread-local storage:

import threading
import re
from django.utils.deprecation import MiddlewareMixin

class DomainStorageMiddleware(MiddlewareMixin):
    _local = threading.local()

    def process_request(self, request):
        host = re.sub(r":\d+$", "", request.get_host().lower())
        self._local.current_host = host

    @classmethod
    def get_current_host(cls):
        return getattr(cls._local, "current_host", None)

Step 2: Custom URL Resolver

Modify your main urls.py to use a custom resolver:

from django.urls import URLResolver, path, include
from yourapp.middleware import DomainStorageMiddleware

class DomainBasedResolver(URLResolver):
    def resolve(self, path):
        host = DomainStorageMiddleware.get_current_host()
        
        # Map domains to sub-app URL patterns
        domain_patterns = {
            "name1.yourdomain.com": include("app1.urls"),
            "name2.yourdomain.com": include("app2.urls"),
        }
        # Fallback to app3 if no match
        urlconf = domain_patterns.get(host, include("app3.urls"))
        
        # Use the selected app's URL patterns to resolve the path
        return super().resolve(path)

# Use the custom resolver in your URL patterns
urlpatterns = [
    DomainBasedResolver("", []),
]

Step 3: Enable the Middleware

Add both middleware to settings.py (make sure DomainStorageMiddleware comes first):

MIDDLEWARE = [
    "yourapp.middleware.DomainStorageMiddleware",
    # ... rest of your middleware
]

This approach keeps your URL paths clean and directly routes requests to the correct sub-app without path rewriting.


What About Django's Sites Framework?

You're right that the Sites framework is primarily for model-level data partitioning, but you can use it alongside the above methods if you later decide to store domain mappings in the database. For your current requirement (no database changes), it's unnecessary—your hardcoded domain-to-app map works perfectly.

If you ever want to switch to database-managed domains, you could update the middleware/resolver to fetch the Site object matching the host, then use the Site's name or id to pick the sub-app. But since you don't want to modify the database now, stick with the hardcoded map.


内容的提问来源于stack exchange,提问作者Francisco Ghelfi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:45:23