Django formsets: make first required?

These formsets are exhibiting exactly the opposite behavior that I want.

My view is set up like this:

def post(request): # TODO: handle vehicle formset
    VehicleFormSetFactory = formset_factory(VehicleForm, extra=1)
    if request.POST:
        vehicles_formset = VehicleFormSetFactory(request.POST)
    else:
        vehicles_formset = VehicleFormSetFactory()

And my template looks like this:

    <div id="vehicle_forms">
        {{ vehicles_formset.management_form }}
        {% for form in vehicles_formset.forms %}
            <h4>Vehicle {{forloop.counter}}</h4>
            <table>
                {% include "form.html" %}
            </table>
        {% endfor %}
    </div>

That way it initially generates only 1 form, like I want. But I want that one form to be required!

When I dynamically add blank forms with JavaScript and vehicles_formset.empty_form all those extra forms are required, which I don’t want.

From the docs:

The formset is smart enough to ignore extra forms that were not changed.

This is the behavior the first form is exhibiting (not what I want) but not the behavior that the extra forms are exhibiting (what I do want).

Is there some attribute I can can change to at least make one form required?

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

Found a better solution:

class RequiredFormSet(BaseFormSet):
    def __init__(self, *args, **kwargs):
        super(RequiredFormSet, self).__init__(*args, **kwargs)
        for form in self.forms:
            form.empty_permitted = False

Then create your formset like this:

MyFormSet = formset_factory(MyForm, formset=RequiredFormSet)

I really don’t know why this wasn’t an option to begin with… but, whatever. It only took a few hours of my life to figure out.

This will make all the forms required. You could make just the first one required by setting self.forms[0].empty_permitted to False.

Method 2

New in Django 1.7: you can specify this behaviour with your formset_factory

https://docs.djangoproject.com/en/1.8/topics/forms/formsets/#validate-min

VehicleFormSetFactory = formset_factory(VehicleForm, min_num=1, validate_min=True, extra=1)

Method 3

Well… this makes the first form required.

class RequiredFormSet(BaseFormSet):
    def clean(self):
        if any(self.errors):
            return
        if not self.forms[0].has_changed():
            raise forms.ValidationError('Please add at least one vehicle.')

Only “problem” is that if there are 0 forms, then the clean method doesn’t seem to get called at all, so I don’t know how to check if there are 0. Really…this should never happen though (except that my JS has a bug in it, allowing you to remove all the forms).

Method 4

Oh I think I see. Try this:

from django.forms.formsets import BaseFormSet, formset_factory
class OneExtraRequiredFormSet(BaseFormSet):
    def initial_form_count(self):
        return max(super(OneExtraRequiredFormSet,self).initial_form_count() - 1,0)

VehicleFormSetFactory = formset_factory(VehicleForm, formset=OneExtraRequiredFormSet, extra=1)

== Original answer below ==

When you say “at least make one form required”, I assume you mean “make only one extra form required, regardless of how many have been added via javascript”.

You will need to have hidden input on your page which contains the number of forms that have been added via javascript, and then use that number, minus 1, as the value to pass in as the extra attribute to your formsets constructor.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x