====================================Form handling with class-based views====================================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 :ref:`Using a form in a view<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:.. code-block:: python:caption: ``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 dictionarypassThe view can be constructed using a ``FormView``:.. code-block:: python:caption: ``views.py``from myapp.forms import ContactFormfrom django.views.generic.edit import FormViewclass ContactFormView(FormView):template_name = 'contact.html'form_class = ContactFormsuccess_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 inherits:class:`~django.views.generic.base.TemplateResponseMixin` so:attr:`~django.views.generic.base.TemplateResponseMixin.template_name`can be used here.* The default implementation for:meth:`~django.views.generic.edit.FormMixin.form_valid` simplyredirects to the :attr:`~django.views.generic.edit.FormMixin.success_url`.Model forms===========Generic views really shine when working with models. These genericviews will automatically create a :class:`~django.forms.ModelForm`, so long asthey can work out which model class to use:* If the :attr:`~django.views.generic.edit.ModelFormMixin.model` attribute isgiven, that model class will be used.* If :meth:`~django.views.generic.detail.SingleObjectMixin.get_object()`returns an object, the class of that object will be used.* If a :attr:`~django.views.generic.detail.SingleObjectMixin.queryset` isgiven, the model for that queryset will be used.Model form views provide a:meth:`~django.views.generic.edit.ModelFormMixin.form_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`` for:class:`~django.views.generic.edit.CreateView` or:class:`~django.views.generic.edit.UpdateView` - they will use:meth:`~django.db.models.Model.get_absolute_url()` on the model object if available.If you want to use a custom :class:`~django.forms.ModelForm` (for instance toadd extra validation), set:attr:`~django.views.generic.edit.FormMixin.form_class` on your view... note::When specifying a custom form class, you must still specify the model,even though the :attr:`~django.views.generic.edit.FormMixin.form_class` maybe a :class:`~django.forms.ModelForm`.First we need to add :meth:`~django.db.models.Model.get_absolute_url()` to our``Author`` class:.. code-block:: python:caption: ``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 :class:`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:.. code-block:: python:caption: ``views.py``from django.urls import reverse_lazyfrom django.views.generic.edit import CreateView, DeleteView, UpdateViewfrom myapp.models import Authorclass AuthorCreateView(CreateView):model = Authorfields = ['name']class AuthorUpdateView(UpdateView):model = Authorfields = ['name']class AuthorDeleteView(DeleteView):model = Authorsuccess_url = reverse_lazy('author-list').. note::We have to use :func:`~django.urls.reverse_lazy` instead of``reverse()``, 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 :class:`~django.forms.ModelForm`. Unless you define theform class in another way, the attribute is required and the view will raisean :exc:`~django.core.exceptions.ImproperlyConfigured` exception if it's not.If you specify both the :attr:`~django.views.generic.edit.ModelFormMixin.fields`and :attr:`~django.views.generic.edit.FormMixin.form_class` attributes, an:exc:`~django.core.exceptions.ImproperlyConfigured` exception will be raised.Finally, we hook these new views into the URLconf:.. code-block:: python:caption: ``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 inherit:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`which uses:attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_suffix`to construct the:attr:`~django.views.generic.base.TemplateResponseMixin.template_name`based on the model.In this example:* :class:`CreateView` and :class:`UpdateView` use ``myapp/author_form.html``* :class:`DeleteView` uses ``myapp/author_confirm_delete.html``If you wish to have separate templates for :class:`CreateView` and:class:`UpdateView`, you can set either:attr:`~django.views.generic.base.TemplateResponseMixin.template_name` or:attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_suffix`on your view class.Models and ``request.user``===========================To track the user that created an object using a :class:`CreateView`,you can use a custom :class:`~django.forms.ModelForm` to do this. First, addthe foreign key relation to the model:.. code-block:: python:caption: ``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 override:meth:`~django.views.generic.edit.ModelFormMixin.form_valid()` to add the user:.. code-block:: python:caption: ``views.py``from django.contrib.auth.mixins import LoginRequiredMixinfrom django.views.generic.edit import CreateViewfrom myapp.models import Authorclass AuthorCreateView(LoginRequiredMixin, CreateView):model = Authorfields = ['name']def form_valid(self, form):form.instance.created_by = self.request.userreturn super().form_valid(form):class:`~django.contrib.auth.mixins.LoginRequiredMixin` prevents users whoaren't logged in from accessing the form. If you omit that, you'll need tohandle unauthorized users in :meth:`~.ModelFormMixin.form_valid()`... _content-negotiation-example: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 responseelse: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 responseelse:data = {'pk': self.object.pk,}return JsonResponse(data)class AuthorCreateView(JsonableResponseMixin, CreateView):model = Authorfields = ['name']