Django formset handling with class based views, custom errors and validation (2024)

Django formset handling with class based views, custom errors and validation (1)

How to use formsets taking benefits of generic views

Published:TagsDjango, Forms

Overview

In Django, forms and formsets are very similar, but by default itsupports just regular forms in class based views, this is a guide toeasily use Formsets with Class Based Views (CBVs).

Basic

The most basic view for handling forms is one that:

  1. displays a form.1.1. On error, redisplays the form with validation errors;1.2. on success, redirects to a new URL.

That behaviour is already handled atdjango.views.generic.edit.FormView.

What if we want to take advantage of the above view but display anarbitrary number of forms for the same model?

Let’s assume we have the following in myapp/models.py:

from django.db import modelsfrom django.urls import reverseclass Author(models.Model): name = models.CharField(max_length=200) def get_absolute_url(self): return reverse('author-detail', kwargs={'pk': self.pk})

To use a regular FormView CBV that displays a single form, we wouldhave to define it with a form in myapp/forms.py:

from django import formsclass AuthorForm(forms.Form): name = forms.CharField()

and a view at myapp/views.py using the above form specifying in atform_class attribute:

from myapp.forms import AuthorFormfrom django.views.generic.edit import FormViewclass AuthorFormView(FormView): template_name = author.html form_class = AuthorForm success_url = '/'

with a template like:

<form method="post">{% csrf_token %} {{ form.as_p }} <input type="submit" value="Add"></form>

Using a formset

To display two author forms in our template with a single submitbutton, we slightly change the above code.

Create formset

First of all, we create a new formset in myapp/forms.py:

AuthorFormSet = formset_factory( AuthorForm, extra=2, max_num=2, min_num=1)

We are using formset_factory which isdefined at from django.forms import formset_factory like:

def formset_factory(form, formset=BaseFormSet, extra=1, can_order=False, can_delete=False, max_num=None, validate_max=False, min_num=None, validate_min=False): ...

Use formset in view

Then use the above formset in the view, instead of a singleAuthorForm we use AuthorFormSet:

from myapp.forms import AuthorFormSetfrom django.views.generic.edit import FormViewclass AuthorFormView(FormView): template_name = author.html form_class = AuthorFormSet success_url = '/'

Now the template would display two forms, a simple test to be surethat the name is required (this test isn’t complete but I left hereto give an idea how to use TestCase.assertFormsetError):

from django.test import TestCasefrom django.urls import reversefrom myapp.views import class AuthorFormView(TestCase): def test_language_is_required(self): name = 'Richard Morales'  data = { 'form-TOTAL_FORMS': '2', 'form-INITIAL_FORMS': '1', 'form-MAX_NUM_FORMS': '2', #'form-0-name':name, not sent to raise the error } response = self.client.post(reverse('create-two-authors'), data) self.assertFormsetError(response, formset='form', form_index=0, field='name', errors='Please specify the name of theauthor.')

Adding custom validation to formset

To further customize the validation process, i.e.: overridingfull_clean(), we need to inherit from BaseFormSet instead of justdefining the method in the view, because it is the formset who doesthe proper validation for each of its forms and the collect all ofthose forms cleaned data at self.cleaned_data.

Then in the myapp/forms.py we explicitely create a newformset=BaseFormSet to extend from, called AuthorBaseFormSet andthen use it in formset_factory.

When defining our custom full_clean function, we can access allforms data in the self.cleaned_data list, which contains the datafor each form in a dictionary like: [{'name': 'entered name in first form'},{'name': 'entered name in second form'}].

Finally, we add a specific error message and attach it to the rightform field like self.forms[0].add_error('name', "the name of the first form should contain vowels").

Summarizing:

class AuthorBaseFormSet(BaseFormSet): def full_clean(self, *args, **kwargs): """ Cleaning and validating fields that depend on each other""" super().full_clean(*args, **kwargs) # if basic clean has any problem stop further processing # if not is_valid() it won't have cleaned_data if(not self.is_valid()): return  # now we can have access to self.cleaned_data# and be able to raise any error or attach an error to a form field# after some validation we add the errors ... msg0 = "First author name should contain vowels" msg1 = "Second author name should contain consonants" self.forms[0].add_error('rawtext', msg0) self.forms[1].add_error('rawtext', msg1)AuthorFormSet = formset_factory( AuthorForm, formset=AuthorBaseFormSet, # added to handle validation and error management extra=2, max_num=2, min_num=1)

Now to test the above expected behaviour:

from django.test import TestCasefrom django.urls import reversefrom myapp.views import class AuthorFormView(TestCase): def test_view_names_have_vowels_and_consonants(self): name0 = 'ZZZZZ' name1 = 'AAAAA' data = { 'form-TOTAL_FORMS': '2', 'form-INITIAL_FORMS': '2', 'form-MIN_NUM_FORMS': '1', 'form-MAX_NUM_FORMS': '2', 'form-0-name': name0, 'form-1-name': name1, } response = self.client.post(reverse('create-two-authors'), data) self.assertEqual(response.status_code, 200) # first form self.assertFormsetError(response, formset='form', form_index=0, field='name', errors='First author name should contain vowels') # second form self.assertFormsetError(response, formset='form', form_index=1, field='name', errors='Second author name should contain consonants')

Conclusion

In this way we can get all the benefits of Class Based Views usingthem with Formsets without having to repeat code for common taskalready well tested.

References

    Articles

    • Adding a Cancel button in Django class-based views, editing views and formsJuly 15, 2019
    • Using Django Model Primary Key in Custom Forms THE RIGHT WAYJuly 13, 2019
    • Django formset handling with class based views, custom errors and validation
    • How To Use Bootstrap 4 In Django FormsMay 25, 2018
    • Understanding Django FormsApril 30, 2018
    • How To Create A Form In DjangoJuly 29, 2016

Except as otherwise noted, the content of this page is licensed under CC BY-NC-ND 4.0 . Terms and Policy.

Powered by SimpleIT Hugo Theme

·

Django formset handling with class based views, custom errors and validation (2024)

References

Top Articles
Latest Posts
Article information

Author: Msgr. Refugio Daniel

Last Updated:

Views: 6493

Rating: 4.3 / 5 (74 voted)

Reviews: 89% of readers found this page helpful

Author information

Name: Msgr. Refugio Daniel

Birthday: 1999-09-15

Address: 8416 Beatty Center, Derekfort, VA 72092-0500

Phone: +6838967160603

Job: Mining Executive

Hobby: Woodworking, Knitting, Fishing, Coffee roasting, Kayaking, Horseback riding, Kite flying

Introduction: My name is Msgr. Refugio Daniel, I am a fine, precious, encouraging, calm, glamorous, vivacious, friendly person who loves writing and wants to share my knowledge and understanding with you.