Правильный способ добавления полей динамической формы в WagtailModelAdminForm

У меня есть вариант использования, когда мне нужно добавить поля динамической формы в WagtailModelAdminForm. Со стандартным django я обычно просто создаю собственный подкласс и добавляю поля в метод __init__ формы. В трясогузке из-за того, что формы создаются с помощью обработчиков edit_handler, это становится кошмаром.

У меня есть следующая динамическая форма:

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 так, как я сделал выше (используя его как base_form_class в TabbedInterface), почти работает; Он добавляет поля в форму; НО я не контролирую рендеринг.

Б) Если я раскомментирую строку ObjectList(attributes_panel, heading='Attributes'), (чтобы получить хорошую визуализацию полей), то я получаю ошибку для моих динамических полей, говоря, что они отсутствуют.

Это очень важное требование в проекте, над которым я работаю.

Временным решением является создание пользовательской панели для отображения динамических полей непосредственно в HTML-шаблоне; Но тогда я теряю проверку формы Django, что также является важным требованием для этого.

Есть ли способ добавить динамические поля в WagtailModelAdminForm, которые сохраняют функции modeladmin, такие как наборы форм, разрешения и т. д.

Инструменты для веб-скрапинга с открытым исходным кодом: Python Developer Toolkit
Инструменты для веб-скрапинга с открытым исходным кодом: Python Developer Toolkit
Веб-скрейпинг, как мы все знаем, это дисциплина, которая развивается с течением времени. Появляются все более сложные средства борьбы с ботами, а...
Библиотека для работы с мороженым
Библиотека для работы с мороженым
Лично я попрощался с операторами print() в python. Без шуток.
Эмиссия счетов-фактур с помощью Telegram - Python RPA (BotCity)
Эмиссия счетов-фактур с помощью Telegram - Python RPA (BotCity)
Привет, люди RPA, это снова я и я несу подарки! В очередном моем приключении о том, как создавать ботов для облегчения рутины. Вот, думаю, стоит...
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Шаг 1: Создание приложения Slack Чтобы создать Slackbot, вам необходимо создать приложение Slack. Войдите в свою учетную запись Slack и перейдите на...
Учебник по веб-скрапингу
Учебник по веб-скрапингу
Привет, ребята... В этот раз мы поговорим о веб-скрейпинге. Целью этого обсуждения будет узнать и понять, что такое веб-скрейпинг, а также узнать, как...
Тонкая настройка GPT-3 с помощью Anaconda
Тонкая настройка GPT-3 с помощью Anaconda
Зарегистрируйте аккаунт Open ai, а затем получите ключ API ниже.
0
0
53
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В итоге я создал отдельный AttributeForm для атрибутов.

Пользовательская панель затем ищет этот новый экземпляр формы как атрибут основной формы. Например, в основной форме я могу «очистить» эту внутреннюю форму при вызове основной формы clean() и вызвать любые ошибки, которые мне нужны, в обеих формах.

Затем я настроил метод EditView.post(), чтобы добавить экземпляр AttributesForm в форму нашей основной модели.

Это немного обходной путь, но пока работает достаточно хорошо. Я хотел бы, чтобы был более простой способ, но сейчас он не похож на него.

Другие вопросы по теме