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

如何基于单个settings.py部署多域名多语言Django网站,实现单容器运行

多语言站点架构优化问题

我运行着一个多语言网站,涉及域名包括example.com、example.it、example.at、example.ch……example.fr。目前使用Django sites框架,每个域名对应一个Docker容器。按照Django官方文档的说明,为每个SITE_ID创建了单独的设置文件(导入全局设置避免重复),通过指定DJANGO_SETTINGS_MODULE来对应不同站点。

当前目录结构如下:

├── ...
├── urls.py
├── settings.py
├── sites
│   ├── examplecom.py
│   ├── exampleit.py
│   ├── exampleat.py
│   ├── examplech.py
│   ├── ...
│   └── examplefr.py
├── ...
└── wsgi.py

自定义配置文件示例(以examplepl.py为例):

from myapp.settings import *

SITE_ID             = 19
LANGUAGE_CODE       = SITEID_INFO[SITE_ID]['language']
COUNTRY_CODE        = SITEID_INFO[SITE_ID]['country']
ALPHABET_CHARACTERS = SITEID_INFO[SITE_ID]['alphabet']

SITE_RELATED_COUNTRIES = ['PL']

DEFAULT_FROM_EMAIL          = SITEID_INFO[SITE_ID]['default_from_email']
DEFAULT_FROM_EMAIL_NAME     = SITEID_INFO[SITE_ID]['default_from_email_name']

SHORT_DATE_FORMAT = 'd.m.Y'

SOME_TOKEN = 'foobar'

AVAILABLE_ORDER_PACKAGES = [28, 5, 6, 29, 14, 15, 30, 23, 24 ]

PAYMENT_CURRENCY_CODE = "PLN"
PAYMENTOPTION['przelewy24'] = True
...

目前每个站点的语言、访问令牌、货币、支付方式等变量都是硬编码在各自的配置文件中,同时使用Wagtail(内容动态展示部分不在讨论范围内)。随着站点数量增加,每个域名对应一个Docker容器的维护成本极高,目标是用单个Docker容器承载所有站点,现提出两个技术问题:

  1. 如何实现基于域名的动态语言切换?是否存在类似Locale Middleware的机制?
  2. 能否通过中间件根据请求域名动态修改settings.py中的变量?

问题解答

1. 基于域名的动态语言切换

可以通过自定义中间件实现域名驱动的语言切换逻辑,步骤如下:

  • 建立域名与语言的映射关系
    在全局settings.py中维护字典,记录域名对应的语言代码:

    DOMAIN_LANG_MAP = {
        'example.com': 'en',
        'example.it': 'it',
        'example.at': 'de',
        'example.ch': 'de',
        'example.fr': 'fr',
        # 补充其他域名映射
    }
    
  • 编写自定义语言中间件
    创建middleware.py,实现根据请求域名设置当前语言的逻辑:

    from django.utils import translation
    from django.conf import settings
    
    class DomainLocaleMiddleware:
        def __init__(self, get_response):
            self.get_response = get_response
    
        def __call__(self, request):
            # 提取域名(去除端口号)
            host = request.get_host().split(':')[0]
            # 获取对应语言,默认使用全局LANGUAGE_CODE
            lang_code = settings.DOMAIN_LANG_MAP.get(host, settings.LANGUAGE_CODE)
            # 激活当前语言
            translation.activate(lang_code)
            request.LANGUAGE_CODE = translation.get_language()
    
            response = self.get_response(request)
            # 在响应头中添加语言标识
            response['Content-Language'] = translation.get_language()
            translation.deactivate()
            return response
    
  • 替换Locale Middleware
    在settings.py的MIDDLEWARE列表中,将django.middleware.locale.LocaleMiddleware替换为自定义的DomainLocaleMiddleware,注意中间件顺序:放在SessionMiddleware之后、CommonMiddleware之前,确保语言设置能正确作用于后续请求处理:

    MIDDLEWARE = [
        # ...其他中间件
        'myapp.middleware.DomainLocaleMiddleware',
        # ...其他中间件
    ]
    

2. 通过中间件动态调整站点配置

