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

如何减少Django模型重复代码并优化?继承报错求解决方案

Django重复模型类的代码优化方案

问题场景

存在多个结构高度相似的Django模型类,重复代码较多,示例如下:

class One(models.Model):
    CHOICES = (
        ('Text1', 'Text1'),
        ('Text2', 'Text2'),
        ('Text3', 'Text3'),
    )
    name = models.CharField(
        max_length=50,
        verbose_name='Name1',
        choices=CHOICES,
        unique=True
    )

    class Meta:
        ordering = ('id',)
        verbose_name = 'Name'
        verbose_name_plural = 'Names'

    def __str__(self):
        return self.name


class Two(models.Model):
    CHOICES = (
        ('Text4', 'Text4'),
        ('Text5', 'Text5'),
    )
    name = models.CharField(
        max_length=50,
        verbose_name='Name1',
        choices=CHOICES,
        unique=True
    )

    class Meta:
        ordering = ('id',)
        verbose_name = 'Name'
        verbose_name_plural = 'Names'

    def __str__(self):
        return self.name

需要优化以减少代码冗余。

尝试的继承方案及报错

尝试通过继承普通models.Model基类实现复用,但执行迁移时报错:

错误信息:你正尝试为模型one添加一个不可为空的字段'choices_ptr',但未设置默认值;我们无法完成此操作(数据库需要填充已有行的字段值)。

尝试的代码如下:

class Choices(models.Model):
    CHOICES = (('.', '.'),)
    name = models.CharField(
        max_length=50,
        verbose_name='Name',
        choices=CHOICES,
        unique=True,
    )

    class Meta:
        ordering = ('id',)

    def __str__(self):
        return self.name


class One(Choices):
    CHOICES = (
        ('Text1', 'Text1'),
        ('Text2', 'Text2'),
        ('Text3', 'Text3'),
    )

    class Meta:
        verbose_name = 'Name'
        verbose_name_plural = 'Names'


class Two(Choices):
    CHOICES = (
        ('Text4', 'Text4'),
        ('Text5', 'Text5'),
    )

    class Meta:
        verbose_name = 'Name'
        verbose_name_plural = 'Names'

报错原因

普通的Django模型继承属于多表继承,Django会为父类创建独立的数据库表,子类表会自动添加一个指向父类的外键字段(如choices_ptr)。如果子类对应的数据库表已存在(之前已执行过迁移),新增父类继承时,数据库需要为已有行填充这个外键字段的值,但未设置默认值,因此触发错误。

正确优化方案:使用抽象基类

将基类标记为抽象基类(在Meta中设置abstract=True),Django不会为抽象基类创建数据库表,仅复用其字段、方法和元数据,不会生成额外的外键字段,完美解决代码冗余问题。

修改后的代码如下:

class ChoicesBase(models.Model):
    name = models.CharField(
        max_length=50,
        verbose_name='Name',
        unique=True,
    )

    class Meta:
        abstract = True  # 标记为抽象基类,不生成数据库表
        ordering = ('id',)

    def __str__(self):
        return self.name


class One(ChoicesBase):
    CHOICES = (
        ('Text1', 'Text1'),
        ('Text2', 'Text2'),
        ('Text3', 'Text3'),
    )
    # 重写name字段,仅修改choices和verbose_name参数
    name = models.CharField(
        max_length=50,
        verbose_name='Name1',
        choices=CHOICES,
        unique=True,
    )

    class Meta(ChoicesBase.Meta):
        # 继承父类Meta的ordering,仅添加自定义的verbose_name
        verbose_name = 'Name'
        verbose_name_plural = 'Names'


class Two(ChoicesBase):
    CHOICES = (
        ('Text4', 'Text4'),
        ('Text5', 'Text5'),
    )
    name = models.CharField(
        max_length=50,
        verbose_name='Name1',
        choices=CHOICES,
        unique=True,
    )

    class Meta(ChoicesBase.Meta):
        verbose_name = 'Name'
        verbose_name_plural = 'Names'

进一步优化(可选)

如果verbose_name等元数据也存在重复,可以使用类工厂函数动态生成模型类,示例如下:

def create_choice_model(choices, verbose_name, verbose_name_plural):
    class ChoiceModel(ChoicesBase):
        CHOICES = choices
        name = models.CharField(
            max_length=50,
            verbose_name='Name1',
            choices=CHOICES,
            unique=True,
        )

        class Meta(ChoicesBase.Meta):
            verbose_name = verbose_name
            verbose_name_plural = verbose_name_plural
            # 必须设置db_table,避免动态生成的模型类表名冲突
            db_table = f'app_{verbose_name.lower()}'
    
    return ChoiceModel

# 生成模型类
One = create_choice_model(
    choices=(('Text1', 'Text1'), ('Text2', 'Text2'), ('Text3', 'Text3')),
    verbose_name='Name',
    verbose_name_plural='Names'
)

Two = create_choice_model(
    choices=(('Text4', 'Text4'), ('Text5', 'Text5')),
    verbose_name='Name',
    verbose_name_plural='Names'
)

注意:动态生成模型类需要确保db_table唯一,避免数据库表名冲突。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 10:40:16