Python / Django / Class Based Views

Django Class Based Views

By Marcelo Fernandes Dec 31, 2017

Overview of Django Class Based Views


From Django documentation, it is stated that some advantages of using Class Based Views (CBVs) over function views are:

  • Organization of code related to specific HTTP methods (GET, POST, etc.) can be addressed by separate methods instead of conditional branching.
  • Object oriented techniques such as mixins (multiple inheritance) can be used to factor code into reusable components.

It is almost a guarantee that once you get to work on a huge project, CBVs will become part of your daily work.



Base Views

The Base Views are the parent views of all the other django views. They create the initial functionality that will simplify the creation and usage for all the others that are yet to come in this post.

The Base views are composed by three views:


View

class django.views.generic.base.View

The core view. Every other class will be inheriting from this one.

The class will be calling those methods in sequence:

  1. dispatch() - accepts a request, inspect an HTTP method and tries to delegate a method that matches the HTTP Method (GET, POST...)
  2. http_method_not_allowed() - If the view was called with a HTTP method it doesn’t support, this method is called instead.
  3. options() - Returns a response with the Allow header containing a list of the view’s allowed HTTP method names.

Attributes:

    http_method_names The list of HTTP method names that this view will accept.


Example

['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']


This view also implements the class method as_view(**initkwargs). This method is very important, Any positional and/or keyword arguments captured from the URL pattern are assigned to the args and kwargs attributes. Check it on github.


Example:


views.py

from django.http import HttpResponse
from django.views import View

class HomeView(View):

    def get(self, request, *args, **kwargs):
        return HttpResponse('Your Homepage Response!')

urls.py

from django.urls import path

from myapp.views import MyView

urlpatterns = [
    path('', Home.as_view(), name='home'),
]

TemplateView

class django.views.generic.base.TemplateView

This view returns a given template, it will render it with a context that contains the parameters captured in the URL and information that can be populated in get_context_data()

This class will be calling these methods in sequence:

  1. dispatch()
  2. http_method_not_allowed()
  3. get_context_data() - Comes from the ContextMixin, it must return a dictionary containing the template context
  4. render_to_response(context, **response_kwargs) - Returns a response, using the response_class for this view, with a template rendered with the given context.
  5. get_template_names() - Returns a list of template names to search for when rendering the template.

This class inherits from: TemplateResponseMixin, TemplateResponseMixin and View.

You can also add context using the extra_context keyword argument for as_view(). This is new in django 2.0!

Example:


views.py
from django.views.generic.base import TemplateView

from articles.models import Article

class HomeView(TemplateView):

    template_name = "home.html"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['latest_articles'] = Article.objects.all()[:5]
        return context
urls.py
from django.urls import path

from myapp.views import HomePageView

urlpatterns = [
    path('', HomePageView.as_view(), name='home'),
]

It also inherits the methods from TemplateResponseMixin: render_to_response(context, **response_kwargs) and get_template_names()


RedirectView

class django.views.generic.base.RedirectView

Redirects to a given URL.

Calls these methods in the following order:

  1. dispatch()
  2. http_method_not_allowed()
  3. get_redirect_url() - Constructs the target URL for redirection.

Attributes:

  • url - The URL to redirect to, as a string. Or None to raise a 410 (Gone) HTTP error.
  • pattern_name - The name of the URL pattern to redirect to. Reversing will be done using the same args and kwargs as are passed in for this view.
  • permanent - Whether the redirect should be permanent. The only difference here is the HTTP status code returned. If True, then the redirect will use status code 301. If False, then the redirect will use status code 302. By default, permanent is False.
  • query_string - Whether to pass along the GET query string to the new location. If True, then the query string is appended to the URL. If False, then the query string is discarded. By default, query_string is False.

Example:


views.py
from django.shortcuts import get_object_or_404
from django.views.generic.base import RedirectView

from articles.models import Article

class ArticleCounterRedirectView(RedirectView):

    permanent = False
    query_string = True
    pattern_name = 'article-detail'

    def get_redirect_url(self, *args, **kwargs):
        article = get_object_or_404(Article, pk=kwargs['pk'])
        article.update_counter()
        return super().get_redirect_url(*args, **kwargs)
urls.py 
from django.urls import path
from django.views.generic.base import RedirectView

from article.views import ArticleCounterRedirectView, ArticleDetail

urlpatterns = [
    path('counter/<int:pk>/', ArticleCounterRedirectView.as_view(), name='article-counter'),
    path('details/<int:pk>/', ArticleDetail.as_view(), name='article-detail'),
    path('go-to-django/', RedirectView.as_view(url='https://djangoproject.com'), name='go-to-django'),
]


Generic Display Views

Those 2 following classes are probably the most common used Django Views.

Generic Display Views aims to handle data display.

DetailView

class django.views.generic.detail.DetailView

