如何通过默认数据库的Tenant模型填充Django多租户的connections列表?
实现方案
1. 完善Tenant模型
首先扩展你的Tenant模型,补充数据库连接所需的核心字段,同时添加方法生成符合Django数据库配置格式的字典:
from django.db import models class Tenant(models.Model): name = models.CharField(max_length=20) # 数据库连接基础信息 db_name = models.CharField(max_length=100) db_user = models.CharField(max_length=50) db_password = models.CharField(max_length=100) db_host = models.CharField(max_length=100, default='localhost') db_port = models.CharField(max_length=10, default='5432') # 根据你的数据库类型调整,比如MySQL用3306 def get_db_config(self): """返回符合DATABASES结构的配置字典""" return { 'ENGINE': 'django.db.backends.postgresql', # 替换为你的数据库引擎,如django.db.backends.mysql 'NAME': self.db_name, 'USER': self.db_user, 'PASSWORD': self.db_password, 'HOST': self.db_host, 'PORT': self.db_port, }
2. 动态加载租户数据库配置到DATABASES
在项目启动阶段,从default数据库读取所有租户数据,将它们的数据库配置自动注入到Django的settings.DATABASES中。可以在主应用的apps.py中实现:
from django.apps import AppConfig from django.conf import settings from django.db import connections from .models import Tenant import sys class MainAppConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'main_app' # 替换为你的应用名称 def ready(self): # 仅在运行服务或shell时执行,避免migrate等初始化命令报错 if 'runserver' in sys.argv or 'shell' in sys.argv: tenants = Tenant.objects.all() for tenant in tenants: # 添加租户数据库配置 settings.DATABASES[tenant.name] = tenant.get_db_config() # 刷新connections池,使新配置立即生效 connections._connections.pop(tenant.name, None)
记得在应用的__init__.py中指定自定义的AppConfig:
default_app_config = 'main_app.apps.MainAppConfig'
3. 动态切换租户数据库(可选)
如果需要根据请求自动切换到对应租户的数据库,可编写中间件实现。这里以子域名识别租户为例,你可以根据实际场景替换为请求头、Session等方式:
from django.db import connections, router from .models import Tenant class TenantMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): # 从子域名提取租户名称,如 tenant1.yourdomain.com -> tenant1 host_parts = request.get_host().split('.') if len(host_parts) >= 3: tenant_name = host_parts[0] try: tenant = Tenant.objects.get(name=tenant_name) # 切换当前数据库连接 connections['default'].close() request.tenant = tenant # 覆盖路由规则,让ORM操作指向租户数据库 router.db_for_read = lambda model, **hints: tenant.name router.db_for_write = lambda model, **hints: tenant.name except Tenant.DoesNotExist: # 租户不存在时的处理,比如返回404 pass response = self.get_response(request) # 请求结束后关闭租户数据库连接 if hasattr(request, 'tenant'): connections[request.tenant.name].close() return response
将中间件添加到settings.MIDDLEWARE中:
MIDDLEWARE = [ # ... 其他中间件 'main_app.middleware.TenantMiddleware', ]
4. 批量迁移租户数据库
由于每个租户有独立数据库,需要批量执行迁移。编写自定义管理命令:
from django.core.management.base import BaseCommand from django.core.management import call_command from main_app.models import Tenant class Command(BaseCommand): help = '执行所有租户数据库的迁移操作' def handle(self, *args, **options): tenants = Tenant.objects.all() for tenant in tenants: self.stdout.write(f"正在迁移租户 {tenant.name} 的数据库...") call_command('migrate', database=tenant.name, **options) self.stdout.write(self.style.SUCCESS("所有租户数据库迁移完成"))
运行命令即可批量迁移:
python manage.py migrate_all_tenants
内容的提问来源于stack exchange,提问作者BugliL
相关产品推荐
相关产品推荐

