基于Django实现单代码库适配多客户多数据库的方案咨询
Django多客户独立数据库与配置实现方案
核心思路
复用同一套代码,通过动态加载客户专属配置和数据库隔离机制,让每个客户拥有独立数据库与配置,新增客户仅需添加配置文件和创建数据库,代码变更时统一更新即可,无需为每个客户重复操作。
1. 配置文件拆分与动态加载
- 把通用配置(如中间件、
INSTALLED_APPS、通用业务参数)保留在主settings.py中,每个客户的独立配置(数据库、专属静态路径、自定义业务参数)放在单独的配置目录,比如configs/customer_a.py、configs/customer_b.py - 在
settings.py中通过环境变量或请求域名动态加载对应客户的配置:
# settings.py import os import importlib # 优先从环境变量取客户标识,也可从请求域名解析 CUSTOMER_ID = os.environ.get('CUSTOMER_ID', 'default') # 加载客户专属配置并合并到全局 customer_config = importlib.import_module(f'configs.{CUSTOMER_ID}') globals().update(vars(customer_config))
- 客户配置文件示例(
configs/customer_a.py):
# 独立数据库配置 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'customer_a_restaurant', 'USER': 'db_user', 'PASSWORD': 'customer_a_pass', 'HOST': 'localhost', 'PORT': '3306', } } # 客户专属静态文件前缀 STATIC_URL = '/customer_a/static/' # 其他自定义参数,比如餐厅名称 RESTAURANT_NAME = 'A餐厅'
2. 数据库路由(按需选择)
如果需要单进程处理多个客户请求(比如共享一个WSGI进程),可以用Django的数据库路由实现动态切换:
- 创建
db_router.py文件:
from threading import local _thread_locals = local() class CustomerDatabaseRouter: def db_for_read(self, model, **hints): # 返回当前线程绑定的客户数据库 return getattr(_thread_locals, 'current_customer_db', 'default') def db_for_write(self, model, **hints): return self.db_for_read(model, **hints) def allow_relation(self, obj1, obj2, **hints): # 允许同一数据库内的关联操作 return obj1._state.db == obj2._state.db def allow_migrate(self, db, app_label, model_name=None, **hints): # 只在对应客户数据库执行迁移 return db == getattr(_thread_locals, 'current_customer_db', 'default')
- 在
settings.py中注册路由:
DATABASE_ROUTERS = ['myproject.db_router.CustomerDatabaseRouter']
- 新增中间件绑定客户标识到线程:
class CustomerMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): # 从域名解析客户(比如customer_a.example.com -> customer_a) host_parts = request.get_host().split('.') customer_id = host_parts[0] if len(host_parts) >=3 else 'default' # 绑定到线程局部变量 _thread_locals.current_customer_db = customer_id response = self.get_response(request) return response
- 在
settings.py的MIDDLEWARE中添加这个中间件。
3. 部署方案
多进程/容器模式(推荐)
每个客户启动独立的Django进程(或Docker容器),共享同一代码目录,通过环境变量指定客户:
- 用Gunicorn启动示例:
# 启动客户A的服务 CUSTOMER_ID=customer_a gunicorn myproject.wsgi:application --bind 127.0.0.1:8000 # 启动客户B的服务 CUSTOMER_ID=customer_b gunicorn myproject.wsgi:application --bind 127.0.0.1:8001
- 用Nginx做反向代理,根据域名路由到对应进程:
server { server_name customer_a.example.com; location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } # 直接处理静态文件,减轻Django压力 location /customer_a/static/ { root /path/to/your/static/files; } } server { server_name customer_b.example.com; location / { proxy_pass http://127.0.0.1:8001; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location /customer_b/static/ { root /path/to/your/static/files; } }
单进程多客户模式(适合小型场景)
用上面的数据库路由+中间件方案,单进程处理所有客户请求,通过域名区分,无需多进程,但要注意进程隔离和性能问题。
4. 客户新增与代码维护
- 新增客户:
- 创建对应数据库并授权
- 在
configs目录新增客户配置文件 - 启动对应进程(多进程模式)或重启单进程
- 执行迁移:
CUSTOMER_ID=new_customer python manage.py migrate
- 代码变更:
直接更新主代码目录,重启所有客户进程即可,所有客户自动同步新代码,无需逐个修改。
注意事项
- 数据库权限严格隔离,每个客户的数据库账号仅能访问自己的库
- 可编写自动化脚本(如
create_customer.py)批量完成配置创建、数据库初始化、迁移操作 - 日志按客户拆分,方便排查问题:
# settings.py 日志配置 LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'customer_file': { 'class': 'logging.FileHandler', 'filename': f'/var/log/django/{CUSTOMER_ID}.log', 'formatter': 'verbose', }, }, 'loggers': { 'django': { 'handlers': ['customer_file'], 'level': 'INFO', 'propagate': True, }, }, 'formatters': { 'verbose': { 'format': '{levelname} {asctime} {module} {message}', 'style': '{', }, }, }
内容的提问来源于stack exchange,提问作者andrew
相关产品推荐
相关产品推荐

