Условный Inline в админской панели Django - ошибка "ManagementForm data is missing or has been tampered with"
Основной текст заметки
Иногда требуется делать условный Inline. как его сделать - много где описано - но в ситуации когда условие отображения инлайна изменяется в процессе редактирования, вылезает ошибка `ManagementForm data is missing or has been tampered with`
Что бы ее избежать приходится сохранять значение атрибута и использовать его при проверке.
Ниже приведен пример
Программный код
==============
admin.py
==============
class CharacterAdmin(admin.ModelAdmin):
model = Character
inlines = [CharacterValueInline]
prepopulated_fields = {"handle": ("internal_name",), "public_name": ("internal_name",)}
def get_inline_instances(self, request, obj:Character=None):
inline_instances = []
for inline_class in self.get_inlines(request, obj):
inline = inline_class(self.model, self.admin_site)
if request:
if not obj or obj._original_character_type != obj.CHARACTER_TYPE_SET:
continue
if not (inline.has_view_or_change_permission(request, obj) or
inline.has_add_permission(request, obj) or
inline.has_delete_permission(request, obj)):
continue
if not inline.has_add_permission(request, obj):
inline.max_num = 0
inline_instances.append(inline)
return inline_instances
def get_formsets_with_inlines(self, request, obj:Character=None):
for inline in self.get_inline_instances(request, obj):
# hide/show market-specific inlines based on market name
if obj and obj._original_character_type == obj.CHARACTER_TYPE_SET:
yield inline.get_formset(request, obj), inline
==============
models.py
==============
class Character(models.Model):
"""
Класс реализует характеристики товара
"""
class Meta:
verbose_name = _('Character')
verbose_name_plural = _('Characters')
app_label = 'store'
internal_name = models.CharField(
max_length=100,
null=False,
blank=False,
unique=True,
verbose_name=_('Internal character name'),
)
handle = models.SlugField(
max_length=32,
null=False,
blank=False,
unique=True,
verbose_name=_('Hadle'),
help_text=_('case insensitive')
)
public_name = models.CharField(
max_length=100,
null=True,
blank=True,
verbose_name=_('Public character name'),
)
name = property(lambda self:self.public_name if self.public_name else self.internal_name)
CHARACTER_TYPE_INHERITED = 0x1000
CHARACTER_TYPE_INTEGER = 0x0001
CHARACTER_TYPE_FLOAT = 0x0002
CHARACTER_TYPE_DECIMAL = 0x0003
CHARACTER_TYPE_SET = 0x0004
CHARACTER_TYPES = (
(CHARACTER_TYPE_INTEGER, _('Integer value')),
(CHARACTER_TYPE_FLOAT, _('Float value')),
(CHARACTER_TYPE_DECIMAL, _('Fixed decimal value')),
(CHARACTER_TYPE_SET, _('Set of string values')),
)
_original_character_type = None
character_type = models.IntegerField(
null=False,
blank=False,
default=CHARACTER_TYPE_INTEGER,
choices=CHARACTER_TYPES
)
def __init__(self, *args, **kwargs):
super(Character, self).__init__(*args, **kwargs)
self._original_character_type = self.character_type
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
self.handle = self.handle.upper() # Переводим значение в верхний регистр
return super().save(force_insert, force_update, using, update_fields)
def __str__(self):
return self.internal_name
Заметка написана: 25.05.2020
Теги заметки: Django