class-based-views.txt 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. =========================
  2. Class-based generic views
  3. =========================
  4. .. versionadded:: 1.3
  5. .. note::
  6. Prior to Django 1.3, generic views were implemented as functions. The
  7. function-based implementation has been deprecated in favor of the
  8. class-based approach described here.
  9. For the reference to the old on details on the old implementation,
  10. see the :doc:`topic guide </topics/generic-views>` and
  11. :doc:`detailed reference </topics/generic-views>`.
  12. Writing Web applications can be monotonous, because we repeat certain patterns
  13. again and again. Django tries to take away some of that monotony at the model
  14. and template layers, but Web developers also experience this boredom at the view
  15. level.
  16. Django's *generic views* were developed to ease that pain. They take certain
  17. common idioms and patterns found in view development and abstract them so that
  18. you can quickly write common views of data without having to write too much
  19. code.
  20. We can recognize certain common tasks, like displaying a list of objects, and
  21. write code that displays a list of *any* object. Then the model in question can
  22. be passed as an extra argument to the URLconf.
  23. Django ships with generic views to do the following:
  24. * Perform common "simple" tasks: redirect to a different page and
  25. render a given template.
  26. * Display list and detail pages for a single object. If we were creating an
  27. application to manage conferences then a ``TalkListView`` and a
  28. ``RegisteredUserListView`` would be examples of list views. A single
  29. talk page is an example of what we call a "detail" view.
  30. * Present date-based objects in year/month/day archive pages,
  31. associated detail, and "latest" pages. The Django Weblog's
  32. (http://www.djangoproject.com/weblog/) year, month, and
  33. day archives are built with these, as would be a typical
  34. newspaper's archives.
  35. * Allow users to create, update, and delete objects -- with or
  36. without authorization.
  37. Taken together, these views provide easy interfaces to perform the most common
  38. tasks developers encounter.
  39. Simple usage
  40. ============
  41. Class-based generic views (and indeed any class-based views that are
  42. based on the base classes Django provides) can be configured in two
  43. ways: subclassing, or passing in arguments directly in the URLconf.
  44. When you subclass a class-based view, you can override attributes
  45. (such as the template name, ``template_name``) or methods (such as
  46. ``get_context_data``) in your subclass to provide new values or
  47. methods. Consider, for example, a view that just displays one
  48. template, ``about.html``. Django has a generic view to do this -
  49. :class:`~django.views.generic.base.TemplateView` - so we can just
  50. subclass it, and override the template name::
  51. # some_app/views.py
  52. from django.views.generic import TemplateView
  53. class AboutView(TemplateView):
  54. template_name = "about.html"
  55. Then, we just need to add this new view into our URLconf. As the class-based
  56. views themselves are classes, we point the URL to the as_view class method
  57. instead, which is the entrypoint for class-based views::
  58. # urls.py
  59. from django.conf.urls.defaults import *
  60. from some_app.views import AboutView
  61. urlpatterns = patterns('',
  62. (r'^about/', AboutView.as_view()),
  63. )
  64. Alternatively, if you're only changing a few simple attributes on a
  65. class-based view, you can simply pass the new attributes into the as_view
  66. method call itself::
  67. from django.conf.urls.defaults import *
  68. from django.views.generic import TemplateView
  69. urlpatterns = patterns('',
  70. (r'^about/', TemplateView.as_view(template_name="about.html")),
  71. )
  72. A similar overriding pattern can be used for the ``url`` attribute on
  73. :class:`~django.views.generic.base.RedirectView`, another simple
  74. generic view.
  75. Generic views of objects
  76. ========================
  77. :class:`~django.views.generic.base.TemplateView` certainly is useful,
  78. but Django's generic views really shine when it comes to presenting
  79. views on your database content. Because it's such a common task,
  80. Django comes with a handful of built-in generic views that make
  81. generating list and detail views of objects incredibly easy.
  82. Let's take a look at one of these generic views: the "object list" view. We'll
  83. be using these models::
  84. # models.py
  85. from django.db import models
  86. class Publisher(models.Model):
  87. name = models.CharField(max_length=30)
  88. address = models.CharField(max_length=50)
  89. city = models.CharField(max_length=60)
  90. state_province = models.CharField(max_length=30)
  91. country = models.CharField(max_length=50)
  92. website = models.URLField()
  93. def __unicode__(self):
  94. return self.name
  95. class Meta:
  96. ordering = ["-name"]
  97. class Book(models.Model):
  98. title = models.CharField(max_length=100)
  99. authors = models.ManyToManyField('Author')
  100. publisher = models.ForeignKey(Publisher)
  101. publication_date = models.DateField()
  102. To build a list page of all publishers, we'd use a URLconf along these lines::
  103. from django.conf.urls.defaults import *
  104. from django.views.generic import ListView
  105. from books.models import Publisher
  106. urlpatterns = patterns('',
  107. (r'^publishers/$', ListView.as_view(
  108. model=Publisher,
  109. )),
  110. )
  111. That's all the Python code we need to write. We still need to write a template,
  112. however. We could explicitly tell the view which template to use
  113. by including a ``template_name`` key in the arguments to as_view, but in
  114. the absence of an explicit template Django will infer one from the object's
  115. name. In this case, the inferred template will be
  116. ``"books/publisher_list.html"`` -- the "books" part comes from the name of the
  117. app that defines the model, while the "publisher" bit is just the lowercased
  118. version of the model's name.
  119. .. highlightlang:: html+django
  120. This template will be rendered against a context containing a variable called
  121. ``object_list`` that contains all the publisher objects. A very simple template
  122. might look like the following::
  123. {% extends "base.html" %}
  124. {% block content %}
  125. <h2>Publishers</h2>
  126. <ul>
  127. {% for publisher in object_list %}
  128. <li>{{ publisher.name }}</li>
  129. {% endfor %}
  130. </ul>
  131. {% endblock %}
  132. That's really all there is to it. All the cool features of generic views come
  133. from changing the "info" dictionary passed to the generic view. The
  134. :doc:`generic views reference</ref/class-based-views>` documents all the generic
  135. views and all their options in detail; the rest of this document will consider
  136. some of the common ways you might customize and extend generic views.
  137. Extending generic views
  138. =======================
  139. .. highlightlang:: python
  140. There's no question that using generic views can speed up development
  141. substantially. In most projects, however, there comes a moment when the
  142. generic views no longer suffice. Indeed, the most common question asked by new
  143. Django developers is how to make generic views handle a wider array of
  144. situations.
  145. This is one of the reasons generic views were redesigned for the 1.3 release -
  146. previously, they were just view functions with a bewildering array of options;
  147. now, rather than passing in a large amount of configuration in the URLconf,
  148. the recommended way to extend generic views is to subclass them, and override
  149. their attributes or methods.
  150. Making "friendly" template contexts
  151. -----------------------------------
  152. You might have noticed that our sample publisher list template stores all the
  153. books in a variable named ``object_list``. While this works just fine, it isn't
  154. all that "friendly" to template authors: they have to "just know" that they're
  155. dealing with publishers here. A better name for that variable would be
  156. ``publisher_list``; that variable's content is pretty obvious.
  157. We can change the name of that variable easily with the ``context_object_name``
  158. attribute - here, we'll override it in the URLconf, since it's a simple change:
  159. .. parsed-literal::
  160. urlpatterns = patterns('',
  161. (r'^publishers/$', ListView.as_view(
  162. model=Publisher,
  163. **context_object_name = "publisher_list",**
  164. )),
  165. )
  166. Providing a useful ``context_object_name`` is always a good idea. Your
  167. coworkers who design templates will thank you.
  168. Adding extra context
  169. --------------------
  170. Often you simply need to present some extra information beyond that
  171. provided by the generic view. For example, think of showing a list of
  172. all the books on each publisher detail page. The
  173. :class:`~django.views.generic.detail.DetailView` generic view provides
  174. the publisher to the context, but it seems there's no way to get
  175. additional information in that template.
  176. However, there is; you can subclass
  177. :class:`~django.views.generic.detail.DetailView` and provide your own
  178. implementation of the ``get_context_data`` method. The default
  179. implementation of this that comes with
  180. :class:`~django.views.generic.detail.DetailView` simply adds in the
  181. object being displayed to the template, but we can override it to show
  182. more::
  183. from django.views.generic import DetailView
  184. from some_app.models import Publisher, Book
  185. class PublisherDetailView(DetailView):
  186. context_object_name = "publisher"
  187. model = Publisher
  188. def get_context_data(self, **kwargs):
  189. # Call the base implementation first to get a context
  190. context = DetailView.get_context_data(self, **kwargs)
  191. # Add in a QuerySet of all the books
  192. context['book_list'] = Book.objects.all()
  193. return context
  194. Viewing subsets of objects
  195. --------------------------
  196. Now let's take a closer look at the ``model`` argument we've been
  197. using all along. The ``model`` argument, which specifies the database
  198. model that the view will operate upon, is available on all the
  199. generic views that operate on a single object or a collection of
  200. objects. However, the ``model`` argument is not the only way to
  201. specify the objects that the view will operate upon -- you can also
  202. specify the list of objects using the ``queryset`` argument::
  203. from django.views.generic import DetailView
  204. from some_app.models import Publisher, Book
  205. class PublisherDetailView(DetailView):
  206. context_object_name = "publisher"
  207. queryset = Publisher.object.all()
  208. Specifying ``mode = Publisher`` is really just shorthand for saying
  209. ``queryset = Publisher.objects.all()``. However, by using ``queryset``
  210. to define a filtered list of objects you can be more specific about the
  211. objects that will be visible in the view (see :doc:`/topics/db/queries`
  212. for more information about ``QuerySet`` objects, and see the
  213. :doc:`generic views reference</ref/generic-views>` for the complete details).
  214. To pick a simple example, we might want to order a list of books by
  215. publication date, with the most recent first::
  216. urlpatterns = patterns('',
  217. (r'^publishers/$', ListView.as_view(
  218. queryset = Publisher.objects.all(),
  219. context_object_name = "publisher_list",
  220. )),
  221. (r'^publishers/$', ListView.as_view(
  222. queryset = Book.objects.order_by("-publication_date"),
  223. context_object_name = "book_list",
  224. )),
  225. )
  226. That's a pretty simple example, but it illustrates the idea nicely. Of course,
  227. you'll usually want to do more than just reorder objects. If you want to
  228. present a list of books by a particular publisher, you can use the same
  229. technique (here, illustrated using subclassing rather than by passing arguments
  230. in the URLconf)::
  231. from django.views.generic import ListView
  232. from some_app.models import Book
  233. class AcmeBookListView(ListView):
  234. context_object_name = "book_list"
  235. queryset = Book.objects.filter(publisher__name="Acme Publishing")
  236. template_name = "books/acme_list.html"
  237. Notice that along with a filtered ``queryset``, we're also using a custom
  238. template name. If we didn't, the generic view would use the same template as the
  239. "vanilla" object list, which might not be what we want.
  240. Also notice that this isn't a very elegant way of doing publisher-specific
  241. books. If we want to add another publisher page, we'd need another handful of
  242. lines in the URLconf, and more than a few publishers would get unreasonable.
  243. We'll deal with this problem in the next section.
  244. .. note::
  245. If you get a 404 when requesting ``/books/acme/``, check to ensure you
  246. actually have a Publisher with the name 'ACME Publishing'. Generic
  247. views have an ``allow_empty`` parameter for this case. See the
  248. :doc:`generic views reference</ref/class-based-views>` for more details.
  249. Dynamic filtering
  250. -----------------
  251. Another common need is to filter down the objects given in a list page by some
  252. key in the URL. Earlier we hard-coded the publisher's name in the URLconf, but
  253. what if we wanted to write a view that displayed all the books by some arbitrary
  254. publisher?
  255. Handily, the ListView has a ``get_queryset`` method we can override. Previously,
  256. it has just been returning the value of the ``queryset`` attribute, but now we
  257. can add more logic.
  258. The key part to making this work is that when class-based views are called,
  259. various useful things are stored on ``self``; as well as the request
  260. (``self.request``) this includes the positional (``self.args``) and name-based
  261. (``self.kwargs``) arguments captured according to the URLconf.
  262. Here, we have a URLconf with a single captured group::
  263. from some_app.views import PublisherBookListView
  264. urlpatterns = patterns('',
  265. (r'^books/(\w+)/$', PublisherBookListView.as_view()),
  266. )
  267. Next, we'll write the ``PublisherBookListView`` view itself::
  268. from django.shortcuts import get_object_or_404
  269. from django.views.generic import ListView
  270. from some_app.models import Book, Publisher
  271. class PublisherBookListView(ListView):
  272. context_object_name = "book_list"
  273. template_name = "books/books_by_publisher.html",
  274. def get_queryset(self):
  275. publisher = get_object_or_404(Publisher, name__iexact=self.args[0])
  276. return Book.objects.filter(publisher=publisher)
  277. As you can see, it's quite easy to add more logic to the queryset selection;
  278. if we wanted, we could use ``self.request.user`` to filter using the current
  279. user, or other more complex logic.
  280. We can also add the publisher into the context at the same time, so we can
  281. use it in the template::
  282. class PublisherBookListView(ListView):
  283. context_object_name = "book_list"
  284. template_name = "books/books_by_publisher.html",
  285. def get_queryset(self):
  286. self.publisher = get_object_or_404(Publisher, name__iexact=self.args[0])
  287. return Book.objects.filter(publisher=self.publisher)
  288. def get_context_data(self, **kwargs):
  289. # Call the base implementation first to get a context
  290. context = ListView.get_context_data(self, **kwargs)
  291. # Add in the publisher
  292. context['publisher'] = self.publisher
  293. return context
  294. Performing extra work
  295. ---------------------
  296. The last common pattern we'll look at involves doing some extra work before
  297. or after calling the generic view.
  298. Imagine we had a ``last_accessed`` field on our ``Author`` object that we were
  299. using to keep track of the last time anybody looked at that author::
  300. # models.py
  301. class Author(models.Model):
  302. salutation = models.CharField(max_length=10)
  303. first_name = models.CharField(max_length=30)
  304. last_name = models.CharField(max_length=40)
  305. email = models.EmailField()
  306. headshot = models.ImageField(upload_to='/tmp')
  307. last_accessed = models.DateTimeField()
  308. The generic ``DetailView`` class, of course, wouldn't know anything about this
  309. field, but once again we could easily write a custom view to keep that field
  310. updated.
  311. First, we'd need to add an author detail bit in the URLconf to point to a
  312. custom view:
  313. .. parsed-literal::
  314. from some_app.views import AuthorDetailView
  315. urlpatterns = patterns('',
  316. #...
  317. **(r'^authors/(?P<pk>\\d+)/$', AuthorDetailView.as_view()),**
  318. )
  319. Then we'd write our new view - ``get_object`` is the method that retrieves the
  320. object, so we simply override it and wrap the call::
  321. import datetime
  322. from some_app.models import Author
  323. from django.views.generic import DetailView
  324. from django.shortcuts import get_object_or_404
  325. class AuthorDetailView(DetailView):
  326. queryset = Author.objects.all()
  327. def get_object(self, **kwargs):
  328. # Call the superclass
  329. object = DetailView.get_object(self, **kwargs)
  330. # Record the lass accessed date
  331. object.last_accessed = datetime.datetime.now()
  332. object.save()
  333. # Return the object
  334. return object
  335. .. note::
  336. This code won't actually work unless you create a
  337. ``books/author_detail.html`` template.
  338. .. note::
  339. The URLconf here uses the named group ``pk`` - this name is the default
  340. name that DetailView uses to find the value of the primary key used to
  341. filter the queryset.
  342. If you want to change it, you'll need to do your own ``get()`` call
  343. on ``self.queryset`` using the new named parameter from ``self.kwargs``.
  344. More than just HTML
  345. -------------------
  346. So far, we've been focusing on rendering templates to generate
  347. responses. However, that's not all generic views can do.
  348. Each generic view is composed out of a series of mixins, and each
  349. mixin contributes a little piece of the entire view. Some of these
  350. mixins -- such as
  351. :class:`~django.views.generic.base.TemplateResponseMixin` -- are
  352. specifically designed for rendering content to a HTML response using a
  353. template. However, you can write your own mixins that perform
  354. different rendering behavior.
  355. For example, you a simple JSON mixin might look something like this::
  356. class JSONResponseMixin(object):
  357. def render_to_response(self, context):
  358. "Returns a JSON response containing 'context' as payload"
  359. return self.get_json_response(self.convert_context_to_json(context))
  360. def get_json_response(self, content, **httpresponse_kwargs):
  361. "Construct an `HttpResponse` object."
  362. return http.HttpResponse(content,
  363. content_type='application/json',
  364. **httpresponse_kwargs)
  365. def convert_context_to_json(self, context):
  366. "Convert the context dictionary into a JSON object"
  367. # Note: This is *EXTREMELY* naive; in reality, you'll need
  368. # to do much more complex handling to ensure that arbitrary
  369. # objects -- such as Django model instances or querysets
  370. # -- can be serialized as JSON.
  371. return json.dumps(content)
  372. Then, you could build a JSON-returning
  373. :class:`~django.views.generic.detail.DetailView` by mixing your
  374. :class:`JSONResponseMixin` with the
  375. :class:`~django.views.generic.detail.BaseDetailView` -- (the
  376. :class:`~django.views.generic.detail.DetailView` before template
  377. rendering behavior has been mixed in)::
  378. class JSONDetailView(JSONResponseMixin, BaseDetailView):
  379. pass
  380. This view can then be deployed in the same way as any other
  381. :class:`~django.views.generic.detail.DetailView`, with exactly the
  382. same behavior -- except for the format of the response.
  383. If you want to be really adventurous, you could even mix a
  384. :class:`~django.views.generic.detail.DetailView` subclass that is able
  385. to return *both* HTML and JSON content, depending on some property of
  386. the HTTP request, such as a query argument or a HTTP header. Just mix
  387. in both the :class:`JSONResponseMixin` and a
  388. :class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`,
  389. and override the implementation of :func:`render_to_response()` to defer
  390. to the appropriate subclass depending on the type of response that the user
  391. requested::
  392. class HybridDetailView(JSONResponseMixin, SingleObjectTemplateResponseMixin, BaseDetailView):
  393. def render_to_response(self, context):
  394. # Look for a 'format=json' GET argument
  395. if self.request.GET.get('format','html') == 'json':
  396. return JSONResponseMixin.render_to_response(self, context)
  397. else:
  398. return SingleObjectTemplateResponseMixin.render_to_response(self, context)
  399. Because of the way that Python resolves method overloading, the local
  400. :func:``render_to_response()`` implementation will override the
  401. versions provided by :class:`JSONResponseMixin` and
  402. :class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`.