Django Formset некорректно заполняет данные формы

Я пытаюсь создать страницу, на которой к заказу можно сделать возврат, с необходимым количеством элементов возврата. Пустые формы для дополнительных ReturnItems генерируются JavaScript, передающим HTML-строку пустой формы, созданной фабрикой форм. Когда формы правильно заполнены, все работает как ожидалось. Однако, когда я пытаюсь отправить сообщение, когда несколько форм ReturnItems пусты, валидация форм не работает должным образом. Я не понимаю, что, хотя данные request.POST верны, поля 'reason' и 'opened' не заполнены для форм, что приводит к тому, что формы оказываются пустыми и пропускаются, вместо того, чтобы выдать ошибку ValidationError из-за отсутствия названия/кода товара.

Любая помощь или предложения были бы полезны, спасибо.

Пример данных request.POST

<QueryDict: {'csrfmiddlewaretoken': ['token'], 'first_name': ['first'], 'last_name': ['last'], 'email': ['e@email.com'], 'telephone': ['12345'], 'order_id': ['123'], 'order_date': ['2022-10-05'], 'form-TOTAL_FORMS': ['2'], 'form-INITIAL_FORMS': ['0'], 'form-MIN_NUM_FORMS': ['1'], 'form-MAX_NUM_FORMS': ['15'], 'form-0-product_name': ['prod1'], 'form-0-product_code': ['code1'], 'form-0-quantity': ['2'], 'form-0-reason': ['1'], 'form-0-opened': ['False'], 'form-1-product_name': ['prod2'], 'form-1-product_code': ['code2'], 'form-1-quantity': ['3'], 'form-1-reason': ['1'], 'form-1-opened': ['False'], 'comment': ['comment']>

Views.py

def return_view(request):
    sitekey = settings.H_CAPTCHA_SITEKEY
    ReturnItemFormSet = formset_factory(
        ReturnItemForm,
        extra=0,
        min_num=1,
        max_num=15,
        validate_min=True,
        absolute_max=15,
    )
    return_form = ReturnForm()
    return_item_formset = ReturnItemFormSet()
    empty_form = return_item_formset.empty_form
    if request.method == "GET":
        context = {
            "return_form": return_form,
            "return_item_formset": return_item_formset,
            "sitekey": sitekey,
            "empty_form_template": empty_form.as_p(),
        }
        return render(request, "shop/returns.html", context)
    if request.method == "POST":
        data = request.POST
        res = requests.post(
            "https://hcaptcha.com/siteverify",
            {
                "secret": settings.H_CAPTCHA_SECRET,
                "response": data.get("h-captcha-response"),
                "sitekey": sitekey,
            },
        )
        if res.ok and res.json()["success"]:
            return_form = ReturnForm(data)
            return_item_formset = ReturnItemFormSet(request.POST)
            if return_form.is_valid() and return_item_formset.is_valid():
                create_return(return_form, return_item_formset) #Logic to create instances
                return redirect(reverse("shop:returns"))
        context = {
            "return_form": return_form,
            "return_item_formset": return_item_formset,
            "sitekey": sitekey,
            "empty_form_template": empty_form.as_p(),
        }
        return render(request, "shop/returns.html", context)

ReturnItem model

class ReturnItem(models.Model):
    RETURN_REASON = [
        (1, "Dead on Arrival"),
        (2, "Faulty (please specify below)"),
        (3, "Item Error"),
        (4, "Order Error"),
        (5, "Other (please specify below)"),
    ]
    PRODUCT_OPENED = [(True, "Yes"), (False, "No")]

    return_info = models.ForeignKey(
        "Return",
        on_delete=models.CASCADE,
        related_name="items",
        related_query_name="item",
    )
    product_name = models.CharField(max_length=200, verbose_name="Product Name")
    product_code = models.CharField(max_length=100, verbose_name="Product Code")
    quantity = models.PositiveSmallIntegerField(verbose_name="Quantity")
    reason = models.SmallIntegerField(
        choices=RETURN_REASON, default=1, verbose_name="Reason for Return"
    )
    opened = models.BooleanField(
        choices=PRODUCT_OPENED, default=False, verbose_name="Product is Opened"
    )

Форма ВозвратИзменения

class ReturnItemForm(StyledModelForm):
    class Meta:
        model = ReturnItem
        fields = ["product_name", "product_code", "quantity", "reason", "opened"]
        widgets = {
            "reason": forms.RadioSelect(),
            "opened": forms.RadioSelect(),
        }

    def __init__(self, *args, **kwargs):
        super(ReturnItemForm, self).__init__(*args, **kwargs)
        for visible_field in self.visible_fields():
            if visible_field.name == "reason" or visible_field.name == "opened":
                visible_field.field.widget.attrs["class"] = ""

    def clean(self):
        cleaned_data = super(ReturnItemForm, self).clean()
        if not (
            cleaned_data.get("product_name")
            or cleaned_data.get("product_code")
            or cleaned_data.get("quantity")
        ):
            raise ValidationError("All fields must filled for the product.")

JS для добавления новых форм

//Dynamic form generation
const totalFormsCount = document.querySelector("[name=form-TOTAL_FORMS]");
const addFormBtn = document.getElementById("btn-add-product");
addFormBtn.addEventListener("click", (e) => {
  e.preventDefault();
  totalFormsCount.setAttribute("value", parseInt(totalFormsCount.value) + 1);
  const newReturnItemForm = new DOMParser().parseFromString(
    formTemplate,
    "text/html"
  );
  //Replace all '__prefix__' of form template with the correct form number for correct tracking.
  let formTemplateAsString = newReturnItemForm.body.innerHTML.replaceAll(
    "__prefix__",
    (parseInt(totalFormsCount.value) - 1).toString()
  );
  addFormBtn.insertAdjacentHTML(
    "beforebegin",
    `<div class="return-item-header">Product ${totalFormsCount.value}</div>`
  );
  addFormBtn.insertAdjacentHTML("beforebegin", formTemplateAsString);
});

Вернуться на верх