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

Django StackedPolymorphicInline报empty_form错误如何修复

问题描述
  • 基于Django REST Framework开发Django API项目,共定义4个数据模型,独立的多态模型后台管理功能可正常运行
  • 为Module模型配置StackedPolymorphicInline关联Level多态模型后,访问Module后台修改页直接抛出异常
  • 注释掉ModuleAdmin中的inlines字段配置后,项目运行无任何异常
  • 运行环境:Django 4.0.5、Python 3.9.7,已安装应用包含django.contrib.admin、rest_framework、polymorphic
相关代码

模型定义

class Module(models.Model):
    name = models.CharField(null=False, max_length=50, verbose_name='название')
    description = models.TextField(null=False, verbose_name='описание')
    image = models.ImageField(null=False, upload_to="image/module", verbose_name='картинка модуля')
    first_level = models.ForeignKey('plotbackend.Level', null=False, db_column='first_level_id', on_delete=models.PROTECT, related_name='+', verbose_name='первый уровень')

    def __str__(self):
        return self.name

    class Meta:
        db_table = 'module'
        verbose_name = "модуль"
        verbose_name_plural = 'модули'


class Level(PolymorphicModel):
    name = models.CharField(null=True, blank=True, max_length=25, verbose_name="название")
    description = models.TextField(null=True, blank=True, verbose_name="описание")
    module = models.ForeignKey(Module, null=True, blank=True, verbose_name='модуль', db_column='module_id', on_delete=models.CASCADE, related_name='levels')
    next_level = models.ForeignKey('self', null=True, blank=True, verbose_name='следующий уровень', db_column='next_level_id', on_delete=models.SET_NULL)

    def __str__(self):
        return f'{self.name}'

    class Meta:
        db_table = 'level'
        verbose_name = "уровень"
        verbose_name_plural = 'уровни'


class Puzzle(Level):
    time = models.PositiveIntegerField(null=False)


class Test(Level):
    time = models.IntegerField(null=True, blank=True, verbose_name='время на прохождение теста')

    class Meta:
        db_table = 'test'
        verbose_name = "тест"
        verbose_name_plural = "тесты"

Admin后台配置

from django.contrib import admin
from polymorphic.admin import StackedPolymorphicInline, PolymorphicInlineSupportMixin, PolymorphicChildModelAdmin, PolymorphicParentModelAdmin
from .models import *

@admin.register(Test)
class TestAdmin(PolymorphicChildModelAdmin):
    base_model = Test


@admin.register(Puzzle)
class PuzzleAdmin(PolymorphicChildModelAdmin):
    base_model = Puzzle


@admin.register(Level)
class LevelAdmin(PolymorphicParentModelAdmin):
    base_model = Level
    child_models = (Test, Puzzle, )


class LevelInline(StackedPolymorphicInline):
    class TestInline(StackedPolymorphicInline.Child):
        model = Test

    class PuzzleInline(StackedPolymorphicInline.Child):
        model = Puzzle

    model = Level
    child_inlines = (
        TestInline,
        PuzzleInline,
    )


@admin.register(Module)
class ModuleAdmin(admin.ModelAdmin, PolymorphicInlineSupportMixin):
    inlines = (LevelInline,)
报错信息

访问http://127.0.0.1:8000/admin/plotbackend/module/1/change/时抛出异常,核心错误:

Exception Type: RuntimeError
Exception Value: 'empty_form' is not used in polymorphic formsets, use 'empty_forms' instead.

错误触发逻辑:

  1. Django admin渲染修改页时,会计算内联表单集的media静态资源
  2. 迭代内联表单集过程中,Django 4.x版本默认访问formset.empty_form属性生成空表单模板
  3. django-polymorphic的多态表单集未实现empty_form属性,仅提供适配多子模型的empty_forms属性,访问时主动抛出RuntimeError提示使用正确属性
解决方案

该问题是django-polymorphic与Django 4.0+版本的兼容性bug,选择以下任意一种方案即可修复:

  • 方案一:降级Django版本到3.2 LTS,该版本admin逻辑不会强制访问empty_form属性,与当前稳定版django-polymorphic完全兼容
  • 方案二:保留Django 4.x版本,为多态内联表单集添加兼容补丁,在项目admin.py或任意初始化加载的文件顶部添加如下代码:
from polymorphic.formsets.models import BasePolymorphicModelFormSet

def _get_empty_form_compat(self):
    return list(self.empty_forms.values())[0]

BasePolymorphicModelFormSet.empty_form = property(_get_empty_form_compat)

补丁会让empty_form属性返回第一个子类型的空表单,兼容Django 4.x admin的渲染逻辑,不影响原有多态内联的empty_forms功能。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 00:48:25