2

I am creating a formset with django which contains children information. I am using createview (CBV) for this. The form is displayed properly, it has functionality of adding children and removing children all working properly. But, when I click on submit, form_invalid is called instead of form_valid. To know this issue I printed form.errors and i saw following errors

<ul class="errorlist"><li>deal_id<ul class="errorlist"><li>This field is required.</li></ul></li><li>child_name<ul class="errorlist"><li>This field is required.</li></ul></li><li>son_or_daugher<ul class="errorlist"><li>This field is required.</li></ul></li><li>child_age<ul class="errorlist"><li>This field is required.</li></ul></li><li>child_education<ul class="errorlist"><li>This field is required.</li></ul></li><li>child_occupation<ul class="errorlist"><li>This field is required.</li></ul></li></ul>

Below is my code

Template :-

{% extends "forms_app/base.html" %}
{% load static %}

{% block title %}{% endblock %}
{% block content %}
    <h2>Profile</h2>
    <hr>
    <div class="col-md-4">
        <form action="" method="post">{% csrf_token %}
            <table class="table">
                {{ childrens.management_form }}

                {% for form in childrens.forms %}
                    {% if forloop.first %}
                        <thead>
                        <tr>
                            {% for field in form.visible_fields %}
                                <th>{{ field.label|capfirst }}</th>
                            {% endfor %}
                        </tr>
                        </thead>
                    {% endif %}
                    <tr class="{% cycle row1 row2 %} formset_row">
                        {% for field in form.visible_fields %}
                            <td>
                                {# Include the hidden fields in the form #}
                                {% if forloop.first %}
                                    {% for hidden in form.hidden_fields %}
                                        {{ hidden }}
                                    {% endfor %}
                                {% endif %}
                                {{ field.errors.as_ul }}
                                {{ field }}
                            </td>
                        {% endfor %}
                    </tr>
                {% endfor %}
            </table>
            <input type="submit" value="Submit"/> <a href="">back to the list</a>
        </form>
    </div>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
    <script src="{% static 'forms_app/jquery.formset.js' %}"></script>
    <script type="text/javascript">
        $('.formset_row').formset({
            addText: 'Add Children',
            deleteText: 'Remove',
            prefix: 'familymember_set'
        });
    </script>
{% endblock %}

View Code

class ChildrenView(CreateView):
    template_name = 'forms_app/children_form.html'
    form_class = ChildrenForm
    success_url = reverse_lazy('forms_app:deal-entering')

    # fields = ['name','class']

    def get_context_data(self, **kwargs):
        data = super(ChildrenView, self).get_context_data(**kwargs)
        if self.request.POST:
            data['childrens'] = ChildrenFormSet(self.request.POST)
        else:
            print('child form initial data')
            initial_list = []
            try:
                print('its going on')
                for i in range(self.request.session['children_count']   ):
                    print('still going on')
                    for field in self.fields:
                        initial_list.append(self.request.session[i + field])
                        data['childrens'] = ChildrenFormSet(initial = initial_list)
            except:
                data['childrens'] = ChildrenFormSet()
            # data['childrens'].extra = self.request.session['children_count']
        return data


    def form_valid(self, form):
        if form.is_valid():
            count = 0
            for f in form:
                for field in ['deal_id','child_name','son_or_daugher','child_age','child_education','child_occupation']:
                    self.request.session[count + field] = f.cleaned_data[field]
                count += 1

            self.request.session['children_count'] = count

            for i in range(count):
                for field in ['deal_id','child_name','son_or_daugher','child_age','child_education','child_occupation']:
                    print(self.request.session[i + field])


            for field in self.fields:
                self.request.session[field] = form.cleaned_data[field]

            self.request.session['valid_children'] = True

        return super(ChildrenView, self).form_valid(form)


    def form_invalid(self,form):

        if form.is_valid():
            pass
        print(form.errors)
        print('form invalid')
        return super(ChildrenView, self).form_invalid(form)

Form Code

class ChildrenForm(forms.ModelForm):
    class Meta:
        model = Children
        fields = '__all__'


    def clean(self):
        print(self.cleaned_data)

ChildrenFormSet = modelformset_factory(Children,fields = '__all__',extra=1)

I have read several post on StackOverflow. I went through django documents but still I am unable to figure out the problem. Please help me.

Below is screenshot of my form enter image description here

In case you have any problem understanding please comment, I will reply superfast.

6
  • 1
    Does the error appear when you fill every field with data or only if the form is blank? Commented Aug 19, 2019 at 11:49
  • Your form_valid is trying to validate ChildrenForm which is the form_class and isn't in your template. I normally use formsets linking them to a parent form (so the parent form goes in the form_class) and I'm not sure how you use CBV without that. Are these children related to a parent in someway? If so, set form_class=parentForm and in your template put {{ form.as_p }} straight after the {% csrf_token %} Commented Aug 19, 2019 at 12:34
  • This error comes when i submit the form @SallyZeitler. Form is completely filled Commented Aug 19, 2019 at 13:07
  • @HenryM there is no parent. This form contain only children. How to add parentForm Commented Aug 19, 2019 at 13:09
  • This may seem like an unusual suggestion, but have you tried re-writing the view as a functional based view? Personally, I find working with formsets in a functional based view easier to debug. Commented Aug 19, 2019 at 15:38

3 Answers 3

1

Maybe the problem is in this line:

form_class = ChildrenForm

Try removing that line or change it to:

form_class = ChildrenFormSet

I think that your form_valid is checking against the form ChildrenForm.

Sign up to request clarification or add additional context in comments.

2 Comments

After changing giving new error. Now form is not loading :- __init__() got an unexpected keyword argument 'instance'
@ShubhamSharma Indeed this is the issue, however it will not work with CreateView. I would approach it this way.
1

I think you need to completely ignore form_valid because you don't have a ChildrenForm to be valid so override the post method instead

Try something like this:

def post(self, *args, **kwargs):
    formset = ChildrenFormSet(self.request.POST)
    if formset.is_valid():
       do stuff
    else :
       return self.form.is_invlaid()
    return redirect to your success url

Comments

1

Your template, renders ChildrenFormSet instead of the ChildrenForm.

Consequently, the user enters the data in the formset while ChildrenView is validating the ChildrenForm which is inevitably empty.

@HenryM provides a logical solution (I have not tested it) but it cancels the meaning of using a CreateView.

I would use a FormView in order to manipulate the Formset directly:

forms.py:

class ChildrenView(FormView):
    template_name = 'forms_app/children_form.html'
    form_class = ChildrenFormSet
    success_url = reverse_lazy('forms_app:deal-entering')

    def form_valid(self, form):
        """
        If the formset is valid, create the objects in the database
        """
        instances = []
        for sub_form in form:
            obj = Children(**sub_form.cleaned_data)
            instances.append(obj)
        Children.objects.bulk_create(instances)
        return super().form_valid(form)

    def get_form_kwargs(self):
        """
        Here you define the queryset that gathers the forms presented to the user.
        We return none() in order to have always an empty formset and simplify the code.
        """
        kwargs = super().get_form_kwargs()
        kwargs["queryset"] = Children.objects.none()
        return kwargs

children_form.html:

{% extends "forms_app/base.html" %}
{% load static %}

{% block title %}{% endblock %}
{% block content %}
    <h2>Profile</h2>
    <hr>
    <div class="col-md-4">
        <form action="" method="post">{% csrf_token %}
          {{ form.as_table }}
          <input type="submit" value="Submit"/> <a href="">back to the list</a>
        </form>
    </div>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
    <script src="{% static 'forms_app/jquery.formset.js' %}"></script>
    <script type="text/javascript">
        $('.formset_row').formset({
            addText: 'Add Children',
            deleteText: 'Remove',
            prefix: 'familymember_set'
        });
    </script>
{% endblock %}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.