Class-based views
In Chapter 1 and at the start of
this chapter -- in listing 2-1 -- you saw how to define a Django
url and make it operate with a Django template without the need of
a view method. This was possible due to the
django.views.generic.TemplateView
class, which is
called a class-based view.
Unlike Django view methods backed
by standard Python methods that use a Django
HttpRequest
input parameter and output a Django
HttpResponse
, class-based views offer their
functionality through full-fledged Python classes. This in turn
allows Django views to operate with object oriented programming
(OOP) principles (e.g. encapsulation, polymorphism and inheritance)
leading to greater re-usability and shorter implementation
times.
Although Django class-based views represent a more powerful approach to create Django views, they are simply an alternative to the view methods you've used up to this point. If you want to quickly execute business logic on Django requests you can keep using view methods, but for more demanding view requirements (e.g. form processing, boilerplate model queries) class-based views can save you considerable time.
Built-in class-based views
The functionality provided by the
django.views.generic.TemplateView
class-based view is
real a time saver. While it would have been possible to configure a
url to execute on an empty view method and then send control to a
template, the TemplateView
class allows this process
to be done in one line.
In addition to the
TemplateView
class-based view, Django offers many
other built-in class-based views to shorten the creation process
for common Django view operations using OOP-like principles. Table
2-11 illustrates Django's built-in classes for views.
Table 2-11. Built-in classes for views
Class | Description |
---|---|
django.views.generic.View |
Parent class of all class-based views, providing core functionality. |
django.views.generic.TemplateView |
Allows a url to return the contents of a template, without the need of a view. |
django.views.generic.RedirectView |
Allows a url to perform a redirect, without the need of a view. |
django.views.generic.ArchiveIndexView |
Allows a view to return date-based object results, without the need to explicitly perform Django model queries. |
django.views.generic.CreateView |
Allows a view to execute Create-Read-Update-Delete (CRUD) operations , without the need to explicitly perform Django model queries. |
In the upcoming and final section of this chapter, I'll explain the
classes in the top-half of table 2-11 so you can gain a better
understanding of the structure and execution process of Django
class-based views. The class-based views in bottom half of table
2-11 that involve Django models are described in a separate chapter
on Django class-based views models.
Class-based view structure & execution
To create a class-based view you need to create a class that inherits from one of the classes in table 2-11. Listing 2-37 shows a class-based view with this inheritance technique, as well as the corresponding url definition to execute a class-based view.
Listing 2-37 Class-based view inherited from TemplateView with url definition
---------------------------------------------------------------------------- # views.py from django.views.generic import TemplateView class AboutIndex(TemplateView): template_name = 'index.html' def get_context_data(self, **kwargs): # **kwargs contains keyword context initialization values (if any) # Call base implementation to get a context context = super(AboutIndex, self).get_context_data(**kwargs) # Add context data to pass to template context['aboutdata'] = 'Custom data' return context ---------------------------------------------------------------------------- # urls.py from coffeehouse.about.views import AboutIndex from django.urls import path urlpatterns = [ path('about/index/',AboutIndex.as_view(),{'onsale':True}), ]
I chose to create a view that
inherits from TemplateView
first because of its
simplicity and because you already know the purpose of this class.
The example in listing 2-37 and the first example in this chapter
from listing 2-1 produce nearly identical outcomes.
The difference is, listing 2-1
declares a TemplateView
class instance directly as
part of the url (e.g.
TemplateView.as_view(template_name='index.html'))
),
where as listing 2-37 declares an instance of a
TemplateView
sub-class named AboutIndex
.
Comparing the two approaches, you can get the initial feel for the
OOP behavior of class-based views.
The first part in listing 2-37
declares the AboutIndex
class-based view which
inherits its behavior from the TemplateView
class.
Notice the class declares the template_name
attribute
and the get_context_data()
method.
The template_name
value in the AboutIndex
class acts as a default
template for the class-based view. But in OOP fashion, this same
value can be overridden by providing a value at instance creation
(e.g. AboutIndex.as_view(template_name='other.html')
to use the other.html
template).
The get_context_data
method in the AboutIndex
class allows you to add
context data to the class-view template. Notice the signature of
the get_context_data
method uses **kwargs
to gain access to context initialization values (e.g. declared in
the url or parent class-views) and invokes a parent's class
get_context_data
method using the Python
super()
method per standard OOP Python practice. Next,
the get_context_data
method adds the additional
context data with the aboutdata
key and returns the
modified context
reference.
Tip See Appendix A - Methods arguments: Default, optional, *args & **kwargs for more details on the use of **kwargs
in Python.
In the second part of listing
2-37, you can see how the AboutIndex
class-based view
is first imported into a urls.py
file and then
hooked-up to a url definition. Notice how the class-based view is
declared on the url
definition using the
as_view()
method. In addition, notice how the url
definition declares the url extra option
{'onsale':True}
which gets passed as context data to
the class-based view (i.e. in the **kwargs
of the
get_context_data
method).
Tip All class-based views use the as_view()
method
to integrate into url definitions.
Now that you have a basic understanding of Django class-based views, listing 2-38 shows another class-based view with different implementation details.
Listing 2-38 Class-based view inherited from View with multiple HTTP handling
---------------------------------------------------------------------------- # views.py from django.views.generic import View from django.http import HttpResponse from django.shortcuts import render class ContactPage(View): mytemplate = 'contact.html' unsupported = 'Unsupported operation' def get(self, request): return render(request, self.mytemplate) def post(self, request): return HttpResponse(self.unsupported) ---------------------------------------------------------------------------- #urls.py from coffeehouse.contact.views import ContactPage urlpatterns = [ url(r'^contact/$',ContactPage.as_view()), ]
The first difference in listing
2-38 is the class-based view inherits its behavior from the general
purpose django.views.generic.View
class. As outlined
in table 2-11, the View
class provides the core
functionality for all class-based views. So in fact, the
TemplateView
class used in listing 2-37 is a sub-class
of View
, meaning class-based views that use
TemplateView
have access to the same functionalities
of class-based views that use View
.
The reason you would chose one class over another to implement class-based views is rooted in OOP polymorphism principles. For example, in OOP you can have a class hierarchy Drink→ Coffee → Latte, where a Drink class offers generic functionalities available to Drink, Coffee and Latte instances, a Coffee class offers more specific functionalities applicable to Coffee and Latter instances, and a Latte class offers the most specific functionalities applicable to only Latte instances.
Therefore if you know beforehand
you need a class-based view to relinquish control to a template
without applying elaborate business logic or custom request &
response handling, the TemplateView
class offers the
quickest path to a solution vs. the more generic View
class. Expanding on this same principle, once you start working
with Django models and views, you'll come to realize some of the
more specialized class-based views in table 2-11 also offer quicker
solutions than creating a class-based view that inherits from the
general purpose View
class. Now that you know the
reason why you would chose a View
class-based view
over a more specialized class, lets break down the functionality in
listing 2-38.
Notice the class-based view
ContactPage
declares two attributes:
mytemplate
and unsupported
. These are
generic class attributes and I used the mytemplate
name to illustrate there's no relation to the
template_name
attribute used in listing 2-37 and
TemplateView
class-based views. Class-based views
derived from a TemplateView
expect a
template_name
value and automatically use this
template to generate a response. However, class-based views derived
from a View
class don't expect a specific template,
but instead expect you to implement how to generate a response,
which is where the get
and post
methods
in listing 2-38 come into play.
The get
method is
used to handle HTTP GET requests on the view, while the
post
method is used to HTTP POST requests on the view.
This offers a much more modular approach to handle different HTTP
operations vs. standard view methods that require explicitly
inspecting a request and creating conditionals to handle different
HTTP operations. For the moment, don't worry about HTTP GET and
HTTP POST view handling, this is explored in greater detail in
Django forms where the topic is of greater relevance.
Next, notice both the
get
and post
methods declare a
request
input, which represents a Django
HttpRequest
instance just like standard view methods.
In both cases, the methods immediately return a response, but it's
possible to inspect a request value or execute any business logic
before generating a response, just like it can be done in standard
view methods.
The get
method
generates a response with the django.shortcuts.render
method and the post
method generates a response with
the HttpResponse
class, both of which are the same
techniques used to generate responses in standard view methods .
The only minor difference in listing 2-38 is both the
render
method and HttpResponse
class use
instance attributes (e.g. self.mytemplate
,
self.unsupported
) to generate the response, but other
than this, you're free to return a Django HttpResponse
with any of the variations already explained in this chapter (e.g.
listing 2-24 response alternatives, table 2-6 shortcut
responses)
Finally, the last part in listing
2-38 shows how the ContactPage
class-based view is
imported into a urls.py
file and later hooked up to a
url using the as_view()
method.
To close out the discussion on
class-based views and this chapter, we come to the
django.views.generic.RedirectView
class. Similar to
the TemplateView
class-based view which allows you to
quickly generate a response without a view method, the
RedirectView
class-based view allows you to quickly
generate an HTTP redirect -- like the ones described in table 2-5
-- without the need of a view method.
The RedirectView
class supports four attributes described in the following list:
permanent
.- Defaults toFalse
to perform a non-permanent re-direct supported by theHttpResponseRedirect
class described in table 2-5. If set toTrue
, a permanent re-direct is made with theHttpResponsePermanentRedirect
class described in table 2-5.url
.- Defaults toNone
. Defines a url value to perform the redirect.pattern_name
.- Defaults toNone
. Defines a url name to generate a redirect url via thereverse
method. Note thereverse
method is explained in the url naming and namespace section earlier in this chapter.query_string
.- Default toFalse
to append a query string to a redirect url. If provided, thequery_string
value to the redirect url.
And with this we conclude our exploration into Django views and urls. In the next two chapters, you'll learn about Django templates and Jinja templates.