【发布时间】:2023-01-31 18:04:34
【问题描述】:
我有一个用例,我需要将动态表单字段添加到 WagtailModelAdminForm。使用标准的 Django,我通常只创建一个自定义子类并在表单的 __init__ 方法中添加字段。在 Wagtail 中,因为表单是用 edit_handlers 构建的,所以这变成了一个噩梦。
我有以下动态形式:
class ProductForm(WagtailAdminModelForm):
class Meta:
model = get_product_model()
exclude = ['attributes', 'state', 'variant_of']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance:
self.inject_attribute_fields()
def inject_attribute_fields(self):
for k, attr in self.instance.attributes.items():
field_klass = None
field_data = attr.get("input")
field_args = {
'label': field_data['name'],
'help_text': field_data['help_text'],
'required': field_data['is_required'],
'initial': attr['value'],
}
if 'choices' in field_data:
field_args['choices'] = (
(choice["id"], choice["value"])
for choice in field_data['choices']
)
if field_data['is_multi_choice']:
field_klass = forms.MultipleChoiceField
else:
field_klass = forms.ChoiceField
else:
typ = field_data['attr_type']
if typ == 'text':
field_klass = forms.CharField
elif typ == 'textarea':
field_klass = forms.CharField
field_args['widget'] = forms.Textarea
elif typ == 'bool':
field_klass = forms.BooleanField
elif typ == 'int':
field_klass = forms.IntegerField
elif typ == 'decimal':
field_klass = forms.DecimalField
elif typ == 'date':
field_klass = forms.DateField
field_args['widget'] = AdminDateInput
elif typ == 'time':
field_klass = forms.TimeField
field_args['widget'] = AdminTimeInput
elif typ == 'datetime':
field_klass = forms.DateTimeField
field_args['widget'] = AdminDateTimeInput
if field_klass is None:
raise AttributeError('Cannot create widgets for invalid field types.')
# Create the custom key
self.fields[f"attributes__{k}"] = field_klass(**field_args)
接下来我自定义了 ModelAdmin EditView(创建视图中不存在属性):
class EditProductView(EditView):
def get_edit_handler(self):
summary_panels = [
FieldPanel('title'),
FieldPanel('description'),
FieldPanel('body'),
]
# NOTE: Product attributes are dynamic, so we generate them
attributes_panel = get_product_attributes_panel(self.instance)
variants_panel = []
if self.instance.is_variant:
variants_panel.append(
InlinePanel(
'stockrecords',
classname="collapsed",
heading="Variants & Prices"
)
)
else:
variants_panel.append(ProductVariantsPanel())
return TabbedInterface([
ObjectList(summary_panels, heading='Summary'),
# This panel creates dynamic panels related to the dynamic form fields,
# but raises an error saying that the "fields are missing".
# Understandable because it's not present on the original model
# ObjectList(attributes_panel, heading='Attributes'),
ObjectList(variants_panel, heading='Variants'),
ObjectList(promote_panels, heading='Promote'),
ObjectList(settings_panels, heading='Settings'),
], base_form_class=ProductForm).bind_to_model(self.model_admin.model)
这里是get_product_attributes_panel()函数供参考:
def get_product_attributes_panel(product) -> list:
panels = []
for key, attr in product.attributes.items():
widget = None
field_name = "attributes__" + key
attr_type = attr['input'].get('attr_type')
if attr_type == 'date':
widget = AdminDateInput()
elif attr_type == 'datetime':
widget = AdminDateTimeInput()
else:
if attr_type is None and 'choices' in attr['input']:
if attr['input']['is_multi_choice']:
widget = forms.SelectMultiple
else:
widget = forms.Select
else:
widget = forms.TextInput()
if widget:
panels.append(FieldPanel(field_name, widget=widget))
else:
panels.append(FieldPanel(field_name))
return panels
所以问题是...
A) 按照我上面的方式添加 ProductForm(通过将其用作 TabbedInterface 中的 base_form_class)几乎可以工作;它将字段添加到表单中;但是我无法控制渲染。
B) 如果我取消评论ObjectList(attributes_panel, heading='Attributes'), 行(为了更好地呈现字段),然后我的动态字段出现错误,说它们丢失了。
这是我正在从事的项目中非常重要的要求。
一个临时的解决方法是创建一个自定义面板直接在 html 模板中呈现动态字段;但是后来我失去了 Django 表单验证,这也是一个重要的要求。
有没有办法在 WagtailModelAdminForm 中添加动态字段,那果酱modeladmin 功能,如表单集、权限等。
【问题讨论】:
标签: python django wagtail wagtail-admin