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

使用Django路由转储django-simple-history历史数据至其他数据库报错

解决django-simple-history跨库存储历史记录的关联表错误

问题描述

使用django-simple-history追踪模型变更记录,需求是将历史数据单独存储到另一数据库。配置Django数据库路由后,触发以下错误:

django.db.utils.OperationalError: (1824, "Failed to open the referenced table 'common_user'")

原因是common_user表仅存在于默认数据库,而历史数据库中无此表,但自动生成的历史模型包含指向该表的外键约束,导致读写历史记录时触发表不存在的错误。

解决方案

1. 自定义历史模型,禁用外键约束

django-simple-history生成的历史模型会继承原模型的外键字段,默认会创建数据库级外键约束。需要自定义历史模型,将所有外键的db_constraint设为False,避免历史数据库依赖默认库的关联表。

修改models.py:

from django.db import models
from simple_history.models import HistoricalRecords, HistoricalModel
from django.contrib.auth.models import User
import uuid

# 自定义历史模型
class HistoricalProfile(HistoricalModel):
    profile_type = models.ForeignKey('ProfileType', on_delete=models.PROTECT, db_constraint=False)
    user = models.ForeignKey(User, on_delete=models.CASCADE, db_constraint=False)
    org = models.ForeignKey('Org', null=True, on_delete=models.CASCADE, blank=True, related_name="historical_user_org", db_constraint=False)
    nickname = models.CharField(max_length=64, unique=True, blank=False, null=False)
    is_active = models.BooleanField(default=True)
    is_section_admin = models.BooleanField(default=False)
    is_organization_admin = models.BooleanField(default=False)
    is_organization_member = models.BooleanField(default=False)
    date_of_joining = models.DateField(auto_now_add=True, null=True, blank=True)
    referral = models.CharField(max_length=64, blank=True, null=True)
    referred = models.CharField(max_length=64, blank=False, null=False, unique=True, default=uuid.uuid4().hex)

    class Meta:
        proxy = True

class Profile(models.Model):
    profile_type = models.ForeignKey('ProfileType', on_delete=models.PROTECT)
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    nickname = models.CharField(
        max_length=64, unique=True, blank=False, null=False)
    org = models.ForeignKey(
        'Org', null=True, on_delete=models.CASCADE, blank=True, related_name="user_org"
    )
    address = models.ManyToManyField('Address', blank=True)
    is_active = models.BooleanField(default=True)
    is_section_admin = models.BooleanField(default=False)
    is_organization_admin = models.BooleanField(default=False)
    is_organization_member = models.BooleanField(default=False)
    date_of_joining = models.DateField(auto_now_add=True, null=True, blank=True)
    referral = models.CharField(
        max_length=64, blank=True, null=True)
    referred = models.CharField(
        max_length=64, blank=False, null=False, unique=True, default=uuid.uuid4().hex)
    # 指定自定义历史模型
    history = HistoricalRecords(m2m_fields=(address, ), use_base_model_db=False, cascade_delete_history=True, history_model=HistoricalProfile)

2. 修正数据库路由逻辑

调整路由的allow_migrate方法,确保历史模型仅在history_db执行迁移,其他模型仅在default库迁移,同时处理django-simple-history的迁移提示。

修改routers.py:

class HistoryRouter:
    def db_for_read(self, model, **hints):
        if model._meta.model_name.startswith('historical'):
            return 'history_db'
        return 'default'

    def db_for_write(self, model, **hints):
        if model._meta.model_name.startswith('historical'):
            return 'history_db'
        return 'default'

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        # 处理simple-history传递的模型提示
        if hints.get('model') and hints['model']._meta.model_name.startswith('historical'):
            return db == 'history_db'
        # 匹配历史模型名称
        if model_name and model_name.startswith('historical'):
            return db == 'history_db'
        # 非历史模型仅在默认库迁移
        return db == 'default'

3. 重新执行数据库迁移

按顺序执行迁移,确保原模型和历史模型分别在对应数据库创建表:

  1. 迁移默认数据库:
python manage.py migrate --database default
  1. 迁移历史数据库:
python manage.py migrate --database history_db

若之前已在history_db执行过错误迁移,需清空该库的迁移记录(删除django_migrations表中对应条目)后重新迁移。

关键说明

  • db_constraint=False仅禁用数据库级外键约束,Django ORM层面的关联查询仍可正常使用,且历史记录的外键值均来自原模型的合法数据,无需担心数据一致性问题。
  • use_base_model_db=False确保历史记录的读写操作完全路由到history_db,符合单独存储历史数据的需求。

内容的提问来源于stack exchange,提问作者Reza Teymoori

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 14:35:01