Django Formset.is_valid() failing for extra forms

2020-02-25 08:26发布

问题:

In my Django application application I have a formset that is created from a simple (not-model) form, with the extra=1 (to allow javasript to add more forms later on).

class SomeForm(forms.Form):
    #some fields with required=False
    length = forms.IntegerField(required=False)

    # An example of one of the fields with choices i have
    A = 0
    B = 1
    C = 2
    D = 3

    choices = ((A, 'Aah'), (B, 'Baa'), (C, 'Caa'), (D, 'Daa'))

    # This is a required choice field
    pickme = forms.ChoiceField(choices=choices)


SomeFormset = formset_factory(SomeForm, can_delete=True, extra=1)

Now, when I create and try to validate it in my view on the POST request:

my_formset = SomeFormset(request.POST, request.FILES)

if(my_formset.is_valid()):
    # FAIL

it always fails the above check, if the extra rendered form is submitted empty.

If I check for form.changed_data on the last empty extra form, I get the fields that have choices on them (like the pickme above). In other words, the formset is not smart enough to figure out that the empty submitted form should be ignored, when some choice fields are required.

回答1:

Thanks Carl, you led me to discover the root of my problem.

When creating a form with a choice field, which is required, we must set an initial value, otherwise the form will consider that field changed.

So for a form like this:

class SomeForm(forms.Form):

    A = 0
    B = 1
    C = 2
    D = 3

   choices = ((A, 'Aah'), (B, 'Baa'), (C, 'Caa'), (D, 'Daa'))

    # This is a required choice field
    pickme = forms.ChoiceField(choices=choices)

we do this:

pickme = forms.ChoiceField(choices=choices, initial=A)

Then when a formset checks the extra form it will see that pickme had an initial value of A, and it is A now as well, and will consider it unchanged.



回答2:

This is not the usual behavior of formsets. Formsets pass empty_permitted=True to all "extra" forms, and a form with empty_permitted that hasn't been modified should always pass validation. Note that this works just fine in the Django admin (if you use inlines).

You must be doing something else in your code that is breaking this behavior somewhere. Post the full code of the relevant form?