tenant-schemas 必须用 postgresql,因其依赖 postgresql 的原生 schema 隔离机制实现租户数据分离;mysql/sqlite 不支持该特性,强行适配会破坏隔离性或导致迁移失败。

tenant-schemas 为什么必须用 PostgreSQL?
因为 tenant-schemas 的核心机制依赖 PostgreSQL 的 schema 特性——每个租户对应一个独立的数据库 schema,共享同一物理数据库。MySQL 和 SQLite 不支持原生 schema 隔离(MySQL 的 database 概念不等价,且无法在单连接中动态切换),强行适配会破坏数据隔离性或导致迁移失败。
常见错误现象:django.core.exceptions.ImproperlyConfigured: tenant-schemas only supports PostgreSQL —— 这不是警告,是硬性限制,别试 SQL Server 或 MySQL。
- 部署前确认 PostgreSQL 版本 ≥ 9.4(推荐 12+)
- 数据库用户需有
CREATE SCHEMA权限(CREATEDB不够) - 不要在
settings.DATABASES中配置TEST数据库名,tenant-schemas 会自动处理测试 schema
如何正确注册公共 schema 和租户模型?
公共 schema(public)必须存在且只存公共表(如 Tenant、Domain),所有租户 schema 从它派生。关键不在“建多少个表”,而在“哪些表该放在哪”。
典型错误:把 User 或 Product 模型没加 schema_editor = 'shared' 声明,结果迁移时被塞进租户 schema,导致登录失败或 404。
-
Tenant模型必须继承TenantMixin,且字段schema_name不能为public(保留字) - 在
INSTALLED_APPS中,tenant_schemas必须排在所有自定义 app 之前 - 公共表模型需显式标记:
class Meta: schema_editor = 'shared'(Django 3.2+)或使用SHARED_APPS/TENANT_APPS分离配置 - 运行
python manage.py migrate_schemas --shared初始化 public,再migrate_schemas(无参数)同步租户
请求来了,怎么自动路由到对应租户 schema?
靠 django-tenants(注意:不是 tenant-schemas)的中间件已过时;tenant-schemas 用的是 django-tenant-schemas(已归档),实际生产应迁移到更活跃的 django-tenants。但如果你坚持用原版,路由逻辑全在 get_tenant_model() + hostname_from_request() 里。
最常踩的坑:域名解析错位。比如租户域名是 acme.example.com,但 Nginx 把 Host 头转成了 example.com,或者开发时用 localhost:8000 根本没传 Host。
- 确保反向代理(Nginx/Apache)透传原始
Host头,禁用underscores_in_headers off - 本地调试加
ALLOWED_HOSTS = ['*', 'localhost', '127.0.0.1'],并用http://tenant1.localhost:8000测试 - 自定义
DEFAULT_SCHEMA不要设成租户名,只能是public - 检查
Domain表是否为每个租户正确绑定tenant_id和domain(如acme.myapp.com)
迁移和 shell 下如何切换 schema?
python manage.py shell 默认连 public,查租户数据会报 relation "myapp_mymodel" does not exist。这不是代码错了,是 schema 没切过去。
别手动改 connection.set_schema()——容易漏恢复,引发后续请求混乱。要用官方方式:
- 进租户 shell:
python manage.py tenants_command shell --schema=acme - 跑指定租户迁移:
python manage.py migrate_schemas --schema=acme - 执行 SQL 前先确认当前 schema:
connection.schema_name(返回字符串,不是对象) - 写管理命令时,用
schema_context上下文管理器,避免跨请求污染:from tenant_schemas.utils import schema_context with schema_context('acme'): MyModel.objects.create(...)
复杂点在于:信号(post_save 等)默认在当前 schema 触发,如果在 public 里触发了创建租户逻辑,但没手动切 schema,新租户的初始化数据就可能写进 public。这种隐式耦合,得靠单元测试+日志盯住。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











