Django formset validation - is_valid() == True, но возвращает пустую дикту clean_data для последней формы

У меня возникла проблема, когда я проверяю набор форм и когда я не предоставляю данные для (не обязательного) поля, он возвращает пустой dict. Чтобы понять, где я ошибаюсь, я создал сокращенную версию формы, с меньшим количеством полей, но точно такой же логикой init. Сокращенная форма работает как ожидалось, когда поле присутствует и удаляется, но полная форма не работает. Поскольку кроме количества полей нет никакой разницы между формами и входными данными, я немного озадачен, почему.

Для ясности - поля формы создаются динамически на основе initial_data или (POST) data (из kwargs) (с использованием field_mappings диктанта).

class MyScaledDownForm(forms.Form):

    field_mappings = {
        "id": {
            "field": forms.IntegerField(
                help_text="",
                widget=forms.HiddenInput(),
            )
        },
        "defender": {"field": forms.CharField(help_text="", required=False)},
        "sid_number": {
            "field": forms.CharField(
                required=False,
                widget=forms.HiddenInput(),
                help_text="",
            )
        },
    }

    def __init__(self, *args, **kwargs):

        super().__init__(*args, **kwargs)

        initial_data = kwargs.get("initial")
        validation_data = kwargs.get("data")

        if initial_data:
            for k, v in initial_data.items():
                field_mapping = self.field_mappings.get(k)
                if field_mapping:
                    self.fields[k] = field_mapping["field"]
                    self.fields[k].initial = v

        elif validation_data:
            for k, v in validation_data.items():
                mk = k.split("-")[-1]
                field_mapping = self.field_mappings.get(mk)
                if field_mapping:
                    self.fields[mk] = field_mapping["field"]

        else:
            if args:
                for k, v in args[0].items():
                    field_mapping = self.field_mappings.get(k)
                    if field_mapping:
                        self.fields[k] = field_mapping["field"]

            elif kwargs:
                for k, v in kwargs.items():
                    k = k.split("-")[-1]
                    field_mapping = self.field_mappings.get(k)
                    if field_mapping:
                        self.fields[k] = field_mapping["field"]


MyScaledDownFormSet = forms.formset_factory(
    form=MyScaledDownForm,
    extra=0,
)

а это полная форма с большим количеством полей, но точно такой же init логикой:

Я ожидаю, что при одинаковых данных для проверки обе формы будут иметь одинаковые результаты, но это не так.

formset_input = {
    "form-TOTAL_FORMS": "2",
    "form-INITIAL_FORMS": "0",
    "form-0-id": 7,
    "form-0-defender": "defender_zero",
    "form-1-id": 8,
    "form-1-defender": "defender_one",
}

scaled_down_formset = MyScaledDownFormSet(request.POST)
my_formset = self.formset(request.POST)

print(f"my_formset - {scaled_down_formset.is_valid()}, {scaled_down_formset.cleaned_data}")
print(f"formset - {my_formset.is_valid()}, {my_formset.cleaned_data}")

Выход

scaled_down_formset - True, [{'id': 7, 'defender': 'defender_zero'}, {'id': 8, 'defender': 'defender_one'}]
my_formset - True, [{'id': 7, 'defender': 'defender_zero'}, {'id': 8, 'defender': 'defender_one'}]

Так что все в порядке, и работает как ожидалось. Теперь, когда я удаляю поля "защитника" из ввода, только одна форма работает так, как ожидалось:

formset_input = {
    "form-TOTAL_FORMS": "2",
    "form-INITIAL_FORMS": "0",
    "form-new_comment": "Banana",
    "form-0-id": 7,
    # "form-0-defender": "defender_zero",
    "form-1-id": 8,
    # "form-1-defender": "defender_one",
}

scaled_down_formset = MyScaledDownFormSet(request.POST)
my_formset = self.formset(request.POST)

print(f"my_formset - {scaled_down_formset.is_valid()}, {scaled_down_formset.cleaned_data}")
print(f"formset - {my_formset.is_valid()}, {my_formset.cleaned_data}")

Выход

scaled_down_formset - True, [{'id': 7}, {'id': 8}] # cleaned data returns both dicts
my_formset - True, [{'id': 7}, {}] # cleaned data returns only the first dict
Вернуться на верх