Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

Use class-based views for form processing


May 14, 2021 Django


Table of contents


Form class-based opinion processing

Form processing typically has three paths:

  • Initial GET (blank or pre-filled)
  • POST contains invalid data (usually re-displaying forms with errors)
  • POST with valid data (processing data and usually redirecting)

Implementing this yourself often results in a lot of duplicate template code (see Using forms in views). To avoid this, Django provides a common set of class-based views for form processing.

Basic form

Give the contact form:

the forms.py

from django import forms

class 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

You can use the construction view FormView:

the views.py

from myapp.forms import ContactForm
from django.views.generic.edit import FormView

class ContactView(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 is inherited, and TemplateResponseMixin template_name be used here.
  • The default implementation of form_valid () is simply to redirect to success_url.

The form of the model

Universal view is really great when you're using models. These generic views will automatically create a ModelForm, as long as they are able to determine which model class to use:

  • If model gives a property, the model class is used.
  • If get_object () return an object, the object's class is used.
  • If quryset gives a, the model of the query set is used.

The model form view provides an implementation form_valid () automatically save the model. I f you have special requirements, you can override this setting. See the example below.

You don't even success_urlfor provide a CreateView or UpdateView-get_absolute_url if available, they will be used on model objects.

If you want to use a custom ModelForm, such as adding additional validation, form_class set up on the view.

Note: When you specify a custom form class, you must form_class model ModelForm, even if it is possible.

First, we need to get_absolute_url () to the Author class:

models.py in the middle

from django.db import models
from django.urls import reverse

class 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 work with friends on CreativeView. N ote how we configure a common class-based view here. We don't have to write any logic ourselves:

the views.py

from django.urls import reverse_lazy
from django.views.generic.edit import CreateView, DeleteView, UpdateView
from myapp.models import Author

class AuthorCreate(CreateView):
  model = Author
  fields = ['name']

class AuthorUpdate(UpdateView):
  model = Author
  fields = ['name']

class AuthorDelete(DeleteView):
  model = Author
  success_url = reverse_lazy('author-list')

Note: We must use reverse_lazy () instead of reverse(), because the url is not loaded when the file is imported.

The Fields property works in the same way as the internal Meta class property on Fields. Unless you otherwise define the form class, this property is required, and the view throws an ImperlyConfigured exception.

If both fields and form_class are specified, TheflyConfigured throws an exception.

Finally, we connect these new views to URLconf:

the urls.py

from django.urls import path
from myapp.views import AuthorCreate, AuthorDelete, AuthorUpdate

urlpatterns = [
  # ...
  path('author/add/', AuthorCreate.as_view(), name='author-add'),
  path('author/<int:pk>/', AuthorUpdate.as_view(), name='author-update'),
  path('author/<int:pk>/delete/', AuthorDelete.as_view(), name='author-delete'),
]

Note: Some ideas inherit singleObjectTemplateResponseMixin It uses template_name_suffix to build template_name model-based.

In this example:

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

If you want to provide CreativeView and Provide a separate template, UpdateView, you can set up a template_name or template_name_suffix.

Model and request.user

To track users who create objects, CreateView can do this using the custom method ModelForm. First, add the foreign key relationship to the model:

models.py in the middle

from django.contrib.auth.models import User
from django.db import models

class Author(models.Model):
  name = models.CharField(max_length=200)
  created_by = models.ForeignKey(User, on_delete=models.CASCADE)

  # ...

In the view, make sure that you created_by list of fields that you want to edit, and override form_valid() to add users:

the views.py

from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic.edit import CreateView
from myapp.models import Author

class AuthorCreate(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 who are not logged in from accessing the form. If you ignore this, you need to deal with the unauthorized user form_valid ().

Example of AJAX

This is an example of how to implement a form that applies to AJAX requests and "normal" form POST:

from django.http import JsonResponse
from django.views.generic.edit import CreateView
from myapp.models import Author

class AjaxableResponseMixin:
  """
  Mixin to add AJAX 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.is_ajax():
          return JsonResponse(form.errors, status=400)
      else:
          return response

  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.is_ajax():
          data = {
              'pk': self.object.pk,
          }
          return JsonResponse(data)
      else:
          return response

class AuthorCreate(AjaxableResponseMixin, CreateView):
  model = Author
  fields = ['name']

For more information: https://docs.djangoproject.com/en/3.0/