如何在django-simple-history中处理Django级联删除的对象?
问题描述
数据库内有多张子表继承自父表,且配置了级联删除。使用django-simple-history查询历史对象时,父表的历史记录可正常获取,但查询被Django自动删除的子表对象历史时,触发如下错误:
MyApp.models.parentTable_object.DoesNotExist: parentTable_object matching query does not exist.
推测问题根源:django-simple-history尝试为已删除的子表对象创建历史记录,但此时该子表继承的父对象已被级联删除,导致创建历史对象时因无法找到父对象而失败。无法取消级联删除,需找到适配django-simple-history的解决方案。
环境信息
- 操作系统:Windows 10
- 浏览器:Firefox
- Django Simple History版本:3.1.1
- Django版本:4.0.3
- 数据库:Django集成的SQLite3
解决办法
方案1:修改历史模型的外键关联行为
django-simple-history生成的子表历史模型会关联父表对象,可通过自定义历史模型,将该外键的on_delete设为SET_NULL并允许为空,避免父对象删除后触发错误:
- 自定义子表的历史模型:
from django.db import models from simple_history.models import HistoricalRecords, ModelHistory class ChildTableHistory(ModelHistory): parent = models.ForeignKey( 'parentTable_object', on_delete=models.SET_NULL, null=True, blank=True, related_name='child_histories' ) class Meta: proxy = True
- 在子表模型中指定使用该自定义历史模型:
class ChildTable(ParentTable): # 子表自定义字段 history = HistoricalRecords(model=ChildTableHistory)
父对象被级联删除时,子表历史对象的父外键会被设为null,不会触发DoesNotExist异常。
方案2:调整历史记录的创建时机
通过自定义信号处理,确保子表的历史记录在父对象删除前完成创建:
- 断开子表默认的
post_delete历史记录信号:
class ChildTable(ParentTable): history = HistoricalRecords() # 移除默认的删除后创建历史的信号绑定 post_delete.disconnect(ChildTable.history.post_delete, sender=ChildTable)
- 编写
pre_delete信号处理函数,在父对象删除前为所有关联子表创建历史记录:
from django.db.models.signals import pre_delete from django.dispatch import receiver @receiver(pre_delete, sender=ParentTable) def save_child_histories_before_parent_delete(sender, instance, **kwargs): # 获取当前父对象关联的所有子表对象 child_objects = instance.childtable_set.all() for child in child_objects: # 手动创建删除操作的历史记录 child.history.create( history_type='-', history_user=getattr(instance, '_history_user', None), **{field.name: getattr(child, field.name) for field in child._meta.fields} )
此方案会在父对象被删除前,先完成所有关联子表的历史记录创建,此时父对象仍存在,不会出现找不到的问题。
方案3:启用历史记录的继承模式
如果使用的是Django多表继承,可在子表的HistoricalRecords中设置inherit=True,让子表历史模型直接包含父表的所有字段,而非通过外键关联父对象:
class ChildTable(ParentTable): # 子表自定义字段 history = HistoricalRecords(inherit=True)
启用该参数后,子表的历史记录会独立存储父表字段数据,无需依赖父对象的存在,自然避免级联删除引发的错误。
内容的提问来源于stack exchange,提问作者lewis32
相关产品推荐
相关产品推荐

