Class-based views. Django Software Foundation Django
Reklamë
Reklamë
Django Documentation, Release 1.6.12.dev20160216120443
{% load
humanize
%}
{{
45000
| intcomma }}
In the above, the
tag loads the humanize tag library, which then makes the intcomma filter available for use.
If you’ve enabled
, you can consult the documentation area in your admin to find
the list of custom libraries in your installation.
The
tag can take multiple library names, separated by spaces. Example:
{% load
humanize i18n
%}
See
Custom template tags and filters
for information on writing your own custom template libraries.
Custom libraries and template inheritance
When you load a custom tag or filter library, the tags/filters are only made available to the current template – not any parent or child templates along the template-inheritance path.
For example, if a template foo.html has {% load humanize %}, a child template (e.g., one that has {% extends "foo.html" %}
) will not have access to the humanize template tags and filters. The child template is responsible for its own {% load humanize %}.
This is a feature for the sake of maintainability and sanity.
See also:
Covers built-in tags, built-in filters, using an alternative template, language, and more.
3.6 Class-based views
A view is a callable which takes a request and returns a response. This can be more than just a function, and Django provides an example of some classes which can be used as views. These allow you to structure your views and reuse code by harnessing inheritance and mixins. There are also some generic views for simple tasks which we’ll get to later, but you may want to design your own structure of reusable views which suits your use case. For full details, see the
class-based views reference documentation .
3.6.1 Introduction to Class-based views
Class-based views provide an alternative way to implement views as Python objects instead of functions. They do not replace function-based views, but have certain differences and advantages when compared to function-based views:
• 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.
The relationship and history of generic views, class-based views, and class-based generic views
In the beginning there was only the view function contract, Django passed your function an
and expected back an
. This was the extent of what Django provided.
Early on it was recognized that there were common idioms and patterns found in view development. Function-based generic views were introduced to abstract these patterns and ease view development for the common cases.
3.6. Class-based views 251
Django Documentation, Release 1.6.12.dev20160216120443
The problem with function-based generic views is that while they covered the simple cases well, there was no way to extend or customize them beyond some simple configuration options, limiting their usefulness in many real-world applications.
Class-based generic views were created with the same objective as function-based generic views, to make view development easier. However, the way the solution is implemented, through the use of mixins, provides a toolkit that results in class-based generic views being more extensible and flexible than their function-based counterparts.
If you have tried function based generic views in the past and found them lacking, you should not think of class-based generic views as simply a class-based equivalent, but rather as a fresh approach to solving the original problems that generic views were meant to solve.
The toolkit of base classes and mixins that Django uses to build class-based generic views are built for maximum flexibility, and as such have many hooks in the form of default method implementations and attributes that you are unlikely to be concerned with in the simplest use cases. For example, instead of limiting you to a class based attribute for form_class, the implementation uses a get_form method, which calls a get_form_class method, which in its default implementation just returns the form_class attribute of the class. This gives you several options for specifying what form to use, from a simple attribute, to a fully dynamic, callable hook. These options seem to add hollow complexity for simple situations, but without them, more advanced designs would be limited.
Using class-based views
At its core, a class-based view allows you to respond to different HTTP request methods with different class instance methods, instead of with conditionally branching code inside a single view function.
So where the code to handle HTTP GET in a view function would look something like:
from
django.http
import
HttpResponse
def my_view
(request):
if
request.method == 'GET' :
# <view logic>
return
HttpResponse( 'result' )
In a class-based view, this would become:
from
django.http
import
HttpResponse
from
django.views.generic
import
View
class MyView
(View):
def get
( self , request):
# <view logic>
return
HttpResponse( 'result' )
Because Django’s URL resolver expects to send the request and associated arguments to a callable function, not a class, class-based views have an
class method which serves as the callable entry point to your class. The as_view entry point creates an instance of your class and calls its
method. dispatch looks at the request to determine whether it is a GET, POST, etc, and relays the request to a matching method if one is defined, or raises
if not:
# urls.py
from
django.conf.urls
import
patterns
from
myapp.views
import
MyView urlpatterns = patterns( '' ,
( r'^about/' , MyView.as_view()),
)
252 Chapter 3. Using Django
Django Documentation, Release 1.6.12.dev20160216120443
It is worth noting that what your method returns is identical to what you return from a function-based view, namely some form of
or
objects are valid to use inside a class-based view.
While a minimal class-based view does not require any class attributes to perform its job, class attributes are useful in many class-based designs, and there are two ways to configure or set class attributes.
The first is the standard Python way of subclassing and overriding attributes and methods in the subclass. So that if your parent class had an attribute greeting like this:
from
django.http
import
HttpResponse
from
django.views.generic
import
View
class GreetingView
(View): greeting = "Good Day"
def get
( self , request):
return
HttpResponse( self
.
greeting)
You can override that in a subclass:
class MorningGreetingView
(GreetingView): greeting = "Morning to ya"
Another option is to configure class attributes as keyword arguments to the
call in the URLconf: urlpatterns = patterns( '' ,
( r'^about/' , GreetingView.as_view(greeting= "G'day" )),
)
Note: While your class is instantiated for each request dispatched to it, class attributes set through the
entry point are configured only once at the time your URLs are imported.
Using mixins
Mixins are a form of multiple inheritance where behaviors and attributes of multiple parent classes can be combined.
For example, in the generic class-based views there is a mixin called
whose primary purpose is to define the method
. When combined with the behavior of the
base class, the result is a
class that will dispatch requests to the appropriate matching methods (a behavior defined in the View base class), and that has a
method that uses a
attribute to return a
object (a behavior defined in the TemplateResponseMixin).
Mixins are an excellent way of reusing code across multiple classes, but they come with some cost. The more your code is scattered among mixins, the harder it will be to read a child class and know what exactly it is doing, and the harder it will be to know which methods from which mixins to override if you are subclassing something that has a deep inheritance tree.
Note also that you can only inherit from one generic view - that is, only one parent class may inherit from
and the rest (if any) should be mixins. Trying to inherit from more than one class that inherits from View - for example, trying to use a form at the top of a list and combining
and
- won’t work as expected.
Handling forms with class-based views
A basic function-based view that handles forms may look something like this:
3.6. Class-based views 253
Django Documentation, Release 1.6.12.dev20160216120443
from
django.http
import
HttpResponseRedirect
from
django.shortcuts
import
render
from
.forms
import
MyForm
def myview
(request):
if
request.method == "POST" : form = MyForm(request.POST)
if
form.is_valid():
# <process form cleaned data>
return
HttpResponseRedirect( '/success/' )
else
: form = MyForm(initial={ 'key' : 'value' })
return
render(request, 'form_template.html' , { 'form' : form})
A similar class-based view might look like:
from
django.http
import
HttpResponseRedirect
from
django.shortcuts
import
render
from
django.views.generic
import
View
from
.forms
import
MyForm
class MyFormView
(View): form_class = MyForm initial = { 'key' : 'value' } template_name = 'form_template.html'
def get
( self , request, *args, **kwargs): form = self
.
form_class(initial= self
.
initial)
return
render(request, self
.
template_name, { 'form' : form})
def post
( self , request, *args, **kwargs): form = self
.
form_class(request.POST)
if
form.is_valid():
# <process form cleaned data>
return
HttpResponseRedirect( '/success/' )
return
render(request, self
.
template_name, { 'form' : form})
This is a very simple case, but you can see that you would then have the option of customizing this view by overriding any of the class attributes, e.g. form_class, via URLconf configuration, or subclassing and overriding one or more of the methods (or both!).
Decorating class-based views
The extension of class-based views isn’t limited to using mixins. You can use also use decorators. Since classbased views aren’t functions, decorating them works differently depending on if you’re using as_view or creating a subclass.
Decorating in URLconf
The simplest way of decorating class-based views is to decorate the result of the
method. The easiest place to do this is in the URLconf where you deploy your view:
254 Chapter 3. Using Django
Django Documentation, Release 1.6.12.dev20160216120443
from
django.contrib.auth.decorators
import
login_required, permission_required
from
django.views.generic
import
TemplateView
from
.views
import
VoteView urlpatterns = patterns( '' ,
( r'^about/' , login_required(TemplateView.as_view(template_name= "secret.html" ))),
( r'^vote/' , permission_required( 'polls.can_vote' )(VoteView.as_view())),
)
This approach applies the decorator on a per-instance basis. If you want every instance of a view to be decorated, you need to take a different approach.
Decorating the class
To decorate every instance of a class-based view, you need to decorate the class definition itself. To do this you apply the decorator to the
method of the class.
A method on a class isn’t quite the same as a standalone function, so you can’t just apply a function decorator to the method – you need to transform it into a method decorator first. The method_decorator decorator transforms a function decorator into a method decorator so that it can be used on an instance method. For example:
from
django.contrib.auth.decorators
import
login_required
from
django.utils.decorators
import
method_decorator
from
django.views.generic
import
TemplateView
class ProtectedView
(TemplateView): template_name = 'secret.html'
@method_decorator(login_required)
def dispatch
( self , *args, **kwargs):
return
super (ProtectedView, self ).dispatch(*args, **kwargs)
In this example, every instance of ProtectedView will have login protection.
Note: method_decorator passes *args and **kwargs as parameters to the decorated method on the class. If your method does not accept a compatible set of parameters it will raise a TypeError exception.
3.6.2 Built-in Class-based generic views
Writing Web applications can be monotonous, because we repeat certain patterns again and again. Django tries to take away some of that monotony at the model and template layers, but Web developers also experience this boredom at the view level.
Django’s generic views were developed to ease that pain. They take certain common idioms and patterns found in view development and abstract them so that you can quickly write common views of data without having to write too much code.
We can recognize certain common tasks, like displaying a list of objects, and write code that displays a list of any object. Then the model in question can be passed as an extra argument to the URLconf.
Django ships with generic views to do the following:
3.6. Class-based views 255
Django Documentation, Release 1.6.12.dev20160216120443
• Display list and detail pages for a single object. If we were creating an application to manage conferences then a TalkListView and a RegisteredUserListView would be examples of list views. A single talk page is an example of what we call a “detail” view.
• Present date-based objects in year/month/day archive pages, associated detail, and “latest” pages.
• Allow users to create, update, and delete objects – with or without authorization.
Taken together, these views provide easy interfaces to perform the most common tasks developers encounter.
Extending generic views
There’s no question that using generic views can speed up development substantially. In most projects, however, there comes a moment when the generic views no longer suffice. Indeed, the most common question asked by new Django developers is how to make generic views handle a wider array of situations.
This is one of the reasons generic views were redesigned for the 1.3 release - previously, they were just view functions with a bewildering array of options; now, rather than passing in a large amount of configuration in the URLconf, the recommended way to extend generic views is to subclass them, and override their attributes or methods.
That said, generic views will have a limit. If you find you’re struggling to implement your view as a subclass of a generic view, then you may find it more effective to write just the code you need, using your own class-based or functional views.
More examples of generic views are available in some third party applications, or you could write your own as needed.
Generic views of objects
certainly is useful, but Django’s generic views really shine when it comes to presenting views of your database content. Because it’s such a common task, Django comes with a handful of built-in generic views that make generating list and detail views of objects incredibly easy.
Let’s start by looking at some examples of showing a list of objects or an individual object.
We’ll be using these models:
# models.py
from
django.db
import
models
class Publisher
(models.Model): name = models.CharField(max_length= 30 ) address = models.CharField(max_length= 50 ) city = models.CharField(max_length= 60 ) state_province = models.CharField(max_length= 30 ) country = models.CharField(max_length= 50 ) website = models.URLField()
class Meta
: ordering = [ "-name" ]
# On Python 3: def __str__(self):
def __unicode__
( self ):
return
self
.
name
class Author
(models.Model): salutation = models.CharField(max_length= 10 ) name = models.CharField(max_length= 200 ) email = models.EmailField() headshot = models.ImageField(upload_to= 'author_headshots' )
256 Chapter 3. Using Django
Django Documentation, Release 1.6.12.dev20160216120443
# On Python 3: def __str__(self):
def __unicode__
( self ):
return
self
.
name
class Book
(models.Model): title = models.CharField(max_length= 100 ) authors = models.ManyToManyField( 'Author' ) publisher = models.ForeignKey(Publisher) publication_date = models.DateField()
Now we need to define a view:
# views.py
from
django.views.generic
import
ListView
from
books.models
import
Publisher
class PublisherList
(ListView): model = Publisher
Finally hook that view into your urls:
# urls.py
from
django.conf.urls
import
patterns, url
from
books.views
import
PublisherList urlpatterns = patterns( '' , url( r'^publishers/$' , PublisherList.as_view()),
)
That’s all the Python code we need to write. We still need to write a template, however. We could explicitly tell the view which template to use by adding a template_name attribute to the view, but in the absence of an explicit template Django will infer one from the object’s name. In this case, the inferred template will be
"books/publisher_list.html"
– the “books” part comes from the name of the app that defines the model, while the “publisher” bit is just the lowercased version of the model’s name.
Note: Thus, when (for example) the
django.template.loaders.app_directories.Loader
template loader is enabled in
a template location could be:
/path/to/project/books/templates/books/publisher_list.html
This template will be rendered against a context containing a variable called object_list that contains all the publisher objects. A very simple template might look like the following:
{% extends
"base.html"
%}
{% block
content
%}
< h2 >Publishers</ h2 >
< ul >
{% for
publisher
in
object_list
%}
< li >
{{
publisher.name
}}
</ li >
{% endfor %}
</ ul >
{% endblock %}
That’s really all there is to it. All the cool features of generic views come from changing the attributes set on the generic view. The
documents all the generic views and their options in detail; the rest of this document will consider some of the common ways you might customize and extend generic views.
3.6. Class-based views 257
Django Documentation, Release 1.6.12.dev20160216120443
Making “friendly” template contexts
You might have noticed that our sample publisher list template stores all the publishers in a variable named object_list . While this works just fine, it isn’t all that “friendly” to template authors: they have to “just know” that they’re dealing with publishers here.
Well, if you’re dealing with a model object, this is already done for you. When you are dealing with an object or queryset, Django is able to populate the context using the lower cased version of the model class’ name. This is provided in addition to the default object_list entry, but contains exactly the same data, i.e. publisher_list.
If this still isn’t a good match, you can manually set the name of the context variable. The context_object_name attribute on a generic view specifies the context variable to use:
# views.py
from
django.views.generic
import
ListView
from
books.models
import
Publisher
class PublisherList
(ListView): model = Publisher context_object_name = 'my_favourite_publishers'
Providing a useful context_object_name is always a good idea. Your coworkers who design templates will thank you.
Adding extra context
Often you simply need to present some extra information beyond that provided by the generic view. For example, think of showing a list of all the books on each publisher detail page. The
generic view provides the publisher to the context, but how do we get additional information in that template?
The answer is to subclass
and provide your own implementation of the get_context_data method. The default implementation simply adds the object being displayed to the template, but you can override it to send more:
from
django.views.generic
import
DetailView
from
books.models
import
Publisher, Book
class PublisherDetail
(DetailView): model = Publisher
def get_context_data
( self , **kwargs):
# Call the base implementation first to get a context context = super (PublisherDetail, self ).get_context_data(**kwargs)
# Add in a QuerySet of all the books context[ 'book_list' ] = Book.objects.all()
return
context
Note: Generally, get_context_data will merge the context data of all parent classes with those of the current class. To preserve this behavior in your own classes where you want to alter the context, you should be sure to call get_context_data on the super class. When no two classes try to define the same key, this will give the expected results. However if any class attempts to override a key after parent classes have set it (after the call to super), any children of that class will also need to explicitly set it after super if they want to be sure to override all parents. If you’re having trouble, review the method resolution order of your view.
258 Chapter 3. Using Django
Django Documentation, Release 1.6.12.dev20160216120443
Viewing subsets of objects
Now let’s take a closer look at the model argument we’ve been using all along. The model argument, which specifies the database model that the view will operate upon, is available on all the generic views that operate on a single object or a collection of objects. However, the model argument is not the only way to specify the objects that the view will operate upon – you can also specify the list of objects using the queryset argument:
from
django.views.generic
import
DetailView
from
books.models
import
Publisher
class PublisherDetail
(DetailView): context_object_name = 'publisher' queryset = Publisher.objects.all()
Specifying model = Publisher is really just shorthand for saying queryset =
Publisher.objects.all()
.
However, by using queryset to define a filtered list of objects you can be more specific about the objects that will be visible in the view (see
for more information about
objects, and see the
for the complete details).
To pick a simple example, we might want to order a list of books by publication date, with the most recent first:
from
django.views.generic
import
ListView
from
books.models
import
Book
class BookList
(ListView): queryset = Book.objects.order_by( '-publication_date' ) context_object_name = 'book_list'
That’s a pretty simple example, but it illustrates the idea nicely. Of course, you’ll usually want to do more than just reorder objects. If you want to present a list of books by a particular publisher, you can use the same technique:
from
django.views.generic
import
ListView
from
books.models
import
Book
class AcmeBookList
(ListView): context_object_name = 'book_list' queryset = Book.objects.filter(publisher__name= 'Acme Publishing' ) template_name = 'books/acme_list.html'
Notice that along with a filtered queryset, we’re also using a custom template name. If we didn’t, the generic view would use the same template as the “vanilla” object list, which might not be what we want.
Also notice that this isn’t a very elegant way of doing publisher-specific books. If we want to add another publisher page, we’d need another handful of lines in the URLconf, and more than a few publishers would get unreasonable.
We’ll deal with this problem in the next section.
Note: If you get a 404 when requesting /books/acme/, check to ensure you actually have a Publisher with the name ‘ACME Publishing’. Generic views have an allow_empty parameter for this case. See the
for more details.
Dynamic filtering
Another common need is to filter down the objects given in a list page by some key in the URL. Earlier we hard-coded the publisher’s name in the URLconf, but what if we wanted to write a view that displayed all the books by some
3.6. Class-based views 259
Django Documentation, Release 1.6.12.dev20160216120443
arbitrary publisher?
Handily, the ListView has a
method we can override. Previously, it has just been returning the value of the queryset attribute, but now we can add more logic.
The key part to making this work is that when class-based views are called, various useful things are stored on self; as well as the request (self.request) this includes the positional (self.args) and name-based (self.kwargs) arguments captured according to the URLconf.
Here, we have a URLconf with a single captured group:
# urls.py
from
django.conf.urls
import
patterns
from
books.views
import
PublisherBookList urlpatterns = patterns( '' ,
( r'^books/([\w-]+)/$' , PublisherBookList.as_view()),
)
Next, we’ll write the PublisherBookList view itself:
# views.py
from
django.shortcuts
import
get_object_or_404
from
django.views.generic
import
ListView
from
books.models
import
Book, Publisher
class PublisherBookList
(ListView): template_name = 'books/books_by_publisher.html'
def get_queryset
( self ): self
.
publisher = get_object_or_404(Publisher, name= self
.
args[ 0 ])
return
Book.objects.filter(publisher= self
.
publisher)
As you can see, it’s quite easy to add more logic to the queryset selection; if we wanted, we could use self.request.user
to filter using the current user, or other more complex logic.
We can also add the publisher into the context at the same time, so we can use it in the template:
# ...
def get_context_data
( self , **kwargs):
# Call the base implementation first to get a context context = super (PublisherBookList, self ).get_context_data(**kwargs)
# Add in the publisher context[ 'publisher' ] = self
.
publisher
return
context
Performing extra work
The last common pattern we’ll look at involves doing some extra work before or after calling the generic view.
Imagine we had a last_accessed field on our Author model that we were using to keep track of the last time anybody looked at that author:
# models.py
from
django.db
import
models
class Author
(models.Model): salutation = models.CharField(max_length= 10 )
260 Chapter 3. Using Django
Django Documentation, Release 1.6.12.dev20160216120443
name = models.CharField(max_length= 200 ) email = models.EmailField() headshot = models.ImageField(upload_to= 'author_headshots' ) last_accessed = models.DateTimeField()
The generic DetailView class, of course, wouldn’t know anything about this field, but once again we could easily write a custom view to keep that field updated.
First, we’d need to add an author detail bit in the URLconf to point to a custom view:
from
django.conf.urls
import
patterns, url
from
books.views
import
AuthorDetailView urlpatterns = patterns( '' ,
#...
url( r'^authors/(?P<pk>\d+)/$' , AuthorDetailView.as_view(), name= 'author-detail' ),
)
Then we’d write our new view – get_object is the method that retrieves the object – so we simply override it and wrap the call:
from
django.views.generic
import
DetailView
from
django.utils
import
timezone
from
books.models
import
Author
class AuthorDetailView
(DetailView): queryset = Author.objects.all()
def get_object
( self ):
# Call the superclass object
=
super (AuthorDetailView, self ).get_object()
# Record the last accessed date object
.
last_accessed = timezone.now() object
.
save()
# Return the object
return
object
Note: The URLconf here uses the named group pk - this name is the default name that DetailView uses to find the value of the primary key used to filter the queryset.
If you want to call the group something else, you can set pk_url_kwarg on the view. More details can be found in the reference for
3.6.3 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
Using a form in a view ). To help
avoid this, Django provides a collection of generic class-based views for form processing.
3.6. Class-based views 261
Django Documentation, Release 1.6.12.dev20160216120443
Basic Forms
Given a simple contact form:
# 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
The view can be constructed using a FormView:
# 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 (ContactView, self ).form_valid(form)
Notes:
• FormView inherits
so
can be used here.
• The default implementation for
simply redirects to the
Model Forms
Generic views really shine when working with models. These generic views will automatically create a
so long as they can work out which model class to use:
• If the
attribute is given, that model class will be used.
• If
returns an object, the class of that object will be used.
• If a
is given, the model for that queryset will be used.
Model form views provide a
implementation that saves the model automatically. You can override this if you have any special requirements; see below for examples.
You don’t even need to provide a success_url for
or
- they will use
on the model object if available.
If you want to use a custom
(for instance to add extra validation) simply set
on your view.
262 Chapter 3. Using Django
Django Documentation, Release 1.6.12.dev20160216120443
Note: When specifying a custom form class, you must still specify the model, even though the
may be a
First we need to add
to our Author class:
# models.py
from
django.core.urlresolvers
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})
Then we can use
and friends to do the actual work. Notice how we’re just configuring the generic class-based views here; we don’t have to write any logic ourselves:
# views.py
from
django.views.generic.edit
import
CreateView, UpdateView, DeleteView
from
django.core.urlresolvers
import
reverse_lazy
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 have to use
here, not just reverse as the urls are not loaded when the file is imported.
In Django 1.6, the fields attribute was added, which works the same way as the fields attribute on the inner
Meta class on
Omitting the fields attribute will work as previously, but is deprecated and this attribute will be required from 1.8
(unless you define the form class in another way).
Finally, we hook these new views into the URLconf:
# urls.py
from
django.conf.urls
import
patterns, url
from
myapp.views
import
AuthorCreate, AuthorUpdate, AuthorDelete urlpatterns = patterns( '' ,
# ...
url( r'author/add/$' , AuthorCreate.as_view(), name= 'author_add' ), url( r'author/(?P<pk>\d+)/$' , AuthorUpdate.as_view(), name= 'author_update' ), url( r'author/(?P<pk>\d+)/delete/$' , AuthorDelete.as_view(), name= 'author_delete' ),
)
3.6. Class-based views 263
Django Documentation, Release 1.6.12.dev20160216120443
Note: These views inherit
SingleObjectTemplateResponseMixin
which uses
to construct the
based on the model.
In this example:
•
and
use myapp/author_form.html
•
uses myapp/author_confirm_delete.html
If you wish to have separate templates for
and
or
on your view class.
Models and request.user
To track the user that created an object using a
to do this. First, add the foreign key relation to the model:
# models.py
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)
# ...
In the view, ensure that you don’t include created_by in the list of fields to edit, and override
to add the user:
# views.py
from
django.views.generic.edit
import
CreateView
from
myapp.models
import
Author
class AuthorCreate
(CreateView): model = Author fields = [ 'name' ]
def form_valid
( self , form): form.instance.created_by = self
.
request.user
return
super (AuthorCreate, self ).form_valid(form)
Note that you’ll need to
using
, or alternatively handle unauthorized users
in the
AJAX example
Here is a simple example showing how you might go about implementing a form that works for AJAX requests as well as ‘normal’ form POSTs:
import
json
from
django.http
import
HttpResponse
from
django.views.generic.edit
import
CreateView
from
myapp.models
import
Author
264 Chapter 3. Using Django
Django Documentation, Release 1.6.12.dev20160216120443
class AjaxableResponseMixin
( object ):
"""
Mixin to add AJAX support to a form.
Must be used with an object-based FormView (e.g. CreateView)
"""
def render_to_json_response
( self , context, **response_kwargs): data = json.dumps(context) response_kwargs[ 'content_type' ] = 'application/json'
return
HttpResponse(data, **response_kwargs)
def form_invalid
( self , form): response = super (AjaxableResponseMixin, self ).form_invalid(form)
if
self
.
request.is_ajax():
return
self
.
render_to_json_response(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 (AjaxableResponseMixin, self ).form_valid(form)
if
self
.
request.is_ajax(): data = {
'pk' : self
.
object.pk,
}
return
self
.
render_to_json_response(data)
else
:
return
response
class AuthorCreate
(AjaxableResponseMixin, CreateView): model = Author fields = [ 'name' ]
3.6.4 Using mixins with class-based views
Caution: This is an advanced topic. A working knowledge of
is advised before exploring these techniques.
Django’s built-in class-based views provide a lot of functionality, but some of it you may want to use separately.
For instance, you may want to write a view that renders a template to make the HTTP response, but you can’t use
; perhaps you need to render a template only on POST, with GET doing something else entirely.
While you could use
directly, this will likely result in duplicate code.
For this reason, Django also provides a number of mixins that provide more discrete functionality. Template rendering, for instance, is encapsulated in the
. The Django reference documentation contains
full documentation of all the mixins .
Context and template responses
Two central mixins are provided that help in providing a consistent interface to working with templates in class-based views.
Every built in view which returns a
will call the
method that TemplateResponseMixin provides. Most of the time this will
3.6. Class-based views 265
Django Documentation, Release 1.6.12.dev20160216120443
be called for you (for instance, it is called by the get() method implemented by both
and
); similarly, it’s unlikely that you’ll need to override it, although if you want your response to
return something not rendered via a Django template then you’ll want to do it. For an example of this, see the
render_to_response() itself calls
which by default will just look up
on the class-based view; two other mixins
SingleObjectTemplateResponseMixin
and
MultipleObjectTemplateResponseMixin
override this to provide more flexible defaults when dealing with actual objects.
Every built in view which needs context data, such as for rendering a template (including
TemplateResponseMixin above), should call
passing any data they want to ensure is in there as keyword arguments. get_context_data() returns a dictionary; in ContextMixin it simply returns its keyword arguments, but it is common to override this to add more members to the dictionary.
Building up Django’s generic class-based views
Let’s look at how two of Django’s generic class-based views are built out of mixins providing discrete functionality.
We’ll consider
, which renders a “detail” view of an object, and
objects, typically from a queryset, and optionally paginate them. This will introduce us to four mixins which between them provide useful functionality when working with either a single Django object, or multiple objects.
There are also mixins involved in the generic edit views (
, and the model-specific views
and
), and in the date-based generic views. These are covered in the
mixin reference documentation .
DetailView: working with a single Django object
To show the detail of an object, we basically need to do two things: we need to look up the object and then we need to make a
with a suitable template, and that object as context.
To get the object,
relies on
method that figures out the object based on the URL of the request (it looks for pk and slug keyword arguments as declared in the
URLConf, and looks the object up either from the
attribute on the view, or the
attribute if that’s provided). SingleObjectMixin also overrides
get_context_data() , which is used across all Django’s built
in class-based views to supply context data for template renders.
To then make a
uses
SingleObjectTemplateResponseMixin ,
which extends
, overriding get_template_names()
as discussed above.
It actually provides a fairly sophisticated set of options, but the main one that most people are going to use is <app_label>/<model_name>_detail.html.
The _detail part can be changed by setting
on a subclass to something else. (For instance, the
use _form for create and update views, and _confirm_delete for delete views.)
ListView: working with many Django objects
Lists of objects follow roughly the same pattern: we need a (possibly paginated) list of objects, typically a
and then we need to make a
with a suitable template using that list of objects.
To get the objects,
uses
and
, there’s no need to key off parts of the URL
to figure out the queryset to work with, so the default just uses the
or
attribute on the view class.
A common reason to override
here would be to dynamically vary the objects, such as depending on the current user or to exclude posts in the future for a blog.
266 Chapter 3. Using Django
Django Documentation, Release 1.6.12.dev20160216120443
also overrides
to include appropriate context variables for pagination (providing dummies if pagination is disabled). It relies on object_list being passed in as a keyword argument, which
arranges for it.
To make a
then uses
MultipleObjectTemplateResponseMixin ; as
with
SingleObjectTemplateResponseMixin
above, this overrides get_template_names() to provide
, with the most commonly-used being <app_label>/<model_name>_list.html,
with the _list part again being taken from the
attribute. (The date based generic views use suffixes such as _archive, _archive_year and so on to use different templates for the various specialised date-based list views.)
Using Django’s class-based view mixins
Now we’ve seen how Django’s generic class-based views use the provided mixins, let’s look at other ways we can combine them. Of course we’re still going to be combining them with either built-in class-based views, or other generic class-based views, but there are a range of rarer problems you can solve than are provided for by Django out of the box.
Warning: Not all mixins can be used together, and not all generic class based views can be used with all other mixins. Here we present a few examples that do work; if you want to bring together other functionality then you’ll have to consider interactions between attributes and methods that overlap between the different classes you’re using, and how method resolution order will affect which versions of the methods will be called in what order.
The reference documentation for Django’s
and
will help you in understanding which attributes and methods are likely to cause conflict between different classes and mixins.
If in doubt, it’s often better to back off and base your work on
or
and
. Although you will probably end up writing more code,
it is more likely to be clearly understandable to someone else coming to it later, and with fewer interactions to worry about you will save yourself some thinking. (Of course, you can always dip into Django’s implementation of the generic class based views for inspiration on how to tackle problems.)
Using SingleObjectMixin with View
If we want to write a simple class-based view that responds only to POST, we’ll subclass
and write a post() method in the subclass. However if we want our processing to work on a particular object, identified from the URL, we’ll want the functionality provided by
We’ll demonstrate this with the publisher modelling we used in the
generic class-based views introduction .
# views.py
from
django.http
import
HttpResponseForbidden, HttpResponseRedirect
from
django.core.urlresolvers
import
reverse
from
django.views.generic
import
View
from
django.views.generic.detail
import
SingleObjectMixin
from
books.models
import
Author
class RecordInterest
(SingleObjectMixin, View):
"""Records the current user's interest in an author.""" model = Author
def post
( self , request, *args, **kwargs):
if not
request.user.is_authenticated():
return
HttpResponseForbidden()
# Look up the author we're interested in.
3.6. Class-based views 267
Django Documentation, Release 1.6.12.dev20160216120443
self
.
object = self
.
get_object()
# Actually record interest somehow here!
return
HttpResponseRedirect(reverse( 'author-detail' , kwargs={ 'pk' : self
.
object.pk}))
In practice you’d probably want to record the interest in a key-value store rather than in a relational database, so we’ve left that bit out. The only bit of the view that needs to worry about using
is where we want to look up the author we’re interested in, which it just does with a simple call to self.get_object(). Everything else is taken care of for us by the mixin.
We can hook this into our URLs easily enough:
# urls.py
from
django.conf.urls
import
patterns, url
from
books.views
import
RecordInterest urlpatterns = patterns( '' ,
#...
url( r'^author/(?P<pk>\d+)/interest/$' , RecordInterest.as_view(), name= 'author-interest' ),
)
Note the pk named group, which
uses to look up the Author instance. You could also use a slug, or any of the other features of
Using SingleObjectMixin with ListView
provides built-in pagination, but you might want to paginate a list of objects that are all linked (by a foreign key) to another object. In our publishing example, you might want to paginate through all the books by a particular publisher.
One way to do this is to combine
with
, so that the queryset for the paginated
list of books can hang off the publisher found as the single object. In order to do this, we need to have two different querysets:
Book
queryset for use by
Since we have access to the Publisher whose books we want to list, we simply override get_queryset() and use the Publisher’s
Publisher
queryset for use in
We’ll rely on the default implementation of get_object() to fetch the correct Publisher object. However, we need to explicitly pass a queryset argument because otherwise the default implementation of get_object() would call get_queryset() which we have overridden to return Book objects instead of Publisher ones.
Note: We have to think carefully about get_context_data(). Since both
and
will put things in the context data under the value of context_object_name if it’s set, we’ll instead explictly ensure the Publisher is in the context data.
will add in the suitable page_obj and paginator for us providing we remember to call super().
Now we can write a new PublisherDetail:
from
django.views.generic
import
ListView
from
django.views.generic.detail
import
SingleObjectMixin
from
books.models
import
Publisher
class PublisherDetail
(SingleObjectMixin, ListView): paginate_by = 2 template_name = "books/publisher_detail.html"
268 Chapter 3. Using Django
Django Documentation, Release 1.6.12.dev20160216120443
def get
( self , request, *args, **kwargs): self
.
object = self
.
get_object(queryset=Publisher.objects.all())
return
super (PublisherDetail, self ).get(request, *args, **kwargs)
def get_context_data
( self , **kwargs): context = super (PublisherDetail, self ).get_context_data(**kwargs) context[ 'publisher' ] = self
.
object
return
context
def get_queryset
( self ):
return
self
.
object.book_set.all()
Notice how we set self.object within get() so we can use it again later in get_context_data() and get_queryset()
. If you don’t set template_name, the template will default to the normal
choice, which in this case would be "books/book_list.html" because it’s a list of books;
knows nothing about
SingleObjectMixin , so it doesn’t have any clue this view is anything to do with a Publisher.
The paginate_by is deliberately small in the example so you don’t have to create lots of books to see the pagination working! Here’s the template you’d want to use:
{% extends
"base.html"
%}
{% block
content
%}
< h2 >Publisher
{{
publisher.name
}}
</ h2 >
< ol >
{% for
book
in
page_obj
%}
< li >
{{
book.title
}}
</ li >
{% endfor %}
</ ol >
< div class
=
"pagination" >
< span class
=
"step-links" >
{% if
page_obj.has_previous
%}
< a href
=
"?page=
{{
page_obj.previous_page_number
}}
" >previous</ a >
{% endif %}
< span class
=
"current" >
Page
{{
page_obj.number
}}
of
{{
paginator.num_pages
}}
.
</ span >
{% if
page_obj.has_next
%}
< a href
=
"?page=
{{
page_obj.next_page_number
}}
" >next</ a >
{% endif %}
</ span >
</ div >
{% endblock %}
Avoid anything more complex
Generally you can use
and
when you need their functionality. As shown above, with a bit of care you can even combine SingleObjectMixin with
things get increasingly complex as you try to do so, and a good rule of thumb is:
Hint:
Each of your views should use only mixins or views from one of the groups of generic class-based views:
and date.
For example it’s fine to combine
(built in view) with
3.6. Class-based views 269
Django Documentation, Release 1.6.12.dev20160216120443
(generic list), but you’re likely to have problems combining SingleObjectMixin
(generic detail) with MultipleObjectMixin (generic list).
To show what happens when you try to get more sophisticated, we show an example that sacrifices readability and maintainability when there is a simpler solution. First, let’s look at a naive attempt to combine
with
to enable use to POST a Django
to the same URL as we’re displaying an object using
Using FormMixin with DetailView
Think back to our earlier example of using
and
together. We were recording a user’s interest in a particular author; say now that we want to let them leave a message saying why they like them. Again, let’s assume we’re not going to store this in a relational database but instead in something more esoteric that we won’t worry about here.
At this point it’s natural to reach for a
to encapsulate the information sent from the user’s browser to Django. Say also that we’re heavily invested in REST , so we want to use the same URL for displaying the author as for capturing the message from the user. Let’s rewrite our AuthorDetailView to do that.
We’ll keep the GET handling from
, although we’ll have to add a
into the context data so we can render it in the template. We’ll also want to pull in form processing from
, and write a bit of code so that
on POST the form gets called appropriately.
Note: We use
and implement post() ourselves rather than try to mix
with
(which provides a suitable post() already) because both of the views implement get(), and things would get much more confusing.
Our new AuthorDetail looks like this:
# CAUTION: you almost certainly do not want to do this.
# It is provided as part of a discussion of problems you can
# run into when combining different generic class-based view
# functionality that is not designed to be used together.
from
django
import
forms
from
django.http
import
HttpResponseForbidden
from
django.core.urlresolvers
import
reverse
from
django.views.generic
import
DetailView
from
django.views.generic.edit
import
FormMixin
from
books.models
import
Author
class AuthorInterestForm
(forms.Form): message = forms.CharField()
class AuthorDetail
(FormMixin, DetailView): model = Author form_class = AuthorInterestForm
def get_success_url
( self ):
return
reverse( 'author-detail' , kwargs={ 'pk' : self
.
object.pk})
def get_context_data
( self , **kwargs): context = super (AuthorDetail, self ).get_context_data(**kwargs) form_class = self
.
get_form_class() context[ 'form' ] = self
.
get_form(form_class)
270 Chapter 3. Using Django
Django Documentation, Release 1.6.12.dev20160216120443
return
context
def post
( self , request, *args, **kwargs):
if not
request.user.is_authenticated():
return
HttpResponseForbidden() self
.
object = self
.
get_object() form_class = self
.
get_form_class() form = self
.
get_form(form_class)
if
form.is_valid():
return
self
.
form_valid(form)
else
:
return
self
.
form_invalid(form)
def form_valid
( self , form):
# Here, we would record the user's interest using the message
# passed in form.cleaned_data['message']
return
super (AuthorDetail, self ).form_valid(form) get_success_url() is just providing somewhere to redirect to, which gets used in the default implementation of form_valid()
. We have to provide our own post() as noted earlier, and override get_context_data() to make the
available in the context data.
A better solution
It should be obvious that the number of subtle interactions between
and
is already testing our ability to manage things. It’s unlikely you’d want to write this kind of class yourself.
In this case, it would be fairly easy to just write the post() method yourself, keeping
as the only generic functionality, although writing
handling code involves a lot of duplication.
Alternatively, it would still be easier than the above approach to have a separate view for processing the form, which could use
distinct from
without concerns.
An alternative better solution
What we’re really trying to do here is to use two different class based views from the same URL. So why not do just that? We have a very clear division here: GET requests should get the
(with the
added to the context data), and POST requests should get the
. Let’s set up those views first.
The AuthorDisplay view is almost the same as
when we first introduced AuthorDetail ; we have to write our
own get_context_data() to make the AuthorInterestForm available to the template. We’ll skip the get_object() override from before for clarity.
from
django.views.generic
import
DetailView
from
django
import
forms
from
books.models
import
Author
class AuthorInterestForm
(forms.Form): message = forms.CharField()
class AuthorDisplay
(DetailView): model = Author
def get_context_data
( self , **kwargs): context = super (AuthorDisplay, self ).get_context_data(**kwargs)
3.6. Class-based views 271
Django Documentation, Release 1.6.12.dev20160216120443
context[ 'form' ] = AuthorInterestForm()
return
context
Then the AuthorInterest is a simple
so we can find the author we’re talking about, and we have to remember to set template_name to ensure that form errors will render the same template as AuthorDisplay is using on GET.
from
django.core.urlresolvers
import
reverse
from
django.http
import
HttpResponseForbidden
from
django.views.generic
import
FormView
from
django.views.generic.detail
import
SingleObjectMixin
class AuthorInterest
(SingleObjectMixin, FormView): template_name = 'books/author_detail.html' form_class = AuthorInterestForm model = Author
def post
( self , request, *args, **kwargs):
if not
request.user.is_authenticated():
return
HttpResponseForbidden() self
.
object = self
.
get_object()
return
super (AuthorInterest, self ).post(request, *args, **kwargs)
def get_success_url
( self ):
return
reverse( 'author-detail' , kwargs={ 'pk' : self
.
object.pk})
Finally we bring this together in a new AuthorDetail view. We already know that calling
on a class-based view gives us something that behaves exactly like a function based view, so we can do that at the point we choose between the two subviews.
You can of course pass through keyword arguments to
in the same way you would in your URLconf, such as if you wanted the AuthorInterest behavior to also appear at another URL but using a different template.
from
django.views.generic
import
View
class AuthorDetail
(View):
def get
( self , request, *args, **kwargs): view = AuthorDisplay.as_view()
return
view(request, *args, **kwargs)
def post
( self , request, *args, **kwargs): view = AuthorInterest.as_view()
return
view(request, *args, **kwargs)
This approach can also be used with any other generic class-based views or your own class-based views inheriting directly from
or
TemplateView , as it keeps the different views as separate as possible.
More than just HTML
Where class based views shine is when you want to do the same thing many times. Suppose you’re writing an API, and every view should return JSON instead of rendered HTML.
We can create a mixin class to use in all of our views, handling the conversion to JSON once.
For example, a simple JSON mixin might look something like this:
272 Chapter 3. Using Django
Django Documentation, Release 1.6.12.dev20160216120443
import
json
from
django.http
import
HttpResponse
class JSONResponseMixin
( object ):
"""
A mixin that can be used to render a JSON response.
"""
def render_to_json_response
( self , context, **response_kwargs):
"""
Returns a JSON response, transforming 'context' to make the payload.
"""
return
HttpResponse( self
.
convert_context_to_json(context), content_type= 'application/json' ,
**response_kwargs
)
def convert_context_to_json
( self , context):
"Convert the context dictionary into a JSON object"
# Note: This is *EXTREMELY* naive; in reality, you'll need
# to do much more complex handling to ensure that arbitrary
# objects -- such as Django model instances or querysets
# -- can be serialized as JSON.
return
json.dumps(context)
Note: Check out the
documentation for more information on how to correctly transform
Django models and querysets into JSON.
This mixin provides a render_to_json_response() method with the same signature as
To use it, we simply need to mix it into a TemplateView for example, and override render_to_response() to call render_to_json_response() instead:
from
django.views.generic
import
TemplateView
class JSONView
(JSONResponseMixin, TemplateView):
def render_to_response
( self , context, **response_kwargs):
return
self
.
render_to_json_response(context, **response_kwargs)
Equally we could use our mixin with one of the generic views. We can make our own version of
by mixing JSONResponseMixin with the django.views.generic.detail.BaseDetailView – (the
before template rendering behavior has been mixed in):
from
django.views.generic.detail
import
BaseDetailView
class JSONDetailView
(JSONResponseMixin, BaseDetailView):
def render_to_response
( self , context, **response_kwargs):
return
self
.
render_to_json_response(context, **response_kwargs)
This view can then be deployed in the same way as any other
, with exactly the same behavior – except
for the format of the response.
If you want to be really adventurous, you could even mix a
subclass that is able to return both HTML and JSON content, depending on some property of the HTTP request, such as a query argument or a HTTP header.
Just mix in both the JSONResponseMixin and a
SingleObjectTemplateResponseMixin
the implementation of
to defer to the appropriate rendering method depending on the type of response that the user requested:
3.6. Class-based views 273
Django Documentation, Release 1.6.12.dev20160216120443
from
django.views.generic.detail
import
SingleObjectTemplateResponseMixin
class HybridDetailView
(JSONResponseMixin, SingleObjectTemplateResponseMixin, BaseDetailView):
def render_to_response
( self , context):
# Look for a 'format=json' GET argument
if
self
.
request.GET.get( 'format' ) == 'json' :
return
self
.
render_to_json_response(context)
else
:
return
super (HybridDetailView, self ).render_to_response(context)
Because of the way that Python resolves method overloading, the call to super(HybridDetailView, self).render_to_response(context) ends up calling the
implementation of
3.6.5 Basic examples
Django provides base view classes which will suit a wide range of applications.
All views inherit from the
class, which handles linking the view in to the URLs, HTTP method dispatching and other simple features.
is for a simple HTTP redirect, and
extends the base class to make it also render a template.
3.6.6 Simple usage in your URLconf
The simplest way to use generic views is to create them directly in your URLconf. If you’re only changing a few simple attributes on a class-based view, you can simply pass them into the
method call itself:
from
django.conf.urls
import
patterns
from
django.views.generic
import
TemplateView urlpatterns = patterns( '' ,
( r'^about/' , TemplateView.as_view(template_name= "about.html" )),
)
Any arguments passed to
will override attributes set on the class.
In this example, we set template_name on the TemplateView. A similar overriding pattern can be used for the url attribute on
3.6.7 Subclassing generic views
The second, more powerful way to use generic views is to inherit from an existing view and override attributes (such as the template_name) or methods (such as get_context_data) in your subclass to provide new values or methods. Consider, for example, a view that just displays one template, about.html. Django has a generic view to do this -
- so we can just subclass it, and override the template name:
# some_app/views.py
from
django.views.generic
import
TemplateView
class AboutView
(TemplateView): template_name = "about.html"
Then we just need to add this new view into our URLconf.
is a class, not a function, so we point the URL to the
class method instead, which provides a function-like entry to class-based views:
274 Chapter 3. Using Django
Shkarko
Reklamë