Django的全局设置在启动时已加载缓存,直接修改settings模块的变量会导致多请求环境下的状态混乱,不推荐这么做。推荐两种安全可行的方案:

方案一:数据库存储站点配置

利用Django sites框架的Site模型,扩展字段存储每个站点的自定义配置:

  1. 创建SiteConfig模型,关联Site:
    from django.db import models
    from django.contrib.sites.models import Site
    
    class SiteConfig(models.Model):
        site = models.OneToOneField(Site, on_delete=models.CASCADE)
        country_code = models.CharField(max_length=2)
        payment_currency = models.CharField(max_length=3)
        some_token = models.CharField(max_length=255)
        available_packages = models.JSONField()
        payment_options = models.JSONField()
        # 按需添加其他配置字段
    
  2. 编写中间件,根据请求域名获取对应Site和SiteConfig,绑定到request对象:
    from django.contrib.sites.models import Site
    from myapp.models import SiteConfig
    
    class SiteConfigMiddleware:
        def __init__(self, get_response):
            self.get_response = get_response
    
        def __call__(self, request):
            host = request.get_host().split(':')[0]
            try:
                site = Site.objects.get(domain=host)
                site_config = SiteConfig.objects.get(site=site)
                request.site_config = site_config
                request.site_id = site.id
            except Site.DoesNotExist:
                # 域名未匹配时使用默认站点配置
                site = Site.objects.get(id=settings.SITE_ID)
                request.site_config = SiteConfig.objects.get(site=site)
                request.site_id = settings.SITE_ID
    
            response = self.get_response(request)
            return response
    
  3. 在视图或模板中通过request.site_config调用配置:
    def checkout_view(request):
        currency = request.site_config.payment_currency
        # 业务逻辑处理
    

方案二:线程局部存储配置

若不想改动数据库,可使用线程局部变量存储当前站点配置,避免全局污染:

  1. 创建site_settings.py定义线程局部存储:
    import threading
    
    _thread_locals = threading.local()
    
    def get_current_site_config():
        return getattr(_thread_locals, 'site_config', None)
    
    def set_current_site_config(config):
        _thread_locals.site_config = config
    
  2. 修改原站点配置文件,将变量封装到CONFIG字典:
    from myapp.settings import *
    
    CONFIG = {
        'SITE_ID': 19,
        'LANGUAGE_CODE': SITEID_INFO[19]['language'],
        'COUNTRY_CODE': SITEID_INFO[19]['country'],
        'PAYMENT_CURRENCY_CODE': "PLN",
        # 其他配置项
    }
    
  3. 编写中间件,根据域名加载对应配置并设置到线程局部变量:
    from myapp.sites.examplecom import CONFIG as COM_CONFIG
    from myapp.sites.exampleit import CONFIG as IT_CONFIG
    from myapp.site_settings import set_current_site_config
    
    DOMAIN_CONFIG_MAP = {
        'example.com': COM_CONFIG,
        'example.it': IT_CONFIG,
        # 其他域名映射
    }
    
    class DynamicSiteConfigMiddleware:
        def __init__(self, get_response):
            self.get_response = get_response
    
        def __call__(self, request):
            host = request.get_host().split(':')[0]
            config = DOMAIN_CONFIG_MAP.get(host, DEFAULT_CONFIG)
            set_current_site_config(config)
    
            response = self.get_response(request)
            return response
    
  4. 在视图中通过get_current_site_config()获取配置:
    from myapp.site_settings import get_current_site_config
    
    def token_view(request):
        config = get_current_site_config()
        token = config['SOME_TOKEN']
        # 业务逻辑处理
    

单容器部署注意事项

  • Web服务器配置:确保Nginx/Apache将所有域名请求转发到同一个容器内的Django应用,虚拟主机配置统一指向容器端口。
  • Django站点记录:提前在数据库中创建所有站点的Site实例,确保域名与Site的domain字段完全匹配(注意是否带www前缀)。
  • 资源管理:统一管理静态/媒体文件,若站点有专属资源,可按域名分目录存储,在视图中根据当前站点动态加载。

内容的提问来源于stack exchange,提问作者Philipp S.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 22:14:52