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

如何在不修改父类时排除子类的django-simple-history HistoricalRecords

问题描述

需要在不修改父类BaseModel的前提下,让子类Manager不继承django-simple-history的HistoricalRecords。现有代码中BaseModel包含history = HistoricalRecords(inherit=True),尝试在Manager类中设置history=None以及在__init__方法中清理字段,但执行makemigration时仍会为Manager生成历史记录。由于BaseModel和CommonWithExtraFieldsWithoutVersioning被广泛使用无法修改,寻求解决方案。

原代码:

from simple_history.models import HistoricalRecords
class BaseModel(models.Model):
    id = models.UUIDField(...some args...)
    created_at = models.DateTimeField(...some args...)
    created_by = models.ForeignKey(...some args...)
    history = HistoricalRecords(inherit=True)

    class Meta:
        abstract = True
        default_permissions = Constants.STANDARD_ACTIONS

    def save(self, suppress_modified=False, from_publish=False, **kwargs):
        super(BaseModel, self).save(**kwargs)


class CommonWithExtraFieldsWithoutVersioning(BaseModel, CommonWithoutVersioning, models.Model):
    class Meta(CommonWithoutVersioning.Meta):
        abstract = True


class Manager(CommonWithExtraFieldsWithoutVersioning):
    from_branch = models.CharField(...some args...)
    to_branch = models.CharField(...some args...)
    from_version = models.IntegerField(...some args...)
    to_version = models.IntegerField(...some args...)
    history = None # 无效
    def __init__(self, *args, **kwargs):
        super(DSLJourneyMergeHistory, self).__init__(*args, **kwargs)
        self.clean_fields('history') # 无效
解决方案

直接设置history=None无法生效,因为django-simple-history的inherit=True会让子类自动继承历史字段,且makemigrations会检测到父类的字段定义。可以通过自定义元类在Manager类创建时彻底移除history字段,避免生成历史表:

from django.db import models
from simple_history.models import HistoricalRecords

# 自定义元类:移除继承的history字段
class DisableHistoryMeta(models.base.ModelBase):
    def __new__(cls, name, bases, attrs):
        # 先移除类属性中的history
        if 'history' in attrs:
            del attrs['history']
        # 创建模型类
        model_class = super().__new__(cls, name, bases, attrs)
        # 从模型的元数据中移除history字段
        model_class._meta.fields = [field for field in model_class._meta.fields if field.name != 'history']
        model_class._meta.local_fields = [field for field in model_class._meta.local_fields if field.name != 'history']
        # 删除类上的history属性(如果存在)
        if hasattr(model_class, 'history'):
            delattr(model_class, 'history')
        return model_class

# 修改Manager类,使用自定义元类
class Manager(CommonWithExtraFieldsWithoutVersioning, metaclass=DisableHistoryMeta):
    from_branch = models.CharField(...some args...)
    to_branch = models.CharField(...some args...)
    from_version = models.IntegerField(...some args...)
    to_version = models.IntegerField(...some args...)

    # 不需要再设置history=None或在__init__中清理

原理说明

  • 元类会在模型类创建的早期阶段介入,直接移除history相关的属性和字段定义,让django的迁移系统无法检测到该字段。
  • 这种方式不会影响其他继承BaseModel的子类,仅对使用该元类的Manager类生效。

内容的提问来源于stack exchange,提问作者Shubham Srivastava

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 01:15:37