Form handling with class-based views | Django documentation (2024)

  • Getting Help
  • el
  • es
  • fr
  • id
  • it
  • ja
  • ko
  • pl
  • pt-br
  • zh-hans
  • Language: en
  • 1.8
  • 1.10
  • 1.11
  • 2.0
  • 2.1
  • 2.2
  • 3.0
  • 3.1
  • 3.2
  • 4.0
  • 4.1
  • 4.2
  • 5.1
  • dev
  • Documentation version: 5.0

Form processing generally has 3 paths:

  • Initial GET (blank or prepopulated form)
  • POST with invalid data (typically redisplay form with errors)
  • POST with valid data (process the data and typically redirect)

Implementing this yourself often results in a lot of repeated boilerplate code(see Using a form in a view). To help avoidthis, Django provides a collection of generic class-based views for formprocessing.

Basic forms

Given a contact form:

forms.py

from django import formsclass ContactForm(forms.Form): name = forms.CharField() message = forms.CharField(widget=forms.Textarea) def send_email(self): # send email using the self.cleaned_data dictionary pass

The view can be constructed using a FormView:

views.py

from myapp.forms import ContactFormfrom django.views.generic.edit import FormViewclass ContactFormView(FormView): template_name = "contact.html" form_class = ContactForm success_url = "/thanks/" def form_valid(self, form): # This method is called when valid form data has been POSTed. # It should return an HttpResponse. form.send_email() return super().form_valid(form)

Notes:

  • FormView inheritsTemplateResponseMixin sotemplate_namecan be used here.
  • The default implementation forform_valid() simplyredirects to the success_url.

Model forms

Generic views really shine when working with models. These genericviews will automatically create a ModelForm, so long asthey can work out which model class to use:

  • If the model attribute isgiven, that model class will be used.
  • If get_object()returns an object, the class of that object will be used.
  • If a queryset isgiven, the model for that queryset will be used.

Model form views provide aform_valid() implementationthat saves the model automatically. You can override this if you have anyspecial requirements; see below for examples.

You don’t even need to provide a success_url forCreateView orUpdateView - they will useget_absolute_url() on the model object if available.

If you want to use a custom ModelForm (for instance toadd extra validation), setform_class on your view.

Note

When specifying a custom form class, you must still specify the model,even though the form_class maybe a ModelForm.

First we need to add get_absolute_url() to ourAuthor class:

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})

Then we can use CreateView and friends to do the actualwork. Notice how we’re just configuring the generic class-based viewshere; we don’t have to write any logic ourselves:

views.py

from django.urls import reverse_lazyfrom django.views.generic.edit import CreateView, DeleteView, UpdateViewfrom myapp.models import Authorclass AuthorCreateView(CreateView): model = Author fields = ["name"]class AuthorUpdateView(UpdateView): model = Author fields = ["name"]class AuthorDeleteView(DeleteView): model = Author success_url = reverse_lazy("author-list")

Note

We have to use reverse_lazy() instead ofreverse(), as the urls are not loaded when the file is imported.

The fields attribute works the same way as the fields attribute on theinner Meta class on ModelForm. Unless you define theform class in another way, the attribute is required and the view will raisean ImproperlyConfigured exception if it’s not.

If you specify both the fieldsand form_class attributes, anImproperlyConfigured exception will be raised.

Finally, we hook these new views into the URLconf:

urls.py

from django.urls import pathfrom myapp.views import AuthorCreateView, AuthorDeleteView, AuthorUpdateViewurlpatterns = [ # ... path("author/add/", AuthorCreateView.as_view(), name="author-add"), path("author/<int:pk>/", AuthorUpdateView.as_view(), name="author-update"), path("author/<int:pk>/delete/", AuthorDeleteView.as_view(), name="author-delete"),]

Note

These views inheritSingleObjectTemplateResponseMixinwhich usestemplate_name_suffixto construct thetemplate_namebased on the model.

In this example:

  • CreateView and UpdateView use myapp/author_form.html
  • DeleteView uses myapp/author_confirm_delete.html

If you wish to have separate templates for CreateView andUpdateView, you can set eithertemplate_name ortemplate_name_suffixon your view class.

Models and request.user

To track the user that created an object using a CreateView,you can use a custom ModelForm to do this. First, addthe foreign key relation to the model:

models.py

from django.contrib.auth.models import Userfrom django.db import modelsclass Author(models.Model): name = models.CharField(max_length=200) created_by = models.ForeignKey(User, on_delete=models.CASCADE) # ...

In the view, ensure that you don’t include created_by in the list of fieldsto edit, and overrideform_valid() to add the user:

views.py

from django.contrib.auth.mixins import LoginRequiredMixinfrom django.views.generic.edit import CreateViewfrom myapp.models import Authorclass AuthorCreateView(LoginRequiredMixin, CreateView): model = Author fields = ["name"] def form_valid(self, form): form.instance.created_by = self.request.user return super().form_valid(form)

LoginRequiredMixin prevents users whoaren’t logged in from accessing the form. If you omit that, you’ll need tohandle unauthorized users in form_valid().

Content negotiation example

Here is an example showing how you might go about implementing a form thatworks with an API-based workflow as well as ‘normal’ form POSTs:

from django.http import JsonResponsefrom django.views.generic.edit import CreateViewfrom myapp.models import Authorclass JsonableResponseMixin: """ Mixin to add JSON support to a form. Must be used with an object-based FormView (e.g. CreateView) """ def form_invalid(self, form): response = super().form_invalid(form) if self.request.accepts("text/html"): return response else: return JsonResponse(form.errors, status=400) def form_valid(self, form): # We make sure to call the parent's form_valid() method because # it might do some processing (in the case of CreateView, it will # call form.save() for example). response = super().form_valid(form) if self.request.accepts("text/html"): return response else: data = { "pk": self.object.pk, } return JsonResponse(data)class AuthorCreateView(JsonableResponseMixin, CreateView): model = Author fields = ["name"]

Built-in class-based generic views

Using mixins with class-based views

Back to Top

Form handling with class-based views | Django documentation (2024)
Top Articles
Latest Posts
Article information

Author: Aron Pacocha

Last Updated:

Views: 6431

Rating: 4.8 / 5 (48 voted)

Reviews: 87% of readers found this page helpful

Author information

Name: Aron Pacocha

Birthday: 1999-08-12

Address: 3808 Moen Corner, Gorczanyport, FL 67364-2074

Phone: +393457723392

Job: Retail Consultant

Hobby: Jewelry making, Cooking, Gaming, Reading, Juggling, Cabaret, Origami

Introduction: My name is Aron Pacocha, I am a happy, tasty, innocent, proud, talented, courageous, magnificent person who loves writing and wants to share my knowledge and understanding with you.