如何实现Django模型跨PostgreSQL Schema关联非托管多对多表
解决方案:跨Schema只读模型关联问题
看起来你已经找对了方向,但在模型配置和数据库路由的细节上有些遗漏,导致Django仍在错误的schema创建表。以下是一步步的修正方案:
1. 修正Authority模型的Schema指定方式
首先,确保模型正确指向lists schema下的authority_list表。PostgreSQL中跨schema的表名需要正确格式化,避免Django解析错误:
class Authority(models.Model): id = models.IntegerField(primary_key=True) code = models.IntegerField() name = models.CharField(max_length=255) class Meta: managed = False # 用双引号包裹schema和表名,确保PostgreSQL正确识别(尤其如果名称包含大写或特殊字符) db_table = '"lists"."authority_list"'
如果你的schema和表名都是小写,也可以简化为db_table = 'lists.authority_list',但双引号的写法更稳妥。
2. 完善数据库路由逻辑
你的路由只处理了模型的读写,但缺少关联权限控制和迁移过滤,这是导致多对多关联异常和表创建问题的关键。更新路由如下:
from yourapp.models import Authority, Doc ROUTED_MODELS = [Authority] class DefaultRouter: def db_for_read(self, model, **hints): # 读取Authority时路由到lists数据库 if model in ROUTED_MODELS: return 'lists' return None def db_for_write(self, model, **hints): # 禁止写入Authority模型 if model in ROUTED_MODELS: raise Exception("Authority model is read-only!") return None def allow_relation(self, obj1, obj2, **hints): # 允许Doc与Authority及其多对多中间表的关联 if (obj1._meta.model in ROUTED_MODELS or obj2._meta.model in ROUTED_MODELS) and \ (obj1._meta.model == Doc or obj2._meta.model == Doc): return True # 允许同一数据库内的其他关联 if obj1._meta.db_table == obj2._meta.db_table: return True return None def allow_migrate(self, db, app_label, model_name=None, **hints): model = hints.get('model') # 完全禁止迁移Authority模型(不管哪个数据库) if model and model in ROUTED_MODELS: return False # 只在default数据库迁移其他模型 if db == 'default': return True return False
这个路由做了三件关键的事:
- 明确禁止迁移Authority模型,避免Django尝试创建表
- 允许Doc和Authority之间的跨数据库关联
- 确保读写操作的路由规则正确
3. 清理迁移并确认权限
步骤1:处理现有迁移
因为之前可能生成了创建authority_list表的迁移文件,需要清理:
- 删除应用中涉及创建
Authority表的迁移文件 - 运行空迁移标记该模型已处理:
python manage.py makemigrations yourapp --empty - 用fake命令让Django认为该迁移已应用:
python manage.py migrate yourapp --fake
步骤2:确认数据库权限
确保你的数据库用户拥有:
listsschema下authority_list表的SELECT权限(只读)intakeschema下所有表(包括自动生成的多对多中间表)的读写权限
4. 验证多对多关联
完成上述配置后,Doc模型的多对多关联会正常工作:
- 多对多中间表会创建在
intakeschema下(属于Doc所在的default数据库) - 查询
doc.authorities.all()时,Django会自动路由到lists数据库读取数据 - 写入
doc.authorities.add(authority)时,只会更新intake下的中间表,不会修改lists的authority_list表
关于原表每周重建的注意事项
因为authority_list表会被定期删除重建,只要重建后的表结构和你的模型字段完全匹配(id、code、name),就不会有问题。如果重建时字段有变化,需要同步更新你的Authority模型。
内容的提问来源于stack exchange,提问作者Rob
相关产品推荐
相关产品推荐

