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.
错误触发逻辑:
- Django admin渲染修改页时,会计算内联表单集的media静态资源
- 迭代内联表单集过程中,Django 4.x版本默认访问
formset.empty_form属性生成空表单模板 - 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
相关产品推荐
相关产品推荐