This class is commonly used to display details of a single object. It's methods are called in the following order:

  1. dispatch()
  2. http_method_not_allowed()
  3. get_template_names()
  4. get_slug_field() - Returns the name of a slug field to be used to look up by slug.
  5. get_queryset() - Returns the queryset that will be used to retrieve the object that this view will display.
  6. get_object() - Returns the single object that this view will display. If queryset is provided, that queryset will be used as the source of objects; otherwise, get_queryset() will be used.
  7. get_context_object_name() - Return the context variable name that will be used to contain the data that this view is manipulating.
  8. get_context_data() - Returns context data for displaying the object.
  9. get()
  10. render_to_response()

Example


 views.py
from django.views.generic.detail import DetailView
from django.utils import timezone

from articles.models import Article

class ArticleDetailView(DetailView):

    model = Article

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['now'] = timezone.now()
        return context

urls.py
from django.urls import path

from article.views import ArticleDetailView

urlpatterns = [
    path('≶slug:slug>/', ArticleDetailView.as_view(), name='article-detail'),
]

ListView

class django.views.generic.list.ListView

A page representing a list of objects. While this view is executing, self.object_list will contain the list of objects (usually, but not necessarily a queryset) that the view is operating upon.

The view will be calling those methods in the following order:

  1. dispatch()
  2. http_method_not_allowed()
  3. get_template_names()
  4. get_queryset() - Returns the queryset that will be used to retrieve the object that this view will display.
  5. get_context_object_name() - Return the context variable name that will be used to contain the data that this view is manipulating.
  6. get_context_data() - Returns context data for displaying the object.
  7. get()
  8. render_to_response()

Example:


views.py
from django.views.generic.list import ListView
from django.utils import timezone

from articles.models import Article

class ArticleListView(ListView):

    model = Article

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['now'] = timezone.now()
        return context

urls.py

from django.urls import path

from article.views import ArticleListView

urlpatterns = [
    path('', ArticleListView.as_view(), name='article-list'),
]


Generic editing views

For the following views, we are going to assume we have the following model



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

class Author(models.Model):
    name = models.CharField(max_length=200)

    def get_absolute_url(self):
        return reverse('author-detail', kwargs={'pk': self.pk})


FormView

class django.views.generic.edit.FormView

This view, as the name says, will display a form. If the form is valid, redirects to a new url, otherwise it returns the form again with the invalid fields.

Useful methods to consider when inheriting from FormView:

  • form_invalid(form) - Renders a response, providing the invalid form as context.
  • form_valid(form) - Redirects to get_success_url().
  • get_form() - Instantiate an instance of form_class using get_form_kwargs(). If form_class isn’t provided get_form_class() will be used.

Example:


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


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)


CreateView

class django.views.generic.edit.CreateView

A view that displays a form for creating an object, redisplaying the form with validation errors (if there are any) and saving the object.

Attributes:

  • template_name_suffix - The CreateView page displayed to a GET request uses a template_name_suffix of '_form'. For example, changing this attribute to '_create_form' for a view creating objects for the example Author model would cause the default template_name to be 'myapp/author_create_form.html'.
  • object

Useful Methods:

  • form_invalid(form) - Renders a response, providing the invalid form as context.
  • form_valid(form) - Redirects to get_success_url().
  • get_form() - Instantiate an instance of form_class using get_form_kwargs(). If form_class isn’t provided get_form_class() will be used.

Example:


views.py

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

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


UpdateView

class django.views.generic.edit.UpdateView

As its name says: A view that displays a form for editing an existing object, redisplaying the form with validation errors (if there are any) and saving changes to the object.

Attributes:

  • template_name_suffix - The UpdateView page displayed to a GET request uses a template_name_suffix of '_form'. For example, changing this attribute to '_update_form' for a view creating objects for the example Author model would cause the default template_name to be 'myapp/author_update_form.html'.
  • object

Useful Methods:

  • form_invalid(form) - Renders a response, providing the invalid form as context.
  • form_valid(form) - Redirects to get_success_url().
  • get_form() - Instantiate an instance of form_class using get_form_kwargs(). If form_class isn’t provided get_form_class() will be used.

Example:


views.py

from django.views.generic.edit import UpdateView
from myapp.models import Author

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


DeleteView

class django.views.generic.edit.DeleteView

As its name says: A view that displays a confirmation page and deletes an existing object.

Attributes:

  • template_name_suffix - The DeleteView page displayed to a GET request uses a template_name_suffix of '_form'. For example, changing this attribute to '_delete_form' for a view creating objects for the example Author model would cause the default template_name to be 'myapp/author_delete_form.html'.
  • object

Useful Methods:

  • form_invalid(form) - Renders a response, providing the invalid form as context.
  • form_valid(form) - Redirects to get_success_url().
  • get_form() - Instantiate an instance of form_class using get_form_kwargs(). If form_class isn’t provided get_form_class() will be used.

Example:


views.py

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

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


Notes