Parcourir la source

Fixed #18451 -- Vastly improved class based view documentation.

Many thanks to Daniel Greenfeld, James Aylett, Marc Tamlyn, Simon Williams, Danilo Bargen and Luke Plant for their work on this.
Jannis Leidel il y a 12 ans
Parent
commit
c4c7fbcc0d
36 fichiers modifiés avec 3112 ajouts et 2016 suppressions
  1. 5 0
      AUTHORS
  2. 8 1
      django/views/generic/base.py
  3. 13 1
      django/views/generic/dates.py
  4. 14 2
      django/views/generic/detail.py
  5. 35 4
      django/views/generic/edit.py
  6. 9 0
      django/views/generic/list.py
  7. 1 1
      docs/glossary.txt
  8. 1 1
      docs/howto/static-files.txt
  9. 4 3
      docs/index.txt
  10. 2 2
      docs/internals/deprecation.txt
  11. 1 1
      docs/intro/tutorial04.txt
  12. 1 1
      docs/misc/api-stability.txt
  13. 0 1367
      docs/ref/class-based-views.txt
  14. 224 0
      docs/ref/class-based-views/base.txt
  15. 273 0
      docs/ref/class-based-views/generic-date-based.txt
  16. 86 0
      docs/ref/class-based-views/generic-display.txt
  17. 78 0
      docs/ref/class-based-views/generic-editing.txt
  18. 59 0
      docs/ref/class-based-views/index.txt
  19. 256 0
      docs/ref/class-based-views/mixins-date-based.txt
  20. 183 0
      docs/ref/class-based-views/mixins-editing.txt
  21. 175 0
      docs/ref/class-based-views/mixins-multiple-object.txt
  22. 60 0
      docs/ref/class-based-views/mixins-simple.txt
  23. 124 0
      docs/ref/class-based-views/mixins-single-object.txt
  24. 14 0
      docs/ref/class-based-views/mixins.txt
  25. 1 1
      docs/ref/index.txt
  26. 1 1
      docs/releases/1.3-alpha-1.txt
  27. 1 1
      docs/releases/1.3.txt
  28. 4 3
      docs/topics/auth.txt
  29. 0 624
      docs/topics/class-based-views.txt
  30. 432 0
      docs/topics/class-based-views/generic-display.txt
  31. 205 0
      docs/topics/class-based-views/generic-editing.txt
  32. 233 0
      docs/topics/class-based-views/index.txt
  33. 605 0
      docs/topics/class-based-views/mixins.txt
  34. 2 0
      docs/topics/forms/index.txt
  35. 1 1
      docs/topics/http/generic-views.txt
  36. 1 1
      docs/topics/index.txt

+ 5 - 0
AUTHORS

@@ -51,6 +51,7 @@ answer newbie questions, and generally made Django that much better:
     Antoni Aloy
     Daniel Alves Barbosa de Oliveira Vaz <danielvaz@gmail.com>
     AgarFu <heaven@croasanaso.sytes.net>
+    James Aylett
     Dagur Páll Ammendrup <dagurp@gmail.com>
     Collin Anderson <cmawebsite@gmail.com>
     Jeff Anderson <jefferya@programmerq.net>
@@ -85,6 +86,7 @@ answer newbie questions, and generally made Django that much better:
     Esdras Beleza <linux@esdrasbeleza.com>
     Chris Bennett <chrisrbennett@yahoo.com>
     James Bennett
+    Danilo Bargen
     Shai Berger <shai@platonix.com>
     Julian Bez
     Arvis Bickovskis <viestards.lists@gmail.com>
@@ -222,6 +224,7 @@ answer newbie questions, and generally made Django that much better:
     pradeep.gowda@gmail.com
     Collin Grady <collin@collingrady.com>
     Gabriel Grant <g@briel.ca>
+    Daniel Greenfeld
     Simon Greenhill <dev@simon.net.nz>
     Owen Griffiths
     Espen Grindhaug <http://grindhaug.org/>
@@ -507,6 +510,7 @@ answer newbie questions, and generally made Django that much better:
     Aaron Swartz <http://www.aaronsw.com/>
     Ville Säävuori <http://www.unessa.net/>
     Mart Sõmermaa <http://mrts.pri.ee/>
+    Marc Tamlyn
     Christian Tanzer <tanzer@swing.co.at>
     Tyler Tarabula <tyler.tarabula@gmail.com>
     Tyson Tate <tyson@fallingbullets.com>
@@ -555,6 +559,7 @@ answer newbie questions, and generally made Django that much better:
     Mike Wiacek <mjwiacek@google.com>
     Frank Wierzbicki
     charly.wilhelm@gmail.com
+    Simon Williams
     Derek Willis <http://blog.thescoop.org/>
     Rachel Willmer <http://www.willmer.com/kb/>
     Jakub Wilk <ubanus@users.sf.net>

+ 8 - 1
django/views/generic/base.py

@@ -90,6 +90,9 @@ class View(object):
         return http.HttpResponseNotAllowed(self._allowed_methods())
 
     def options(self, request, *args, **kwargs):
+        """
+        Handles responding to requests for the OPTIONS HTTP verb.
+        """
         response = http.HttpResponse()
         response['Allow'] = ', '.join(self._allowed_methods())
         response['Content-Length'] = 0
@@ -108,7 +111,11 @@ class TemplateResponseMixin(object):
 
     def render_to_response(self, context, **response_kwargs):
         """
-        Returns a response with a template rendered with the given context.
+        Returns a response, using the `response_class` for this
+        view, with a template rendered with the given context.
+
+        If any keyword arguments are provided, they will be
+        passed to the constructor of the response class.
         """
         return self.response_class(
             request = self.request,

+ 13 - 1
django/views/generic/dates.py

@@ -14,6 +14,9 @@ from django.views.generic.detail import BaseDetailView, SingleObjectTemplateResp
 from django.views.generic.list import MultipleObjectMixin, MultipleObjectTemplateResponseMixin
 
 class YearMixin(object):
+    """
+    Mixin for views manipulating year-based data.
+    """
     year_format = '%Y'
     year = None
 
@@ -67,6 +70,9 @@ class YearMixin(object):
 
 
 class MonthMixin(object):
+    """
+    Mixin for views manipulating month-based data.
+    """
     month_format = '%b'
     month = None
 
@@ -123,6 +129,9 @@ class MonthMixin(object):
 
 
 class DayMixin(object):
+    """
+    Mixin for views manipulating day-based data.
+    """
     day_format = '%d'
     day = None
 
@@ -176,6 +185,9 @@ class DayMixin(object):
 
 
 class WeekMixin(object):
+    """
+    Mixin for views manipulating week-based data.
+    """
     week_format = '%U'
     week = None
 
@@ -312,7 +324,7 @@ class DateMixin(object):
 
 class BaseDateListView(MultipleObjectMixin, DateMixin, View):
     """
-    Abstract base class for date-based views display a list of objects.
+    Abstract base class for date-based views displaying a list of objects.
     """
     allow_empty = False
 

+ 14 - 2
django/views/generic/detail.py

@@ -48,6 +48,7 @@ class SingleObjectMixin(ContextMixin):
                                  % self.__class__.__name__)
 
         try:
+            # Get the single item from the filtered queryset
             obj = queryset.get()
         except ObjectDoesNotExist:
             raise Http404(_("No %(verbose_name)s found matching the query") %
@@ -88,6 +89,9 @@ class SingleObjectMixin(ContextMixin):
             return None
 
     def get_context_data(self, **kwargs):
+        """
+        Insert the single object into the context dict.
+        """
         context = {}
         context_object_name = self.get_context_object_name(self.object)
         if context_object_name:
@@ -97,6 +101,9 @@ class SingleObjectMixin(ContextMixin):
 
 
 class BaseDetailView(SingleObjectMixin, View):
+    """
+    A base view for displaying a single object
+    """
     def get(self, request, *args, **kwargs):
         self.object = self.get_object()
         context = self.get_context_data(object=self.object)
@@ -109,8 +116,13 @@ class SingleObjectTemplateResponseMixin(TemplateResponseMixin):
 
     def get_template_names(self):
         """
-        Return a list of template names to be used for the request. Must return
-        a list. May not be called if render_to_response is overridden.
+        Return a list of template names to be used for the request. May not be
+        called if render_to_response is overridden. Returns the following list:
+
+        * the value of ``template_name`` on the view (if provided)
+        * the contents of the ``template_name_field`` field on the
+          object instance that the view is operating upon (if available)
+        * ``<app_label>/<object_name><template_name_suffix>.html``        
         """
         try:
             names = super(SingleObjectTemplateResponseMixin, self).get_template_names()

+ 35 - 4
django/views/generic/edit.py

@@ -46,6 +46,9 @@ class FormMixin(ContextMixin):
         return kwargs
 
     def get_success_url(self):
+        """
+        Returns the supplied success URL.
+        """
         if self.success_url:
             url = self.success_url
         else:
@@ -54,9 +57,16 @@ class FormMixin(ContextMixin):
         return url
 
     def form_valid(self, form):
+        """
+        If the form is valid, redirect to the supplied URL.
+        """
         return HttpResponseRedirect(self.get_success_url())
 
     def form_invalid(self, form):
+        """
+        If the form is invalid, re-render the context data with the
+        data-filled form and errors.
+        """
         return self.render_to_response(self.get_context_data(form=form))
 
 
@@ -67,7 +77,7 @@ class ModelFormMixin(FormMixin, SingleObjectMixin):
 
     def get_form_class(self):
         """
-        Returns the form class to use in this view
+        Returns the form class to use in this view.
         """
         if self.form_class:
             return self.form_class
@@ -94,6 +104,9 @@ class ModelFormMixin(FormMixin, SingleObjectMixin):
         return kwargs
 
     def get_success_url(self):
+        """
+        Returns the supplied URL.
+        """
         if self.success_url:
             url = self.success_url % self.object.__dict__
         else:
@@ -106,10 +119,17 @@ class ModelFormMixin(FormMixin, SingleObjectMixin):
         return url
 
     def form_valid(self, form):
+        """
+        If the form is valid, save the associated model.
+        """
         self.object = form.save()
         return super(ModelFormMixin, self).form_valid(form)
 
     def get_context_data(self, **kwargs):
+        """
+        If an object has been supplied, inject it into the context with the
+        supplied context_object_name name.
+        """
         context = {}
         if self.object:
             context['object'] = self.object
@@ -122,14 +142,21 @@ class ModelFormMixin(FormMixin, SingleObjectMixin):
 
 class ProcessFormView(View):
     """
-    A mixin that processes a form on POST.
+    A mixin that renders a form on GET and processes it on POST.
     """
     def get(self, request, *args, **kwargs):
+        """
+        Handles GET requests and instantiates a blank version of the form.
+        """
         form_class = self.get_form_class()
         form = self.get_form(form_class)
         return self.render_to_response(self.get_context_data(form=form))
 
     def post(self, request, *args, **kwargs):
+        """
+        Handles POST requests, instantiating a form instance with the passed
+        POST variables and then checked for validity.
+        """
         form_class = self.get_form_class()
         form = self.get_form(form_class)
         if form.is_valid():
@@ -172,7 +199,7 @@ class BaseCreateView(ModelFormMixin, ProcessFormView):
 
 class CreateView(SingleObjectTemplateResponseMixin, BaseCreateView):
     """
-    View for creating an new object instance,
+    View for creating a new object instance,
     with a response rendered by template.
     """
     template_name_suffix = '_form'
@@ -196,7 +223,7 @@ class BaseUpdateView(ModelFormMixin, ProcessFormView):
 class UpdateView(SingleObjectTemplateResponseMixin, BaseUpdateView):
     """
     View for updating an object,
-    with a response rendered by template..
+    with a response rendered by template.
     """
     template_name_suffix = '_form'
 
@@ -208,6 +235,10 @@ class DeletionMixin(object):
     success_url = None
 
     def delete(self, request, *args, **kwargs):
+        """
+        Calls the delete() method on the fetched object and then
+        redirects to the success URL.
+        """
         self.object = self.get_object()
         self.object.delete()
         return HttpResponseRedirect(self.get_success_url())

+ 9 - 0
django/views/generic/list.py

@@ -8,6 +8,9 @@ from django.views.generic.base import TemplateResponseMixin, ContextMixin, View
 
 
 class MultipleObjectMixin(ContextMixin):
+    """
+    A mixin for views manipulating multiple objects.
+    """
     allow_empty = True
     queryset = None
     model = None
@@ -111,6 +114,9 @@ class MultipleObjectMixin(ContextMixin):
 
 
 class BaseListView(MultipleObjectMixin, View):
+    """
+    A base view for displaying a list of objects.
+    """
     def get(self, request, *args, **kwargs):
         self.object_list = self.get_queryset()
         allow_empty = self.get_allow_empty()
@@ -132,6 +138,9 @@ class BaseListView(MultipleObjectMixin, View):
 
 
 class MultipleObjectTemplateResponseMixin(TemplateResponseMixin):
+    """
+    Mixin for responding with a template and list of objects.
+    """
     template_name_suffix = '_list'
 
     def get_template_names(self):

+ 1 - 1
docs/glossary.txt

@@ -16,7 +16,7 @@ Glossary
         A higher-order :term:`view` function that provides an abstract/generic
         implementation of a common idiom or pattern found in view development.
 
-        See :doc:`/ref/class-based-views`.
+        See :doc:`/topics/class-based-views/index`.
 
     model
         Models store your application's data.

+ 1 - 1
docs/howto/static-files.txt

@@ -175,7 +175,7 @@ using :class:`~django.template.RequestContext` when rendering the template.
 As a brief refresher, context processors add variables into the contexts of
 every template. However, context processors require that you use
 :class:`~django.template.RequestContext` when rendering templates. This happens
-automatically if you're using a :doc:`generic view </ref/class-based-views>`,
+automatically if you're using a :doc:`generic view </ref/class-based-views/index>`,
 but in views written by hand you'll need to explicitly use ``RequestContext``
 To see how that works, and to read more details, check out
 :ref:`subclassing-context-requestcontext`.

+ 4 - 3
docs/index.txt

@@ -105,9 +105,10 @@ The view layer
   :doc:`Managing files <topics/files>` |
   :doc:`Custom storage <howto/custom-file-storage>`
 
-* **Generic views:**
-  :doc:`Overview<topics/class-based-views>` |
-  :doc:`Built-in generic views<ref/class-based-views>`
+* **Class-based views:**
+  :doc:`Overview<topics/class-based-views/index>` |
+  :doc:`Built-in class-based views<ref/class-based-views/index>` |
+  :doc:`Built-in view mixins<ref/class-based-views/mixins>`
 
 * **Advanced:**
   :doc:`Generating CSV <howto/outputting-csv>` |

+ 2 - 2
docs/internals/deprecation.txt

@@ -134,7 +134,7 @@ these changes.
 
 * The function-based generic view modules will be removed in favor of their
   class-based equivalents, outlined :doc:`here
-  </topics/class-based-views>`:
+  </topics/class-based-views/index>`:
 
 * The :class:`~django.core.servers.basehttp.AdminMediaHandler` will be
   removed.  In its place use
@@ -221,7 +221,7 @@ these changes.
   the 1.4 release. They will be removed.
 
 * The :doc:`form wizard </ref/contrib/formtools/form-wizard>` has been
-  refactored to use class based views with pluggable backends in 1.4.
+  refactored to use class-based views with pluggable backends in 1.4.
   The previous implementation will be removed.
 
 * Legacy ways of calling

+ 1 - 1
docs/intro/tutorial04.txt

@@ -320,7 +320,7 @@ function anymore -- generic views can be (and are) used multiple times
 Run the server, and use your new polling app based on generic views.
 
 For full details on generic views, see the :doc:`generic views documentation
-</topics/class-based-views>`.
+</topics/class-based-views/index>`.
 
 Coming soon
 ===========

+ 1 - 1
docs/misc/api-stability.txt

@@ -54,7 +54,7 @@ of 1.0. This includes these APIs:
 - :doc:`HTTP request/response handling </topics/http/index>`, including file
   uploads, middleware, sessions, URL resolution, view, and shortcut APIs.
 
-- :doc:`Generic views </topics/class-based-views>`.
+- :doc:`Generic views </topics/class-based-views/index>`.
 
 - :doc:`Internationalization </topics/i18n/index>`.
 

+ 0 - 1367
docs/ref/class-based-views.txt

@@ -1,1367 +0,0 @@
-=========================
-Class-based generic views
-=========================
-
-.. versionadded:: 1.3
-
-.. note::
-    Prior to Django 1.3, generic views were implemented as functions. The
-    function-based implementation has been removed in favor of the
-    class-based approach described here.
-
-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.
-
-A general introduction to class-based generic views can be found in the
-:doc:`topic guide </topics/class-based-views>`.
-
-This reference contains details of Django's built-in generic views, along with
-a list of the keyword arguments that each generic view expects. Remember that
-arguments may either come from the URL pattern or from the ``extra_context``
-additional-information dictionary.
-
-Most generic views require the ``queryset`` key, which is a ``QuerySet``
-instance; see :doc:`/topics/db/queries` for more information about ``QuerySet``
-objects.
-
-Mixins
-======
-
-A mixin class is a way of using the inheritance capabilities of
-classes to compose a class out of smaller pieces of behavior. Django's
-class-based generic views are constructed by composing mixins into
-usable generic views.
-
-For example, the :class:`~django.views.generic.base.detail.DetailView`
-is composed from:
-
-* :class:`~django.db.views.generic.base.View`, which provides the
-  basic class-based behavior
-* :class:`~django.db.views.generic.detail.SingleObjectMixin`, which
-  provides the utilities for retrieving and displaying a single object
-* :class:`~django.db.views.generic.detail.SingleObjectTemplateResponseMixin`,
-  which provides the tools for rendering a single object into a
-  template-based response.
-
-When combined, these mixins provide all the pieces necessary to
-provide a view over a single object that renders a template to produce
-a response.
-
-Django provides a range of mixins. If you want to write your own
-generic views, you can build classes that compose these mixins in
-interesting ways. Alternatively, you can just use the pre-mixed
-`Generic views`_ that Django provides.
-
-.. note::
-
-    When the documentation for a view gives the list of mixins, that view
-    inherits all the properties and methods of that mixin.
-
-Simple mixins
--------------
-
-.. currentmodule:: django.views.generic.base
-
-TemplateResponseMixin
-~~~~~~~~~~~~~~~~~~~~~
-.. class:: TemplateResponseMixin()
-
-    .. attribute:: template_name
-
-        The path to the template to use when rendering the view.
-
-    .. attribute:: response_class
-
-        The response class to be returned by ``render_to_response`` method.
-        Default is
-        :class:`TemplateResponse <django.template.response.TemplateResponse>`.
-        The template and context of ``TemplateResponse`` instances can be
-        altered later (e.g. in
-        :ref:`template response middleware <template-response-middleware>`).
-
-        If you need custom template loading or custom context object
-        instantiation, create a ``TemplateResponse`` subclass and assign it to
-        ``response_class``.
-
-    .. method:: render_to_response(context, **response_kwargs)
-
-        Returns a ``self.response_class`` instance.
-
-        If any keyword arguments are provided, they will be
-        passed to the constructor of the response class.
-
-        Calls :meth:`~TemplateResponseMixin.get_template_names()` to obtain the
-        list of template names that will be searched looking for an existent
-        template.
-
-    .. method:: get_template_names()
-
-        Returns a list of template names to search for when rendering the
-        template.
-
-        If :attr:`TemplateResponseMixin.template_name` is specified, the
-        default implementation will return a list containing
-        :attr:`TemplateResponseMixin.template_name` (if it is specified).
-
-
-Single object mixins
---------------------
-
-.. currentmodule:: django.views.generic.detail
-
-SingleObjectMixin
-~~~~~~~~~~~~~~~~~
-.. class:: SingleObjectMixin()
-
-    .. attribute:: model
-
-        The model that this view will display data for. Specifying ``model
-        = Foo`` is effectively the same as specifying ``queryset =
-        Foo.objects.all()``.
-
-    .. attribute:: queryset
-
-        A ``QuerySet`` that represents the objects. If provided, the value of
-        :attr:`SingleObjectMixin.queryset` supersedes the value provided for
-        :attr:`SingleObjectMixin.model`.
-
-    .. attribute:: slug_field
-
-        The name of the field on the model that contains the slug. By default,
-        ``slug_field`` is ``'slug'``.
-
-    .. attribute:: slug_url_kwarg
-
-        .. versionadded:: 1.4
-
-        The name of the URLConf keyword argument that contains the slug. By
-        default, ``slug_url_kwarg`` is ``'slug'``.
-
-    .. attribute:: pk_url_kwarg
-
-        .. versionadded:: 1.4
-
-        The name of the URLConf keyword argument that contains the primary key.
-        By default, ``pk_url_kwarg`` is ``'pk'``.
-
-    .. attribute:: context_object_name
-
-        Designates the name of the variable to use in the context.
-
-    .. method:: get_object(queryset=None)
-
-        Returns the single object that this view will display. If
-        ``queryset`` is provided, that queryset will be used as the
-        source of objects; otherwise,
-        :meth:`~SingleObjectMixin.get_queryset` will be used.
-        ``get_object()`` looks for a
-        :attr:`SingleObjectMixin.pk_url_kwarg` argument in the arguments
-        to the view; if this argument is found, this method performs a
-        primary-key based lookup using that value. If this argument is not
-        found, it looks for a :attr:`SingleObjectMixin.slug_url_kwarg`
-        argument, and performs a slug lookup using the
-        :attr:`SingleObjectMixin.slug_field`.
-
-    .. method:: get_queryset()
-
-        Returns the queryset that will be used to retrieve the object that
-        this view will display. By default,
-        :meth:`~SingleObjectMixin.get_queryset` returns the value of the
-        :attr:`~SingleObjectMixin.queryset` attribute if it is set, otherwise
-        it constructs a :class:`QuerySet` by calling the `all()` method on the
-        :attr:`~SingleObjectMixin.model` attribute's default manager.
-
-    .. method:: get_context_object_name(obj)
-
-        Return the context variable name that will be used to contain the
-        data that this view is manipulating. If
-        :attr:`~SingleObjectMixin.context_object_name` is not set, the context
-        name will be constructed from the ``object_name`` of the model that
-        the queryset is composed from. For example, the model ``Article``
-        would have context object named ``'article'``.
-
-    .. method:: get_context_data(**kwargs)
-
-        Returns context data for displaying the list of objects.
-
-    **Context**
-
-    * ``object``: The object that this view is displaying. If
-      ``context_object_name`` is specified, that variable will also be
-      set in the context, with the same value as ``object``.
-
-SingleObjectTemplateResponseMixin
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. class:: SingleObjectTemplateResponseMixin()
-
-    A mixin class that performs template-based response rendering for views
-    that operate upon a single object instance. Requires that the view it is
-    mixed with provides ``self.object``, the object instance that the view is
-    operating on. ``self.object`` will usually be, but is not required to be,
-    an instance of a Django model. It may be ``None`` if the view is in the
-    process of constructing a new instance.
-
-    **Extends**
-
-    * :class:`~django.views.generic.base.TemplateResponseMixin`
-
-    .. attribute:: template_name_field
-
-        The field on the current object instance that can be used to determine
-        the name of a candidate template. If either ``template_name_field`` or
-        the value of the ``template_name_field`` on the current object instance
-        is ``None``, the object will not be interrogated for a candidate
-        template name.
-
-    .. attribute:: template_name_suffix
-
-        The suffix to append to the auto-generated candidate template name.
-        Default suffix is ``_detail``.
-
-    .. method:: get_template_names()
-
-        Returns a list of candidate template names. Returns the following list:
-
-        * the value of ``template_name`` on the view (if provided)
-        * the contents of the ``template_name_field`` field on the
-          object instance that the view is operating upon (if available)
-        * ``<app_label>/<object_name><template_name_suffix>.html``
-
-Multiple object mixins
-----------------------
-
-.. currentmodule:: django.views.generic.list
-
-MultipleObjectMixin
-~~~~~~~~~~~~~~~~~~~
-.. class:: MultipleObjectMixin()
-
-    A mixin that can be used to display a list of objects.
-
-    If ``paginate_by`` is specified, Django will paginate the results returned
-    by this. You can specify the page number in the URL in one of two ways:
-
-    * Use the ``page`` parameter in the URLconf. For example, this is what
-      your URLconf might look like::
-
-        (r'^objects/page(?P<page>[0-9]+)/$', PaginatedView.as_view())
-
-    * Pass the page number via the ``page`` query-string parameter. For
-      example, a URL would look like this::
-
-        /objects/?page=3
-
-    These values and lists are 1-based, not 0-based, so the first page would be
-    represented as page ``1``.
-
-    For more on pagination, read the :doc:`pagination documentation
-    </topics/pagination>`.
-
-    As a special case, you are also permitted to use ``last`` as a value for
-    ``page``::
-
-        /objects/?page=last
-
-    This allows you to access the final page of results without first having to
-    determine how many pages there are.
-
-    Note that ``page`` *must* be either a valid page number or the value
-    ``last``; any other value for ``page`` will result in a 404 error.
-
-    .. attribute:: allow_empty
-
-        A boolean specifying whether to display the page if no objects are
-        available. If this is ``False`` and no objects are available, the view
-        will raise a 404 instead of displaying an empty page. By default, this
-        is ``True``.
-
-    .. attribute:: model
-
-        The model that this view will display data for. Specifying ``model
-        = Foo`` is effectively the same as specifying ``queryset =
-        Foo.objects.all()``.
-
-    .. attribute:: queryset
-
-        A ``QuerySet`` that represents the objects. If provided, the value of
-        :attr:`MultipleObjectMixin.queryset` supersedes the value provided for
-        :attr:`MultipleObjectMixin.model`.
-
-    .. attribute:: paginate_by
-
-        An integer specifying how many objects should be displayed per page. If
-        this is given, the view will paginate objects with
-        :attr:`MultipleObjectMixin.paginate_by` objects per page. The view will
-        expect either a ``page`` query string parameter (via ``GET``) or a
-        ``page`` variable specified in the URLconf.
-
-    .. attribute:: paginator_class
-
-       The paginator class to be used for pagination. By default,
-       :class:`django.core.paginator.Paginator` is used. If the custom paginator
-       class doesn't have the same constructor interface as
-       :class:`django.core.paginator.Paginator`, you will also need to
-       provide an implementation for :meth:`MultipleObjectMixin.get_paginator`.
-
-    .. attribute:: context_object_name
-
-        Designates the name of the variable to use in the context.
-
-    .. method:: get_queryset()
-
-        Returns the queryset that represents the data this view will display.
-
-    .. method:: paginate_queryset(queryset, page_size)
-
-        Returns a 4-tuple containing (``paginator``, ``page``, ``object_list``,
-        ``is_paginated``).
-
-        Constructed by paginating ``queryset`` into pages of size ``page_size``.
-        If the request contains a ``page`` argument, either as a captured URL
-        argument or as a GET argument, ``object_list`` will correspond to the
-        objects from that page.
-
-    .. method:: get_paginate_by(queryset)
-
-        Returns the number of items to paginate by, or ``None`` for no
-        pagination. By default this simply returns the value of
-        :attr:`MultipleObjectMixin.paginate_by`.
-
-    .. method:: get_paginator(queryset, per_page, orphans=0, allow_empty_first_page=True)
-
-        Returns an instance of the paginator to use for this view. By default,
-        instantiates an instance of :attr:`paginator_class`.
-
-    .. method:: get_allow_empty()
-
-        Return a boolean specifying whether to display the page if no objects
-        are available. If this method returns ``False`` and no objects are
-        available, the view will raise a 404 instead of displaying an empty
-        page. By default, this is ``True``.
-
-    .. method:: get_context_object_name(object_list)
-
-        Return the context variable name that will be used to contain
-        the list of data that this view is manipulating. If
-        ``object_list`` is a queryset of Django objects and
-        :attr:`~MultipleObjectMixin.context_object_name` is not set,
-        the context name will be the ``object_name`` of the model that
-        the queryset is composed from, with postfix ``'_list'``
-        appended. For example, the model ``Article`` would have a
-        context object named ``article_list``.
-
-    .. method:: get_context_data(**kwargs)
-
-        Returns context data for displaying the list of objects.
-
-    **Context**
-
-    * ``object_list``: The list of objects that this view is displaying. If
-      ``context_object_name`` is specified, that variable will also be set
-      in the context, with the same value as ``object_list``.
-
-    * ``is_paginated``: A boolean representing whether the results are
-      paginated. Specifically, this is set to ``False`` if no page size has
-      been specified, or if the available objects do not span multiple
-      pages.
-
-    * ``paginator``: An instance of
-      :class:`django.core.paginator.Paginator`. If the page is not
-      paginated, this context variable will be ``None``.
-
-    * ``page_obj``: An instance of
-      :class:`django.core.paginator.Page`. If the page is not paginated,
-      this context variable will be ``None``.
-
-MultipleObjectTemplateResponseMixin
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.. class:: MultipleObjectTemplateResponseMixin()
-
-    A mixin class that performs template-based response rendering for views
-    that operate upon a list of object instances. Requires that the view it is
-    mixed with provides ``self.object_list``, the list of object instances that
-    the view is operating on. ``self.object_list`` may be, but is not required
-    to be, a :class:`~django.db.models.query.QuerySet`.
-
-    **Extends**
-
-    * :class:`~django.views.generic.base.TemplateResponseMixin`
-
-    .. attribute:: template_name_suffix
-
-        The suffix to append to the auto-generated candidate template name.
-        Default suffix is ``_list``.
-
-    .. method:: get_template_names()
-
-        Returns a list of candidate template names. Returns the following list:
-
-        * the value of ``template_name`` on the view (if provided)
-        * ``<app_label>/<object_name><template_name_suffix>.html``
-
-Editing mixins
---------------
-
-.. currentmodule:: django.views.generic.edit
-
-FormMixin
-~~~~~~~~~
-.. class:: FormMixin()
-
-    A mixin class that provides facilities for creating and displaying forms.
-
-    .. attribute:: initial
-
-        A dictionary containing initial data for the form.
-
-    .. attribute:: form_class
-
-        The form class to instantiate.
-
-    .. attribute:: success_url
-
-        The URL to redirect to when the form is successfully processed.
-
-    .. method:: get_initial()
-
-        Retrieve initial data for the form. By default, returns a copy of
-        :attr:`.initial`.
-
-    .. admonition:: Changed in 1.4
-
-        In Django 1.3, this method was returning the :attr:`initial` class
-        variable itself.
-
-    .. method:: get_form_class()
-
-        Retrieve the form class to instantiate. By default
-        :attr:`.form_class`.
-
-    .. method:: get_form(form_class)
-
-        Instantiate an instance of ``form_class`` using
-        :meth:`.get_form_kwargs`.
-
-    .. method:: get_form_kwargs()
-
-        Build the keyword arguments required to instantiate the form.
-
-        The ``initial`` argument is set to :meth:`.get_initial`. If the
-        request is a ``POST`` or ``PUT``, the request data (``request.POST``
-        and ``request.FILES``) will also be provided.
-
-    .. method:: get_success_url()
-
-        Determine the URL to redirect to when the form is successfully
-        validated. Returns :attr:`.success_url` by default.
-
-    .. method:: form_valid(form)
-
-        Redirects to :meth:`.get_success_url`.
-
-    .. method:: form_invalid(form)
-
-        Renders a response, providing the invalid form as context.
-
-    .. method:: get_context_data(**kwargs)
-
-        Populates a context containing the contents of ``kwargs``.
-
-    **Context**
-
-    * ``form``: The form instance that was generated for the view.
-
-    .. note::
-
-        Views mixing :class:`FormMixin` must
-        provide an implementation of :meth:`.form_valid` and
-        :meth:`.form_invalid`.
-
-ModelFormMixin
-~~~~~~~~~~~~~~
-.. class:: ModelFormMixin()
-
-    A form mixin that works on ModelForms, rather than a standalone form.
-
-    Since this is a subclass of
-    :class:`~django.views.generic.detail.SingleObjectMixin`, instances of this
-    mixin have access to the :attr:`~SingleObjectMixin.model` and
-    :attr:`~SingleObjectMixin.queryset` attributes, describing the type of
-    object that the ModelForm is manipulating. The view also provides
-    ``self.object``, the instance being manipulated. If the instance is being
-    created, ``self.object`` will be ``None``.
-
-    **Mixins**
-
-    * :class:`django.views.generic.edit.FormMixin`
-    * :class:`django.views.generic.detail.SingleObjectMixin`
-
-    .. attribute:: success_url
-
-        The URL to redirect to when the form is successfully processed.
-
-        ``success_url`` may contain dictionary string formatting, which
-        will be interpolated against the object's field attributes. For
-        example, you could use ``success_url="/polls/%(slug)s/"`` to
-        redirect to a URL composed out of the ``slug`` field on a model.
-
-    .. method:: get_form_class()
-
-        Retrieve the form class to instantiate. If
-        :attr:`FormMixin.form_class` is provided, that class will be used.
-        Otherwise, a ModelForm will be instantiated using the model associated
-        with the :attr:`~SingleObjectMixin.queryset`, or with the
-        :attr:`~SingleObjectMixin.model`, depending on which attribute is
-        provided.
-
-    .. method:: get_form_kwargs()
-
-        Add the current instance (``self.object``) to the standard
-        :meth:`FormMixin.get_form_kwargs`.
-
-    .. method:: get_success_url()
-
-        Determine the URL to redirect to when the form is successfully
-        validated. Returns :attr:`FormMixin.success_url` if it is provided;
-        otherwise, attempts to use the ``get_absolute_url()`` of the object.
-
-    .. method:: form_valid(form)
-
-        Saves the form instance, sets the current object for the view, and
-        redirects to :meth:`.get_success_url`.
-
-    .. method:: form_invalid()
-
-        Renders a response, providing the invalid form as context.
-
-ProcessFormView
-~~~~~~~~~~~~~~~
-.. class:: ProcessFormView()
-
-    A mixin that provides basic HTTP GET and POST workflow.
-
-    .. method:: get(request, *args, **kwargs)
-
-        Constructs a form, then renders a response using a context that
-        contains that form.
-
-    .. method:: post(request, *args, **kwargs)
-
-        Constructs a form, checks the form for validity, and handles it
-        accordingly.
-
-    The PUT action is also handled, as an analog of POST.
-
-DeletionMixin
-~~~~~~~~~~~~~
-.. class:: DeletionMixin()
-
-    Enables handling of the ``DELETE`` http action.
-
-    .. attribute:: success_url
-
-        The url to redirect to when the nominated object has been
-        successfully deleted.
-
-    .. method:: get_success_url(obj)
-
-        Returns the url to redirect to when the nominated object has been
-        successfully deleted. Returns
-        :attr:`~django.views.generic.edit.DeletionMixin.success_url` by
-        default.
-
-Date-based mixins
------------------
-
-.. currentmodule:: django.views.generic.dates
-
-YearMixin
-~~~~~~~~~
-.. class:: YearMixin()
-
-    A mixin that can be used to retrieve and provide parsing information for a
-    year component of a date.
-
-    .. attribute:: year_format
-
-        The :func:`~time.strftime` format to use when parsing the year.
-        By default, this is ``'%Y'``.
-
-    .. attribute:: year
-
-        **Optional** The value for the year (as a string). By default, set to
-        ``None``, which means the year will be determined using other means.
-
-    .. method:: get_year_format()
-
-        Returns the :func:`~time.strftime` format to use when parsing the year. Returns
-        :attr:`YearMixin.year_format` by default.
-
-    .. method:: get_year()
-
-        Returns the year for which this view will display data. Tries the
-        following sources, in order:
-
-        * The value of the :attr:`YearMixin.year` attribute.
-        * The value of the `year` argument captured in the URL pattern
-        * The value of the `year` GET query argument.
-
-        Raises a 404 if no valid year specification can be found.
-
-MonthMixin
-~~~~~~~~~~
-.. class:: MonthMixin()
-
-    A mixin that can be used to retrieve and provide parsing information for a
-    month component of a date.
-
-    .. attribute:: month_format
-
-        The :func:`~time.strftime` format to use when parsing the month. By default, this is
-        ``'%b'``.
-
-    .. attribute:: month
-
-        **Optional** The value for the month (as a string). By default, set to
-        ``None``, which means the month will be determined using other means.
-
-    .. method:: get_month_format()
-
-        Returns the :func:`~time.strftime` format to use when parsing the month. Returns
-        :attr:`MonthMixin.month_format` by default.
-
-    .. method:: get_month()
-
-        Returns the month for which this view will display data. Tries the
-        following sources, in order:
-
-        * The value of the :attr:`MonthMixin.month` attribute.
-        * The value of the `month` argument captured in the URL pattern
-        * The value of the `month` GET query argument.
-
-        Raises a 404 if no valid month specification can be found.
-
-    .. method:: get_next_month(date)
-
-        Returns a date object containing the first day of the month after the
-        date provided. Returns ``None`` if mixed with a view that sets
-        ``allow_future = False``, and the next month is in the future. If
-        ``allow_empty = False``, returns the next month that contains data.
-
-    .. method:: get_prev_month(date)
-
-        Returns a date object containing the first day of the month before the
-        date provided. If ``allow_empty = False``, returns the previous month
-        that contained data.
-
-DayMixin
-~~~~~~~~~
-.. class:: DayMixin()
-
-    A mixin that can be used to retrieve and provide parsing information for a
-    day component of a date.
-
-    .. attribute:: day_format
-
-        The :func:`~time.strftime` format to use when parsing the day. By default, this is
-        ``'%d'``.
-
-    .. attribute:: day
-
-        **Optional** The value for the day (as a string). By default, set to
-        ``None``, which means the day will be determined using other means.
-
-    .. method:: get_day_format()
-
-        Returns the :func:`~time.strftime` format to use when parsing the day. Returns
-        :attr:`DayMixin.day_format` by default.
-
-    .. method:: get_day()
-
-        Returns the day for which this view will display data. Tries the
-        following sources, in order:
-
-        * The value of the :attr:`DayMixin.day` attribute.
-        * The value of the `day` argument captured in the URL pattern
-        * The value of the `day` GET query argument.
-
-        Raises a 404 if no valid day specification can be found.
-
-    .. method:: get_next_day(date)
-
-        Returns a date object containing the next day after the date provided.
-        Returns ``None`` if mixed with a view that sets ``allow_future = False``,
-        and the next day is in the future. If ``allow_empty = False``, returns
-        the next day that contains data.
-
-    .. method:: get_prev_day(date)
-
-        Returns a date object containing the previous day. If
-        ``allow_empty = False``, returns the previous day that contained data.
-
-WeekMixin
-~~~~~~~~~
-.. class:: WeekMixin()
-
-    A mixin that can be used to retrieve and provide parsing information for a
-    week component of a date.
-
-    .. attribute:: week_format
-
-        The :func:`~time.strftime` format to use when parsing the week. By default, this is
-        ``'%U'``.
-
-    .. attribute:: week
-
-        **Optional** The value for the week (as a string). By default, set to
-        ``None``, which means the week will be determined using other means.
-
-    .. method:: get_week_format()
-
-        Returns the :func:`~time.strftime` format to use when parsing the week. Returns
-        :attr:`WeekMixin.week_format` by default.
-
-    .. method:: get_week()
-
-        Returns the week for which this view will display data. Tries the
-        following sources, in order:
-
-        * The value of the :attr:`WeekMixin.week` attribute.
-        * The value of the `week` argument captured in the URL pattern
-        * The value of the `week` GET query argument.
-
-        Raises a 404 if no valid week specification can be found.
-
-
-DateMixin
-~~~~~~~~~
-.. class:: DateMixin()
-
-    A mixin class providing common behavior for all date-based views.
-
-    .. attribute:: date_field
-
-        The name of the ``DateField`` or ``DateTimeField`` in the
-        ``QuerySet``'s model that the date-based archive should use to
-        determine the objects on the page.
-
-        When :doc:`time zone support </topics/i18n/timezones>` is enabled and
-        ``date_field`` is a ``DateTimeField``, dates are assumed to be in the
-        current time zone. Otherwise, the queryset could include objects from
-        the previous or the next day in the end user's time zone.
-
-        .. warning::
-
-            In this situation, if you have implemented per-user time zone
-            selection, the same URL may show a different set of objects,
-            depending on the end user's time zone. To avoid this, you should
-            use a ``DateField`` as the ``date_field`` attribute.
-
-    .. attribute:: allow_future
-
-        A boolean specifying whether to include "future" objects on this page,
-        where "future" means objects in which the field specified in
-        ``date_field`` is greater than the current date/time. By default, this
-        is ``False``.
-
-    .. method:: get_date_field()
-
-        Returns the name of the field that contains the date data that this
-        view will operate on. Returns :attr:`DateMixin.date_field` by default.
-
-    .. method:: get_allow_future()
-
-        Determine whether to include "future" objects on this page, where
-        "future" means objects in which the field specified in ``date_field``
-        is greater than the current date/time. Returns
-        :attr:`DateMixin.allow_future` by default.
-
-BaseDateListView
-~~~~~~~~~~~~~~~~
-.. class:: BaseDateListView()
-
-    A base class that provides common behavior for all date-based views. There
-    won't normally be a reason to instantiate
-    :class:`~django.views.generic.dates.BaseDateListView`; instantiate one of
-    the subclasses instead.
-
-    While this view (and it's subclasses) are executing, ``self.object_list``
-    will contain the list of objects that the view is operating upon, and
-    ``self.date_list`` will contain the list of dates for which data is
-    available.
-
-    **Mixins**
-
-    * :class:`~django.views.generic.dates.DateMixin`
-    * :class:`~django.views.generic.list.MultipleObjectMixin`
-
-    .. attribute:: allow_empty
-
-        A boolean specifying whether to display the page if no objects are
-        available. If this is ``True`` and no objects are available, the view
-        will display an empty page instead of raising a 404. By default, this
-        is ``False``.
-
-    .. method:: get_dated_items():
-
-        Returns a 3-tuple containing (``date_list``, ``object_list``,
-        ``extra_context``).
-
-        ``date_list`` is the list of dates for which data is available.
-        ``object_list`` is the list of objects. ``extra_context`` is a
-        dictionary of context data that will be added to any context data
-        provided by the
-        :class:`~django.views.generic.list.MultipleObjectMixin`.
-
-    .. method:: get_dated_queryset(**lookup)
-
-        Returns a queryset, filtered using the query arguments defined by
-        ``lookup``. Enforces any restrictions on the queryset, such as
-        ``allow_empty`` and ``allow_future``.
-
-    .. method:: get_date_list(queryset, date_type)
-
-        Returns the list of dates of type ``date_type`` for which
-        ``queryset`` contains entries. For example, ``get_date_list(qs,
-        'year')`` will return the list of years for which ``qs`` has entries.
-        See :meth:`~django.db.models.query.QuerySet.dates()` for the
-        ways that the ``date_type`` argument can be used.
-
-
-Generic views
-=============
-
-Simple generic views
---------------------
-
-.. currentmodule:: django.views.generic.base
-
-View
-~~~~
-.. class:: View()
-
-    The master class-based base view. All other generic class-based views
-    inherit from this base class.
-
-    Each request served by a :class:`~django.views.generic.base.View` has an
-    independent state; therefore, it is safe to store state variables on the
-    instance (i.e., ``self.foo = 3`` is a thread-safe operation).
-
-    A class-based view is deployed into a URL pattern using the
-    :meth:`~View.as_view()` classmethod::
-
-        urlpatterns = patterns('',
-                (r'^view/$', MyView.as_view(size=42)),
-            )
-
-    Any argument passed into :meth:`~View.as_view()` will be assigned onto the
-    instance that is used to service a request. Using the previous example,
-    this means that every request on ``MyView`` is able to interrogate
-    ``self.size``.
-
-    .. admonition:: Thread safety with view arguments
-
-        Arguments passed to a view are shared between every instance of a view.
-        This means that you shoudn't use a list, dictionary, or any other
-        variable object as an argument to a view. If you did, the actions of
-        one user visiting your view could have an effect on subsequent users
-        visiting the same view.
-
-    .. method:: dispatch(request, *args, **kwargs)
-
-        The ``view`` part of the view -- the method that accepts a ``request``
-        argument plus arguments, and returns a HTTP response.
-
-        The default implementation will inspect the HTTP method and attempt to
-        delegate to a method that matches the HTTP method; a ``GET`` will be
-        delegated to :meth:`~View.get()`, a ``POST`` to :meth:`~View.post()`,
-        and so on.
-
-        The default implementation also sets ``request``, ``args`` and
-        ``kwargs`` as instance variables, so any method on the view can know
-        the full details of the request that was made to invoke the view.
-
-    .. method:: http_method_not_allowed(request, *args, **kwargs)
-
-        If the view was called with HTTP method it doesn't support, this method
-        is called instead.
-
-        The default implementation returns ``HttpResponseNotAllowed`` with list
-        of allowed methods in plain text.
-
-TemplateView
-~~~~~~~~~~~~
-.. class:: TemplateView()
-
-    Renders a given template, passing it a ``{{ params }}`` template variable,
-    which is a dictionary of the parameters captured in the URL.
-
-    **Mixins**
-
-    * :class:`django.views.generic.base.TemplateResponseMixin`
-
-    .. attribute:: template_name
-
-        The full name of a template to use.
-
-    .. method:: get_context_data(**kwargs)
-
-        Return a context data dictionary consisting of the contents of
-        ``kwargs`` stored in the context variable ``params``.
-
-    **Context**
-
-    * ``params``: The dictionary of keyword arguments captured from the URL
-      pattern that served the view.
-
-RedirectView
-~~~~~~~~~~~~
-.. class:: RedirectView()
-
-    Redirects to a given URL.
-
-    The given URL may contain dictionary-style string formatting, which will be
-    interpolated against the parameters captured in the URL. Because keyword
-    interpolation is *always* done (even if no arguments are passed in), any
-    ``"%"`` characters in the URL must be written as ``"%%"`` so that Python
-    will convert them to a single percent sign on output.
-
-    If the given URL is ``None``, Django will return an ``HttpResponseGone``
-    (410).
-
-    .. attribute:: url
-
-        The URL to redirect to, as a string. Or ``None`` to raise a 410 (Gone)
-        HTTP error.
-
-    .. attribute:: permanent
-
-        Whether the redirect should be permanent. The only difference here is
-        the HTTP status code returned. If ``True``, then the redirect will use
-        status code 301. If ``False``, then the redirect will use status code
-        302. By default, ``permanent`` is ``True``.
-
-    .. attribute:: query_string
-
-        Whether to pass along the GET query string to the new location. If
-        ``True``, then the query string is appended to the URL. If ``False``,
-        then the query string is discarded. By default, ``query_string`` is
-        ``False``.
-
-    .. method:: get_redirect_url(**kwargs)
-
-        Constructs the target URL for redirection.
-
-        The default implementation uses :attr:`~RedirectView.url` as a starting
-        string, performs expansion of ``%`` parameters in that string, as well
-        as the appending of query string if requested by
-        :attr:`~RedirectView.query_string`. Subclasses may implement any
-        behavior they wish, as long as the method returns a redirect-ready URL
-        string.
-
-Detail views
-------------
-
-.. currentmodule:: django.views.generic.detail
-
-DetailView
-~~~~~~~~~~
-.. class:: BaseDetailView()
-.. class:: DetailView()
-
-    A page representing an individual object.
-
-    While this view is executing, ``self.object`` will contain the object that
-    the view is operating upon.
-
-    :class:`~django.views.generic.base.BaseDetailView` implements the same
-    behavior as :class:`~django.views.generic.base.DetailView`, but doesn't
-    include the
-    :class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`.
-
-    **Mixins**
-
-    * :class:`django.views.generic.detail.SingleObjectMixin`
-    * :class:`django.views.generic.detail.SingleObjectTemplateResponseMixin`
-
-List views
-----------
-
-.. currentmodule:: django.views.generic.list
-
-ListView
-~~~~~~~~
-.. class:: BaseListView()
-.. class:: ListView()
-
-    A page representing a list of objects.
-
-    While this view is executing, ``self.object_list`` will contain the list of
-    objects (usually, but not necessarily a queryset) that the view is
-    operating upon.
-
-    :class:`~django.views.generic.list.BaseListView` implements the same
-    behavior as :class:`~django.views.generic.list.ListView`, but doesn't
-    include the
-    :class:`~django.views.generic.list.MultipleObjectTemplateResponseMixin`.
-
-    **Mixins**
-
-    * :class:`django.views.generic.list.MultipleObjectMixin`
-    * :class:`django.views.generic.list.MultipleObjectTemplateResponseMixin`
-
-
-Editing views
--------------
-
-.. currentmodule:: django.views.generic.edit
-
-FormView
-~~~~~~~~
-.. class:: BaseFormView()
-.. class:: FormView()
-
-    A view that displays a form. On error, redisplays the form with validation
-    errors; on success, redirects to a new URL.
-
-    :class:`~django.views.generic.edit.BaseFormView` implements the same
-    behavior as :class:`~django.views.generic.edit.FormView`, but doesn't
-    include the :class:`~django.views.generic.base.TemplateResponseMixin`.
-
-    **Mixins**
-
-    * :class:`django.views.generic.edit.FormMixin`
-    * :class:`django.views.generic.edit.ProcessFormView`
-
-CreateView
-~~~~~~~~~~
-.. class:: BaseCreateView()
-.. class:: CreateView()
-
-    A view that displays a form for creating an object, redisplaying the form
-    with validation errors (if there are any) and saving the object.
-
-    :class:`~django.views.generic.edit.BaseCreateView` implements the same
-    behavior as :class:`~django.views.generic.edit.CreateView`, but doesn't
-    include the :class:`~django.views.generic.base.TemplateResponseMixin`.
-
-    **Mixins**
-
-    * :class:`django.views.generic.edit.ModelFormMixin`
-    * :class:`django.views.generic.edit.ProcessFormView`
-
-UpdateView
-~~~~~~~~~~
-.. class:: BaseUpdateView()
-.. class:: UpdateView()
-
-    A view that displays a form for editing an existing object, redisplaying
-    the form with validation errors (if there are any) and saving changes to
-    the object. This uses a form automatically generated from the object's
-    model class (unless a form class is manually specified).
-
-    :class:`~django.views.generic.edit.BaseUpdateView` implements the same
-    behavior as :class:`~django.views.generic.edit.UpdateView`, but doesn't
-    include the :class:`~django.views.generic.base.TemplateResponseMixin`.
-
-    **Mixins**
-
-    * :class:`django.views.generic.edit.ModelFormMixin`
-    * :class:`django.views.generic.edit.ProcessFormView`
-
-DeleteView
-~~~~~~~~~~
-.. class:: BaseDeleteView()
-.. class:: DeleteView()
-
-    A view that displays a confirmation page and deletes an existing object.
-    The given object will only be deleted if the request method is ``POST``. If
-    this view is fetched via ``GET``, it will display a confirmation page that
-    should contain a form that POSTs to the same URL.
-
-    :class:`~django.views.generic.edit.BaseDeleteView` implements the same
-    behavior as :class:`~django.views.generic.edit.DeleteView`, but doesn't
-    include the :class:`~django.views.generic.base.TemplateResponseMixin`.
-
-    **Mixins**
-
-    * :class:`django.views.generic.edit.DeletionMixin`
-    * :class:`django.views.generic.detail.BaseDetailView`
-
-    **Notes**
-
-    * The delete confirmation page displayed to a GET request uses a
-      ``template_name_suffix`` of ``'_confirm_delete'``.
-
-Date-based views
-----------------
-
-Date-based generic views (in the module :mod:`django.views.generic.dates`)
-are views for displaying drilldown pages for date-based data.
-
-.. currentmodule:: django.views.generic.dates
-
-ArchiveIndexView
-~~~~~~~~~~~~~~~~
-.. class:: BaseArchiveIndexView()
-.. class:: ArchiveIndexView()
-
-    A top-level index page showing the "latest" objects, by date. Objects with
-    a date in the *future* are not included unless you set ``allow_future`` to
-    ``True``.
-
-    :class:`~django.views.generic.dates.BaseArchiveIndexView` implements the
-    same behavior as :class:`~django.views.generic.dates.ArchiveIndexView`, but
-    doesn't include the
-    :class:`~django.views.generic.list.MultipleObjectTemplateResponseMixin`.
-
-    **Mixins**
-
-    * :class:`django.views.generic.dates.BaseDateListView`
-    * :class:`django.views.generic.list.MultipleObjectTemplateResponseMixin`
-
-    **Notes**
-
-    * Uses a default ``context_object_name`` of ``latest``.
-    * Uses a default ``template_name_suffix`` of ``_archive``.
-
-YearArchiveView
-~~~~~~~~~~~~~~~
-.. class:: BaseYearArchiveView()
-.. class:: YearArchiveView()
-
-    A yearly archive page showing all available months in a given year. Objects
-    with a date in the *future* are not displayed unless you set
-    ``allow_future`` to ``True``.
-
-    :class:`~django.views.generic.dates.BaseYearArchiveView` implements the
-    same behavior as :class:`~django.views.generic.dates.YearArchiveView`, but
-    doesn't include the
-    :class:`~django.views.generic.list.MultipleObjectTemplateResponseMixin`.
-
-    **Mixins**
-
-    * :class:`django.views.generic.list.MultipleObjectTemplateResponseMixin`
-    * :class:`django.views.generic.dates.YearMixin`
-    * :class:`django.views.generic.dates.BaseDateListView`
-
-    .. attribute:: make_object_list
-
-        A boolean specifying whether to retrieve the full list of objects for
-        this year and pass those to the template. If ``True``, the list of
-        objects will be made available to the context. By default, this is
-        ``False``.
-
-    .. method:: get_make_object_list()
-
-        Determine if an object list will be returned as part of the context. If
-        ``False``, the ``None`` queryset will be used as the object list.
-
-    **Context**
-
-    In addition to the context provided by
-    :class:`django.views.generic.list.MultipleObjectMixin` (via
-    :class:`django.views.generic.dates.BaseDateListView`), the template's
-    context will be:
-
-    * ``date_list``: A ``DateQuerySet`` object containing all months that
-      have objects available according to ``queryset``, represented as
-      ``datetime.datetime`` objects, in ascending order.
-
-    * ``year``: A ``datetime.date`` object representing the given year.
-
-    * ``next_year``: A ``datetime.date`` object representing the first day
-      of the next year. If the next year is in the future, this will be
-      ``None``.
-
-    * ``previous_year``: A ``datetime.date`` object representing the first
-      day of the previous year. Unlike ``next_year``, this will never be
-      ``None``.
-
-    **Notes**
-
-    * Uses a default ``template_name_suffix`` of ``_archive_year``.
-
-MonthArchiveView
-~~~~~~~~~~~~~~~~
-.. class:: BaseMonthArchiveView()
-.. class:: MonthArchiveView()
-
-    A monthly archive page showing all objects in a given month. Objects with a
-    date in the *future* are not displayed unless you set ``allow_future`` to
-    ``True``.
-
-    :class:`~django.views.generic.dates.BaseMonthArchiveView` implements
-    the same behavior as
-    :class:`~django.views.generic.dates.MonthArchiveView`, but doesn't
-    include the
-    :class:`~django.views.generic.list.MultipleObjectTemplateResponseMixin`.
-
-    **Mixins**
-
-    * :class:`django.views.generic.list.MultipleObjectTemplateResponseMixin`
-    * :class:`django.views.generic.dates.YearMixin`
-    * :class:`django.views.generic.dates.MonthMixin`
-    * :class:`django.views.generic.dates.BaseDateListView`
-
-    **Context**
-
-    In addition to the context provided by
-    :class:`~django.views.generic.list.MultipleObjectMixin` (via
-    :class:`~django.views.generic.dates.BaseDateListView`), the template's
-    context will be:
-
-    * ``date_list``: A ``DateQuerySet`` object containing all days that
-      have objects available in the given month, according to ``queryset``,
-      represented as ``datetime.datetime`` objects, in ascending order.
-
-    * ``month``: A ``datetime.date`` object representing the given month.
-
-    * ``next_month``: A ``datetime.date`` object representing the first day
-      of the next month. If the next month is in the future, this will be
-      ``None``.
-
-    * ``previous_month``: A ``datetime.date`` object representing the first
-      day of the previous month. Unlike ``next_month``, this will never be
-      ``None``.
-
-    **Notes**
-
-    * Uses a default ``template_name_suffix`` of ``_archive_month``.
-
-WeekArchiveView
-~~~~~~~~~~~~~~~
-.. class:: BaseWeekArchiveView()
-.. class:: WeekArchiveView()
-
-    A weekly archive page showing all objects in a given week. Objects with a
-    date in the *future* are not displayed unless you set ``allow_future`` to
-    ``True``.
-
-    :class:`~django.views.generic.dates.BaseWeekArchiveView` implements the
-    same behavior as :class:`~django.views.generic.dates.WeekArchiveView`, but
-    doesn't include the
-    :class:`~django.views.generic.list.MultipleObjectTemplateResponseMixin`.
-
-    **Mixins**
-
-    * :class:`django.views.generic.list.MultipleObjectTemplateResponseMixin`
-    * :class:`django.views.generic.dates.YearMixin`
-    * :class:`django.views.generic.dates.MonthMixin`
-    * :class:`django.views.generic.dates.BaseDateListView`
-
-    **Context**
-
-    In addition to the context provided by
-    :class:`~django.views.generic.list.MultipleObjectMixin` (via
-    :class:`~django.views.generic.dates.BaseDateListView`), the template's
-    context will be:
-
-    * ``week``: A ``datetime.date`` object representing the first day of
-      the given week.
-
-    * ``next_week``: A ``datetime.date`` object representing the first day
-      of the next week. If the next week is in the future, this will be
-      ``None``.
-
-    * ``previous_week``: A ``datetime.date`` object representing the first
-      day of the previous week. Unlike ``next_week``, this will never be
-      ``None``.
-
-    **Notes**
-
-    * Uses a default ``template_name_suffix`` of ``_archive_week``.
-
-DayArchiveView
-~~~~~~~~~~~~~~
-.. class:: BaseDayArchiveView()
-.. class:: DayArchiveView()
-
-    A day archive page showing all objects in a given day. Days in the future
-    throw a 404 error, regardless of whether any objects exist for future days,
-    unless you set ``allow_future`` to ``True``.
-
-    :class:`~django.views.generic.dates.BaseDayArchiveView` implements the same
-    behavior as :class:`~django.views.generic.dates.DayArchiveView`, but
-    doesn't include the
-    :class:`~django.views.generic.list.MultipleObjectTemplateResponseMixin`.
-
-    **Mixins**
-
-    * :class:`django.views.generic.list.MultipleObjectTemplateResponseMixin`
-    * :class:`django.views.generic.dates.YearMixin`
-    * :class:`django.views.generic.dates.MonthMixin`
-    * :class:`django.views.generic.dates.DayMixin`
-    * :class:`django.views.generic.dates.BaseDateListView`
-
-    **Context**
-
-    In addition to the context provided by
-    :class:`~django.views.generic.list.MultipleObjectMixin` (via
-    :class:`~django.views.generic.dates.BaseDateListView`), the template's
-    context will be:
-
-    * ``day``: A ``datetime.date`` object representing the given day.
-
-    * ``next_day``: A ``datetime.date`` object representing the next day.
-      If the next day is in the future, this will be ``None``.
-
-    * ``previous_day``: A ``datetime.date`` object representing the
-      previous day. Unlike ``next_day``, this will never be ``None``.
-
-    * ``next_month``: A ``datetime.date`` object representing the first day
-      of the next month. If the next month is in the future, this will be
-      ``None``.
-
-    * ``previous_month``: A ``datetime.date`` object representing the first
-      day of the previous month. Unlike ``next_month``, this will never be
-      ``None``.
-
-    **Notes**
-
-    * Uses a default ``template_name_suffix`` of ``_archive_day``.
-
-TodayArchiveView
-~~~~~~~~~~~~~~~~
-.. class:: BaseTodayArchiveView()
-.. class:: TodayArchiveView()
-
-    A day archive page showing all objects for *today*. This is exactly the
-    same as :class:`django.views.generic.dates.DayArchiveView`, except today's
-    date is used instead of the ``year``/``month``/``day`` arguments.
-
-    :class:`~django.views.generic.dates.BaseTodayArchiveView` implements the
-    same behavior as :class:`~django.views.generic.dates.TodayArchiveView`, but
-    doesn't include the
-    :class:`~django.views.generic.list.MultipleObjectTemplateResponseMixin`.
-
-    **Mixins**
-
-    * :class:`django.views.generic.list.MultipleObjectTemplateResponseMixin`
-    * :class:`django.views.generic.dates.BaseDayArchiveView`
-
-DateDetailView
-~~~~~~~~~~~~~~
-.. class:: BaseDateDetailView()
-.. class:: DateDetailView()
-
-    A page representing an individual object. If the object has a date value in
-    the future, the view will throw a 404 error by default, unless you set
-    ``allow_future`` to ``True``.
-
-    :class:`~django.views.generic.dates.BaseDateDetailView` implements the same
-    behavior as :class:`~django.views.generic.dates.DateDetailView`, but
-    doesn't include the
-    :class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`.
-
-    **Mixins**
-
-    * :class:`django.views.generic.detail.SingleObjectTemplateResponseMixin`
-    * :class:`django.views.generic.detail.BaseDetailView`
-    * :class:`django.views.generic.dates.DateMixin`
-    * :class:`django.views.generic.dates.YearMixin`
-    * :class:`django.views.generic.dates.MonthMixin`
-    * :class:`django.views.generic.dates.DayMixin`

+ 224 - 0
docs/ref/class-based-views/base.txt

@@ -0,0 +1,224 @@
+==========
+Base views
+==========
+
+The following three classes provide much of the functionality needed to create
+Django views. You may think of them as *parent* views, which can be used by
+themselves or inherited from. They may not provide all the capabilities
+required for projects, in which case there are Mixins and Generic class-based
+views.
+
+.. class:: django.views.generic.base.View
+
+    The master class-based base view. All other class-based views inherit from
+    this base class.
+
+    **Method Flowchart**
+
+    1. :meth:`dispatch()`
+    2. :meth:`http_method_not_allowed()`
+
+    **Example views.py**::
+
+        from django.http import HttpResponse
+        from django.views.generic import View
+
+        class MyView(View):
+
+            def get(self, request, *args, **kwargs):
+                return HttpResponse('Hello, World!')
+
+    **Example urls.py**::
+
+        from django.conf.urls import patterns, url
+        
+        from myapp.views import MyView
+        
+        urlpatterns = patterns('',
+            url(r'^mine/$', MyView.as_view(), name='my-view'),
+        )
+        
+    **Methods**
+
+    .. method:: dispatch(request, *args, **kwargs)
+
+        The ``view`` part of the view -- the method that accepts a ``request``
+        argument plus arguments, and returns a HTTP response.
+
+        The default implementation will inspect the HTTP method and attempt to
+        delegate to a method that matches the HTTP method; a ``GET`` will be
+        delegated to :meth:`~View.get()`, a ``POST`` to :meth:`~View.post()`,
+        and so on.
+
+        The default implementation also sets ``request``, ``args`` and
+        ``kwargs`` as instance variables, so any method on the view can know
+        the full details of the request that was made to invoke the view.
+
+    .. method:: http_method_not_allowed(request, *args, **kwargs)
+
+        If the view was called with a HTTP method it doesn't support, this
+        method is called instead.
+
+        The default implementation returns ``HttpResponseNotAllowed`` with list
+        of allowed methods in plain text.
+        
+    .. note:: 
+
+        Documentation on class-based views is a work in progress. As yet, only the
+        methods defined directly on the class are documented here, not methods
+        defined on superclasses.
+
+.. class:: django.views.generic.base.TemplateView
+
+    Renders a given template, passing it a ``{{ params }}`` template variable,
+    which is a dictionary of the parameters captured in the URL.
+
+    **Ancestors (MRO)**
+
+    * :class:`django.views.generic.base.TemplateView`
+    * :class:`django.views.generic.base.TemplateResponseMixin`
+    * :class:`django.views.generic.base.View`
+
+    **Method Flowchart**
+
+    1. :meth:`dispatch()`
+    2. :meth:`http_method_not_allowed()`
+    3. :meth:`get_context_data()`
+      
+    **Example views.py**::
+
+        from django.views.generic.base import TemplateView
+        
+        from articles.models import Article
+
+        class HomePageView(TemplateView):
+
+            template_name = "home.html"
+            
+            def get_context_data(self, **kwargs):
+                context = super(HomePageView, self).get_context_data(**kwargs)
+                context['latest_articles'] = Article.objects.all()[:5]
+                return context
+                
+    **Example urls.py**::
+
+        from django.conf.urls import patterns, url
+
+        from myapp.views import HomePageView
+
+        urlpatterns = patterns('',
+            url(r'^$', HomePageView.as_view(), name='home'),
+        )
+
+    **Methods and Attributes**
+
+    .. attribute:: template_name
+
+        The full name of a template to use.
+
+    .. method:: get_context_data(**kwargs)
+
+        Return a context data dictionary consisting of the contents of
+        ``kwargs`` stored in the context variable ``params``.
+
+    **Context**
+
+    * ``params``: The dictionary of keyword arguments captured from the URL
+      pattern that served the view.
+
+    .. note:: 
+
+        Documentation on class-based views is a work in progress. As yet, only the
+        methods defined directly on the class are documented here, not methods
+        defined on superclasses.
+
+.. class:: django.views.generic.base.RedirectView
+
+    Redirects to a given URL.
+
+    The given URL may contain dictionary-style string formatting, which will be
+    interpolated against the parameters captured in the URL. Because keyword
+    interpolation is *always* done (even if no arguments are passed in), any
+    ``"%"`` characters in the URL must be written as ``"%%"`` so that Python
+    will convert them to a single percent sign on output.
+
+    If the given URL is ``None``, Django will return an ``HttpResponseGone``
+    (410).
+
+    **Ancestors (MRO)**
+
+    * :class:`django.views.generic.base.View`
+
+    **Method Flowchart**
+
+    1. :meth:`dispatch()`
+    2. :meth:`http_method_not_allowed()`
+    3. :meth:`get_redirect_url()`
+
+    **Example views.py**::
+
+        from django.shortcuts import get_object_or_404
+        from django.views.generic.base import RedirectView
+        
+        from articles.models import Article
+
+        class ArticleCounterRedirectView(RedirectView):
+
+            permanent = False
+            query_string = True
+
+            def get_redirect_url(self, pk):
+                article = get_object_or_404(Article, pk=pk)
+                article.update_counter()
+                return reverse('product_detail', args=(pk,))
+
+    **Example urls.py**::
+
+        from django.conf.urls import patterns, url
+        from django.views.generic.base import RedirectView
+        
+        from article.views import ArticleCounterRedirectView
+
+        urlpatterns = patterns('',
+
+            url(r'r^(?P<pk>\d+)/$', ArticleCounterRedirectView.as_view(), name='article-counter'),
+            url(r'^go-to-django/$', RedirectView.as_view(url='http://djangoproject.com'), name='go-to-django'),
+        )
+
+    **Methods and Attributes**
+
+    .. attribute:: url
+
+        The URL to redirect to, as a string. Or ``None`` to raise a 410 (Gone)
+        HTTP error.
+
+    .. attribute:: permanent
+
+        Whether the redirect should be permanent. The only difference here is
+        the HTTP status code returned. If ``True``, then the redirect will use
+        status code 301. If ``False``, then the redirect will use status code
+        302. By default, ``permanent`` is ``True``.
+
+    .. attribute:: query_string
+
+        Whether to pass along the GET query string to the new location. If
+        ``True``, then the query string is appended to the URL. If ``False``,
+        then the query string is discarded. By default, ``query_string`` is
+        ``False``.
+
+    .. method:: get_redirect_url(**kwargs)
+
+        Constructs the target URL for redirection.
+
+        The default implementation uses :attr:`~RedirectView.url` as a starting
+        string, performs expansion of ``%`` parameters in that string, as well
+        as the appending of query string if requested by
+        :attr:`~RedirectView.query_string`. Subclasses may implement any
+        behavior they wish, as long as the method returns a redirect-ready URL
+        string.
+
+    .. note:: 
+
+        Documentation on class-based views is a work in progress. As yet, only the
+        methods defined directly on the class are documented here, not methods
+        defined on superclasses.

+ 273 - 0
docs/ref/class-based-views/generic-date-based.txt

@@ -0,0 +1,273 @@
+==================
+Generic date views
+==================
+
+Date-based generic views (in the module :mod:`django.views.generic.dates`)
+are views for displaying drilldown pages for date-based data.
+
+.. class:: django.views.generic.dates.ArchiveIndexView
+
+    A top-level index page showing the "latest" objects, by date. Objects with
+    a date in the *future* are not included unless you set ``allow_future`` to
+    ``True``.
+
+    **Ancestors (MRO)**
+
+    * :class:`django.views.generic.dates.ArchiveIndexView`
+    * :class:`django.views.generic.list.MultipleObjectTemplateResponseMixin`
+    * :class:`django.views.generic.base.TemplateResponseMixin`
+    * :class:`django.views.generic.dates.BaseArchiveIndexView`
+    * :class:`django.views.generic.dates.BaseDateListView`
+    * :class:`django.views.generic.list.MultipleObjectMixin`
+    * :class:`django.views.generic.dates.DateMixin`
+    * :class:`django.views.generic.base.View`
+    
+    **Notes**
+
+    * Uses a default ``context_object_name`` of ``latest``.
+    * Uses a default ``template_name_suffix`` of ``_archive``.
+
+.. class:: django.views.generic.dates.YearArchiveView
+
+    A yearly archive page showing all available months in a given year. Objects
+    with a date in the *future* are not displayed unless you set
+    ``allow_future`` to ``True``.
+
+    **Ancestors (MRO)**
+
+    * :class:`django.views.generic.dates.YearArchiveView`
+    * :class:`django.views.generic.list.MultipleObjectTemplateResponseMixin`
+    * :class:`django.views.generic.base.TemplateResponseMixin`
+    * :class:`django.views.generic.dates.BaseYearArchiveView`
+    * :class:`django.views.generic.dates.YearMixin`
+    * :class:`django.views.generic.dates.BaseDateListView`
+    * :class:`django.views.generic.list.MultipleObjectMixin`
+    * :class:`django.views.generic.dates.DateMixin`
+    * :class:`django.views.generic.base.View`
+
+    .. attribute:: make_object_list
+
+        A boolean specifying whether to retrieve the full list of objects for
+        this year and pass those to the template. If ``True``, the list of
+        objects will be made available to the context. By default, this is
+        ``False``.
+
+    .. method:: get_make_object_list()
+
+        Determine if an object list will be returned as part of the context. If
+        ``False``, the ``None`` queryset will be used as the object list.
+
+    **Context**
+
+    In addition to the context provided by
+    :class:`django.views.generic.list.MultipleObjectMixin` (via
+    :class:`django.views.generic.dates.BaseDateListView`), the template's
+    context will be:
+
+    * ``date_list``: A
+      :meth:`DateQuerySet<django.db.models.query.QuerySet.dates>` object object
+      containing all months that have objects available according to
+      ``queryset``, represented as
+      :class:`datetime.datetime<python:datetime.datetime>` objects, in
+      ascending order.
+
+    * ``year``: A :class:`datetime.date<python:datetime.date>` object
+      representing the given year.
+
+    * ``next_year``: A :class:`datetime.date<python:datetime.date>` object
+      representing the first day of the next year. If the next year is in the
+      future, this will be ``None``.
+
+    * ``previous_year``: A :class:`datetime.date<python:datetime.date>` object
+      representing the first day of the previous year. Unlike ``next_year``,
+      this will never be ``None``.
+
+    **Notes**
+
+    * Uses a default ``template_name_suffix`` of ``_archive_year``.
+
+.. class:: django.views.generic.dates.MonthArchiveView
+
+    A monthly archive page showing all objects in a given month. Objects with a
+    date in the *future* are not displayed unless you set ``allow_future`` to
+    ``True``.
+
+    **Ancestors (MRO)**
+
+    * :class:`django.views.generic.dates.MonthArchiveView`
+    * :class:`django.views.generic.list.MultipleObjectTemplateResponseMixin`
+    * :class:`django.views.generic.base.TemplateResponseMixin`
+    * :class:`django.views.generic.dates.BaseMonthArchiveView`
+    * :class:`django.views.generic.dates.YearMixin`
+    * :class:`django.views.generic.dates.MonthMixin`
+    * :class:`django.views.generic.dates.BaseDateListView`
+    * :class:`django.views.generic.list.MultipleObjectMixin`
+    * :class:`django.views.generic.dates.DateMixin`
+    * :class:`django.views.generic.base.View`
+
+    **Context**
+
+    In addition to the context provided by
+    :class:`~django.views.generic.list.MultipleObjectMixin` (via
+    :class:`~django.views.generic.dates.BaseDateListView`), the template's
+    context will be:
+
+    * ``date_list``: A
+      :meth:`DateQuerySet<django.db.models.query.QuerySet.dates>` object
+      containing all days that have objects available in the given month,
+      according to ``queryset``, represented as
+      :class:`datetime.datetime<python:datetime.datetime>` objects, in
+      ascending order.
+
+    * ``month``: A :class:`datetime.date<python:datetime.date>` object
+      representing the given month.
+
+    * ``next_month``: A :class:`datetime.date<python:datetime.date>` object
+      representing the first day of the next month. If the next month is in the
+      future, this will be ``None``.
+
+    * ``previous_month``: A :class:`datetime.date<python:datetime.date>` object
+      representing the first day of the previous month. Unlike ``next_month``,
+      this will never be ``None``.
+
+    **Notes**
+
+    * Uses a default ``template_name_suffix`` of ``_archive_month``.
+
+.. class:: django.views.generic.dates.WeekArchiveView
+
+    A weekly archive page showing all objects in a given week. Objects with a
+    date in the *future* are not displayed unless you set ``allow_future`` to
+    ``True``.
+
+    **Ancestors (MRO)**
+
+    * :class:`django.views.generic.dates.WeekArchiveView`
+    * :class:`django.views.generic.list.MultipleObjectTemplateResponseMixin`
+    * :class:`django.views.generic.base.TemplateResponseMixin`
+    * :class:`django.views.generic.dates.BaseWeekArchiveView`
+    * :class:`django.views.generic.dates.YearMixin`
+    * :class:`django.views.generic.dates.WeekMixin`
+    * :class:`django.views.generic.dates.BaseDateListView`
+    * :class:`django.views.generic.list.MultipleObjectMixin`
+    * :class:`django.views.generic.dates.DateMixin`
+    * :class:`django.views.generic.base.View`
+
+    **Context**
+
+    In addition to the context provided by
+    :class:`~django.views.generic.list.MultipleObjectMixin` (via
+    :class:`~django.views.generic.dates.BaseDateListView`), the template's
+    context will be:
+
+    * ``week``: A :class:`datetime.date<python:datetime.date>` object
+      representing the first day of the given week.
+
+    * ``next_week``: A :class:`datetime.date<python:datetime.date>` object
+      representing the first day of the next week. If the next week is in the
+      future, this will be ``None``.
+
+    * ``previous_week``: A :class:`datetime.date<python:datetime.date>` object
+      representing the first day of the previous week. Unlike ``next_week``,
+      this will never be ``None``.
+
+    **Notes**
+
+    * Uses a default ``template_name_suffix`` of ``_archive_week``.
+
+.. class:: django.views.generic.dates.DayArchiveView
+
+    A day archive page showing all objects in a given day. Days in the future
+    throw a 404 error, regardless of whether any objects exist for future days,
+    unless you set ``allow_future`` to ``True``.
+
+    **Ancestors (MRO)**
+
+    * :class:`django.views.generic.dates.DayArchiveView`
+    * :class:`django.views.generic.list.MultipleObjectTemplateResponseMixin`
+    * :class:`django.views.generic.base.TemplateResponseMixin`
+    * :class:`django.views.generic.dates.BaseDayArchiveView`
+    * :class:`django.views.generic.dates.YearMixin`
+    * :class:`django.views.generic.dates.MonthMixin`
+    * :class:`django.views.generic.dates.DayMixin`
+    * :class:`django.views.generic.dates.BaseDateListView`
+    * :class:`django.views.generic.list.MultipleObjectMixin`
+    * :class:`django.views.generic.dates.DateMixin`
+    * :class:`django.views.generic.base.View`
+
+    **Context**
+
+    In addition to the context provided by
+    :class:`~django.views.generic.list.MultipleObjectMixin` (via
+    :class:`~django.views.generic.dates.BaseDateListView`), the template's
+    context will be:
+
+    * ``day``: A :class:`datetime.date<python:datetime.date>` object
+      representing the given day.
+
+    * ``next_day``: A :class:`datetime.date<python:datetime.date>` object
+      representing the next day. If the next day is in the future, this will be
+      ``None``.
+
+    * ``previous_day``: A :class:`datetime.date<python:datetime.date>` object
+      representing the previous day. Unlike ``next_day``, this will never be
+      ``None``.
+
+    * ``next_month``: A :class:`datetime.date<python:datetime.date>` object
+      representing the first day of the next month. If the next month is in the
+      future, this will be ``None``.
+
+    * ``previous_month``: A :class:`datetime.date<python:datetime.date>` object
+      representing the first day of the previous month. Unlike ``next_month``,
+      this will never be ``None``.
+
+    **Notes**
+
+    * Uses a default ``template_name_suffix`` of ``_archive_day``.
+
+.. class:: django.views.generic.dates.TodayArchiveView
+
+    A day archive page showing all objects for *today*. This is exactly the
+    same as :class:`django.views.generic.dates.DayArchiveView`, except today's
+    date is used instead of the ``year``/``month``/``day`` arguments.
+
+    **Ancestors (MRO)**
+
+    * :class:`django.views.generic.dates.TodayArchiveView`
+    * :class:`django.views.generic.list.MultipleObjectTemplateResponseMixin`
+    * :class:`django.views.generic.base.TemplateResponseMixin`
+    * :class:`django.views.generic.dates.BaseTodayArchiveView`
+    * :class:`django.views.generic.dates.BaseDayArchiveView`
+    * :class:`django.views.generic.dates.YearMixin`
+    * :class:`django.views.generic.dates.MonthMixin`
+    * :class:`django.views.generic.dates.DayMixin`
+    * :class:`django.views.generic.dates.BaseDateListView`
+    * :class:`django.views.generic.list.MultipleObjectMixin`
+    * :class:`django.views.generic.dates.DateMixin`
+    * :class:`django.views.generic.base.View`
+
+.. class:: django.views.generic.dates.DateDetailView
+
+    A page representing an individual object. If the object has a date value in
+    the future, the view will throw a 404 error by default, unless you set
+    ``allow_future`` to ``True``.
+
+    **Ancestors (MRO)**
+
+    * :class:`django.views.generic.dates.DateDetailView`
+    * :class:`django.views.generic.detail.SingleObjectTemplateResponseMixin`
+    * :class:`django.views.generic.base.TemplateResponseMixin`
+    * :class:`django.views.generic.dates.BaseDateDetailView`
+    * :class:`django.views.generic.dates.YearMixin`
+    * :class:`django.views.generic.dates.MonthMixin`
+    * :class:`django.views.generic.dates.DayMixin`
+    * :class:`django.views.generic.dates.DateMixin`
+    * :class:`django.views.generic.detail.BaseDetailView`
+    * :class:`django.views.generic.detail.SingleObjectMixin`
+    * :class:`django.views.generic.base.View`
+
+.. note::
+
+    All of the generic views listed above have matching Base* views that only
+    differ in that the they do not include the
+    :class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`.

+ 86 - 0
docs/ref/class-based-views/generic-display.txt

@@ -0,0 +1,86 @@
+=====================
+Generic display views
+=====================
+
+The two following generic class-based views are designed to display data. On
+many projects they are typically the most commonly used views.
+
+.. class:: django.views.generic.detail.DetailView
+
+    While this view is executing, ``self.object`` will contain the object that
+    the view is operating upon.
+
+    **Ancestors (MRO)**
+
+    * :class:`django.views.generic.detail.SingleObjectTemplateResponseMixin`
+    * :class:`django.views.generic.base.TemplateResponseMixin`
+    * :class:`django.views.generic.detail.BaseDetailView`
+    * :class:`django.views.generic.detail.SingleObjectMixin`
+    * :class:`django.views.generic.base.View`
+
+    **Method Flowchart**
+
+    1. :meth:`dispatch()`
+    2. :meth:`http_method_not_allowed()`
+    3. :meth:`get_template_names()`
+    4. :meth:`get_slug_field()`
+    5. :meth:`get_queryset()`
+    6. :meth:`get_object()`
+    7. :meth:`get_context_object_name()`
+    8. :meth:`get_context_data()`
+    9. :meth:`get()`
+    10. :meth:`render_to_response()`
+
+    **Example views.py**::
+
+        from django.views.generic.detail import DetailView
+        from django.utils import timezone
+
+        from articles.models import Article
+
+        class ArticleDetailView(DetailView):
+        
+            model = Article
+
+            def get_context_data(self, **kwargs):
+                context = super(ArticleDetailView, self).get_context_data(**kwargs)
+                context['now'] = timezone.now()
+                return context
+
+    **Example urls.py**::
+
+        from django.conf.urls import patterns, url
+
+        from article.views import ArticleDetailView
+
+        urlpatterns = patterns('',
+            url(r'^(?P<slug>[-_\w]+)/$', ArticleDetailView.as_view(), 'article-detail'),
+        )    
+
+.. class:: django.views.generic.list.ListView
+
+    A page representing a list of objects.
+
+    While this view is executing, ``self.object_list`` will contain the list of
+    objects (usually, but not necessarily a queryset) that the view is
+    operating upon.
+
+    **Mixins**
+
+    * :class:`django.views.generic.list.ListView`
+    * :class:`django.views.generic.list.MultipleObjectTemplateResponseMixin`
+    * :class:`django.views.generic.base.TemplateResponseMixin`
+    * :class:`django.views.generic.list.BaseListView`
+    * :class:`django.views.generic.list.MultipleObjectMixin`
+    * :class:`django.views.generic.base.View`
+
+    **Method Flowchart**
+
+    1. :meth:`dispatch():`
+    2. :meth:`http_method_not_allowed():`
+    3. :meth:`get_template_names():`
+    4. :meth:`get_queryset():`
+    5. :meth:`get_objects():`
+    6. :meth:`get_context_data():`
+    7. :meth:`get():`
+    8. :meth:`render_to_response():`

+ 78 - 0
docs/ref/class-based-views/generic-editing.txt

@@ -0,0 +1,78 @@
+=====================
+Generic editing views
+=====================
+
+The views described here provide a foundation for editing content.
+
+.. class:: django.views.generic.edit.FormView
+
+    A view that displays a form. On error, redisplays the form with validation
+    errors; on success, redirects to a new URL.
+
+    **Ancestors (MRO)**
+
+    * :class:`django.views.generic.edit.FormView`
+    * :class:`django.views.generic.base.TemplateResponseMixin`
+    * :class:`django.views.generic.edit.BaseFormView`
+    * :class:`django.views.generic.edit.FormMixin`
+    * :class:`django.views.generic.edit.ProcessFormView`
+    * :class:`django.views.generic.base.View`
+
+.. class:: django.views.generic.edit.CreateView
+
+    A view that displays a form for creating an object, redisplaying the form
+    with validation errors (if there are any) and saving the object.
+
+    **Ancestors (MRO)**
+
+    * :class:`django.views.generic.edit.CreateView`
+    * :class:`django.views.generic.detail.SingleObjectTemplateResponseMixin`
+    * :class:`django.views.generic.base.TemplateResponseMixin`
+    * :class:`django.views.generic.edit.BaseCreateView`
+    * :class:`django.views.generic.edit.ModelFormMixin`
+    * :class:`django.views.generic.edit.FormMixin`
+    * :class:`django.views.generic.detail.SingleObjectMixin`
+    * :class:`django.views.generic.edit.ProcessFormView`
+    * :class:`django.views.generic.base.View`
+
+.. class:: django.views.generic.edit.UpdateView
+
+    A view that displays a form for editing an existing object, redisplaying
+    the form with validation errors (if there are any) and saving changes to
+    the object. This uses a form automatically generated from the object's
+    model class (unless a form class is manually specified).
+
+    **Ancestors (MRO)**
+
+    * :class:`django.views.generic.edit.UpdateView`
+    * :class:`django.views.generic.detail.SingleObjectTemplateResponseMixin`
+    * :class:`django.views.generic.base.TemplateResponseMixin`
+    * :class:`django.views.generic.edit.BaseUpdateView`
+    * :class:`django.views.generic.edit.ModelFormMixin`
+    * :class:`django.views.generic.edit.FormMixin`
+    * :class:`django.views.generic.detail.SingleObjectMixin`
+    * :class:`django.views.generic.edit.ProcessFormView`
+    * :class:`django.views.generic.base.View`
+
+.. class:: django.views.generic.edit.DeleteView
+
+    A view that displays a confirmation page and deletes an existing object.
+    The given object will only be deleted if the request method is ``POST``. If
+    this view is fetched via ``GET``, it will display a confirmation page that
+    should contain a form that POSTs to the same URL.
+
+    **Ancestors (MRO)**
+
+    * :class:`django.views.generic.edit.DeleteView`
+    * :class:`django.views.generic.detail.SingleObjectTemplateResponseMixin`
+    * :class:`django.views.generic.base.TemplateResponseMixin`
+    * :class:`django.views.generic.edit.BaseDeleteView`
+    * :class:`django.views.generic.edit.DeletionMixin`
+    * :class:`django.views.generic.detail.BaseDetailView`
+    * :class:`django.views.generic.detail.SingleObjectMixin`
+    * :class:`django.views.generic.base.View`
+
+    **Notes**
+
+    * The delete confirmation page displayed to a GET request uses a
+      ``template_name_suffix`` of ``'_confirm_delete'``.

+ 59 - 0
docs/ref/class-based-views/index.txt

@@ -0,0 +1,59 @@
+=================
+Class-based views
+=================
+
+Class-based views API reference. For introductory material, see
+:doc:`/topics/class-based-views/index`.
+
+.. toctree::
+   :maxdepth: 1
+
+   base
+   generic-display
+   generic-editing
+   generic-date-based
+   mixins
+
+Specification
+-------------
+
+Each request served by a class-based view has an independent state; therefore,
+it is safe to store state variables on the instance (i.e., ``self.foo = 3`` is
+a thread-safe operation).
+
+A class-based view is deployed into a URL pattern using the
+:meth:`~View.as_view()` classmethod::
+
+    urlpatterns = patterns('',
+        (r'^view/$', MyView.as_view(size=42)),
+    )
+
+.. admonition:: Thread safety with view arguments
+
+    Arguments passed to a view are shared between every instance of a view.
+    This means that you shoudn't use a list, dictionary, or any other
+    variable object as an argument to a view. If you did, the actions of
+    one user visiting your view could have an effect on subsequent users
+    visiting the same view.
+
+Any argument passed into :meth:`~View.as_view()` will be assigned onto the
+instance that is used to service a request. Using the previous example,
+this means that every request on ``MyView`` is able to use ``self.size``.
+
+Base vs Generic views
+---------------------
+
+Base class-based views can be thought of as *parent* views, which can be
+used by themselves or inherited from. They may not provide all the
+capabilities required for projects, in which case there are Mixins which
+extend what base views can do.
+
+Django’s generic views are built off of those base views, and were developed
+as a shortcut for common usage patterns such as displaying the details of an
+object. 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 repeat yourself.
+
+Most generic views require the ``queryset`` key, which is a ``QuerySet``
+instance; see :doc:`/topics/db/queries` for more information about ``QuerySet``
+objects.

+ 256 - 0
docs/ref/class-based-views/mixins-date-based.txt

@@ -0,0 +1,256 @@
+=================
+Date-based mixins
+=================
+
+
+.. class:: django.views.generic.dates.YearMixin
+
+    A mixin that can be used to retrieve and provide parsing information for a
+    year component of a date.
+
+    **Methods and Attributes**
+
+    .. attribute:: year_format
+
+        The :func:`~time.strftime` format to use when parsing the year.
+        By default, this is ``'%Y'``.
+
+    .. attribute:: year
+
+        **Optional** The value for the year (as a string). By default, set to
+        ``None``, which means the year will be determined using other means.
+
+    .. method:: get_year_format()
+
+        Returns the :func:`~time.strftime` format to use when parsing the year. Returns
+        :attr:`YearMixin.year_format` by default.
+
+    .. method:: get_year()
+
+        Returns the year for which this view will display data. Tries the
+        following sources, in order:
+
+        * The value of the :attr:`YearMixin.year` attribute.
+        * The value of the `year` argument captured in the URL pattern
+        * The value of the `year` GET query argument.
+
+        Raises a 404 if no valid year specification can be found.
+
+.. class:: django.views.generic.dates.MonthMixin
+
+    A mixin that can be used to retrieve and provide parsing information for a
+    month component of a date.
+
+    **Methods and Attributes**
+
+    .. attribute:: month_format
+
+        The :func:`~time.strftime` format to use when parsing the month. By default, this is
+        ``'%b'``.
+
+    .. attribute:: month
+
+        **Optional** The value for the month (as a string). By default, set to
+        ``None``, which means the month will be determined using other means.
+
+    .. method:: get_month_format()
+
+        Returns the :func:`~time.strftime` format to use when parsing the month. Returns
+        :attr:`MonthMixin.month_format` by default.
+
+    .. method:: get_month()
+
+        Returns the month for which this view will display data. Tries the
+        following sources, in order:
+
+        * The value of the :attr:`MonthMixin.month` attribute.
+        * The value of the `month` argument captured in the URL pattern
+        * The value of the `month` GET query argument.
+
+        Raises a 404 if no valid month specification can be found.
+
+    .. method:: get_next_month(date)
+
+        Returns a date object containing the first day of the month after the
+        date provided. Returns ``None`` if mixed with a view that sets
+        ``allow_future = False``, and the next month is in the future. If
+        ``allow_empty = False``, returns the next month that contains data.
+
+    .. method:: get_prev_month(date)
+
+        Returns a date object containing the first day of the month before the
+        date provided. If ``allow_empty = False``, returns the previous month
+        that contained data.
+
+.. class:: django.views.generic.dates.DayMixin
+
+    A mixin that can be used to retrieve and provide parsing information for a
+    day component of a date.
+
+    **Methods and Attributes**
+
+    .. attribute:: day_format
+
+        The :func:`~time.strftime` format to use when parsing the day. By default, this is
+        ``'%d'``.
+
+    .. attribute:: day
+
+        **Optional** The value for the day (as a string). By default, set to
+        ``None``, which means the day will be determined using other means.
+
+    .. method:: get_day_format()
+
+        Returns the :func:`~time.strftime` format to use when parsing the day. Returns
+        :attr:`DayMixin.day_format` by default.
+
+    .. method:: get_day()
+
+        Returns the day for which this view will display data. Tries the
+        following sources, in order:
+
+        * The value of the :attr:`DayMixin.day` attribute.
+        * The value of the `day` argument captured in the URL pattern
+        * The value of the `day` GET query argument.
+
+        Raises a 404 if no valid day specification can be found.
+
+    .. method:: get_next_day(date)
+
+        Returns a date object containing the next day after the date provided.
+        Returns ``None`` if mixed with a view that sets ``allow_future = False``,
+        and the next day is in the future. If ``allow_empty = False``, returns
+        the next day that contains data.
+
+    .. method:: get_prev_day(date)
+
+        Returns a date object containing the previous day. If
+        ``allow_empty = False``, returns the previous day that contained data.
+
+.. class:: django.views.generic.dates.WeekMixin
+
+    A mixin that can be used to retrieve and provide parsing information for a
+    week component of a date.
+
+    **Methods and Attributes**
+
+    .. attribute:: week_format
+
+        The :func:`~time.strftime` format to use when parsing the week. By default, this is
+        ``'%U'``.
+
+    .. attribute:: week
+
+        **Optional** The value for the week (as a string). By default, set to
+        ``None``, which means the week will be determined using other means.
+
+    .. method:: get_week_format()
+
+        Returns the :func:`~time.strftime` format to use when parsing the week. Returns
+        :attr:`WeekMixin.week_format` by default.
+
+    .. method:: get_week()
+
+        Returns the week for which this view will display data. Tries the
+        following sources, in order:
+
+        * The value of the :attr:`WeekMixin.week` attribute.
+        * The value of the `week` argument captured in the URL pattern
+        * The value of the `week` GET query argument.
+
+        Raises a 404 if no valid week specification can be found.
+
+
+.. class:: django.views.generic.dates.DateMixin
+
+    A mixin class providing common behavior for all date-based views.
+
+    **Methods and Attributes**
+
+    .. attribute:: date_field
+
+        The name of the ``DateField`` or ``DateTimeField`` in the
+        ``QuerySet``'s model that the date-based archive should use to
+        determine the objects on the page.
+
+        When :doc:`time zone support </topics/i18n/timezones>` is enabled and
+        ``date_field`` is a ``DateTimeField``, dates are assumed to be in the
+        current time zone. Otherwise, the queryset could include objects from
+        the previous or the next day in the end user's time zone.
+
+        .. warning::
+
+            In this situation, if you have implemented per-user time zone
+            selection, the same URL may show a different set of objects,
+            depending on the end user's time zone. To avoid this, you should
+            use a ``DateField`` as the ``date_field`` attribute.
+
+    .. attribute:: allow_future
+
+        A boolean specifying whether to include "future" objects on this page,
+        where "future" means objects in which the field specified in
+        ``date_field`` is greater than the current date/time. By default, this
+        is ``False``.
+
+    .. method:: get_date_field()
+
+        Returns the name of the field that contains the date data that this
+        view will operate on. Returns :attr:`DateMixin.date_field` by default.
+
+    .. method:: get_allow_future()
+
+        Determine whether to include "future" objects on this page, where
+        "future" means objects in which the field specified in ``date_field``
+        is greater than the current date/time. Returns
+        :attr:`DateMixin.allow_future` by default.
+
+.. class:: django.views.generic.dates.BaseDateListView
+
+    A base class that provides common behavior for all date-based views. There
+    won't normally be a reason to instantiate
+    :class:`~django.views.generic.dates.BaseDateListView`; instantiate one of
+    the subclasses instead.
+
+    While this view (and it's subclasses) are executing, ``self.object_list``
+    will contain the list of objects that the view is operating upon, and
+    ``self.date_list`` will contain the list of dates for which data is
+    available.
+
+    **Mixins**
+
+    * :class:`~django.views.generic.dates.DateMixin`
+    * :class:`~django.views.generic.list.MultipleObjectMixin`
+
+    **Methods and Attributes**
+
+    .. attribute:: allow_empty
+
+        A boolean specifying whether to display the page if no objects are
+        available. If this is ``True`` and no objects are available, the view
+        will display an empty page instead of raising a 404. By default, this
+        is ``False``.
+
+    .. method:: get_dated_items():
+
+        Returns a 3-tuple containing (``date_list``, ``object_list``,
+        ``extra_context``).
+
+        ``date_list`` is the list of dates for which data is available.
+        ``object_list`` is the list of objects. ``extra_context`` is a
+        dictionary of context data that will be added to any context data
+        provided by the
+        :class:`~django.views.generic.list.MultipleObjectMixin`.
+
+    .. method:: get_dated_queryset(**lookup)
+
+        Returns a queryset, filtered using the query arguments defined by
+        ``lookup``. Enforces any restrictions on the queryset, such as
+        ``allow_empty`` and ``allow_future``.
+
+    .. method:: get_date_list(queryset, date_type)
+
+        Returns the list of dates of type ``date_type`` for which
+        ``queryset`` contains entries. For example, ``get_date_list(qs,
+        'year')`` will return the list of years for which ``qs`` has entries.
+        See :meth:`~django.db.models.query.QuerySet.dates()` for the
+        ways that the ``date_type`` argument can be used.

+ 183 - 0
docs/ref/class-based-views/mixins-editing.txt

@@ -0,0 +1,183 @@
+==============
+Editing mixins
+==============
+
+.. class:: django.views.generic.edit.FormMixin
+
+    A mixin class that provides facilities for creating and displaying forms.
+
+    **Methods and Attributes**
+
+    .. attribute:: initial
+
+        A dictionary containing initial data for the form.
+
+    .. attribute:: form_class
+
+        The form class to instantiate.
+
+    .. attribute:: success_url
+
+        The URL to redirect to when the form is successfully processed.
+
+    .. method:: get_initial()
+
+        Retrieve initial data for the form. By default, returns a copy of
+        :attr:`~django.views.generic.edit.FormMixin.initial`.
+
+        .. versionchanged:: 1.4
+            In Django 1.3, this method was returning the
+            :attr:`~django.views.generic.edit.FormMixin.initial` class variable
+            itself.
+
+    .. method:: get_form_class()
+
+        Retrieve the form class to instantiate. By default
+        :attr:`.form_class`.
+
+    .. method:: get_form(form_class)
+
+        Instantiate an instance of ``form_class`` using
+        :meth:`~django.views.generic.edit.FormMixin.get_form_kwargs`.
+
+    .. method:: get_form_kwargs()
+
+        Build the keyword arguments required to instantiate the form.
+
+        The ``initial`` argument is set to :meth:`.get_initial`. If the
+        request is a ``POST`` or ``PUT``, the request data (``request.POST``
+        and ``request.FILES``) will also be provided.
+
+    .. method:: get_success_url()
+
+        Determine the URL to redirect to when the form is successfully
+        validated. Returns
+        :attr:`~django.views.generic.edit.FormMixin.success_url` by default.
+
+    .. method:: form_valid(form)
+
+        Redirects to
+        :meth:`~django.views.generic.edit.FormMixin.get_success_url`.
+
+    .. method:: form_invalid(form)
+
+        Renders a response, providing the invalid form as context.
+
+    .. method:: get_context_data(**kwargs)
+
+        Populates a context containing the contents of ``kwargs``.
+
+    **Context**
+
+    * ``form``: The form instance that was generated for the view.
+
+    .. note::
+
+        Views mixing :class:`FormMixin` must provide an implementation of
+        :meth:`~django.views.generic.FormMixin.form_valid` and
+        :meth:`~django.views.generic.FormMixin.form_invalid`.
+
+
+.. class:: django.views.generic.edit.ModelFormMixin
+
+    A form mixin that works on ModelForms, rather than a standalone form.
+
+    Since this is a subclass of
+    :class:`~django.views.generic.detail.SingleObjectMixin`, instances of this
+    mixin have access to the :attr:`~SingleObjectMixin.model` and
+    :attr:`~SingleObjectMixin.queryset` attributes, describing the type of
+    object that the ModelForm is manipulating. The view also provides
+    ``self.object``, the instance being manipulated. If the instance is being
+    created, ``self.object`` will be ``None``.
+
+    **Mixins**
+
+    * :class:`django.views.generic.edit.FormMixin`
+    * :class:`django.views.generic.detail.SingleObjectMixin`
+
+    **Methods and Attributes**
+
+    .. attribute:: success_url
+
+        The URL to redirect to when the form is successfully processed.
+
+        ``success_url`` may contain dictionary string formatting, which
+        will be interpolated against the object's field attributes. For
+        example, you could use ``success_url="/polls/%(slug)s/"`` to
+        redirect to a URL composed out of the ``slug`` field on a model.
+
+    .. method:: get_form_class()
+
+        Retrieve the form class to instantiate. If
+        :attr:`FormMixin.form_class` is provided, that class will be used.
+        Otherwise, a ModelForm will be instantiated using the model associated
+        with the :attr:`~SingleObjectMixin.queryset`, or with the
+        :attr:`~SingleObjectMixin.model`, depending on which attribute is
+        provided.
+
+    .. method:: get_form_kwargs()
+
+        Add the current instance (``self.object``) to the standard
+        :meth:`FormMixin.get_form_kwargs`.
+
+    .. method:: get_success_url()
+
+        Determine the URL to redirect to when the form is successfully
+        validated. Returns :attr:`ModelFormMixin.success_url` if it is provided;
+        otherwise, attempts to use the ``get_absolute_url()`` of the object.
+
+    .. method:: form_valid(form)
+
+        Saves the form instance, sets the current object for the view, and
+        redirects to
+        :meth:`~django.views.generic.edit.FormMixin.get_success_url`.
+
+    .. method:: form_invalid()
+
+        Renders a response, providing the invalid form as context.
+            
+.. class:: django.views.generic.edit.ProcessFormView
+
+    A mixin that provides basic HTTP GET and POST workflow.
+
+    .. note:: 
+
+        This is named 'ProcessFormView' and inherits directly from
+        :class:`django.views.generic.base.View`, but breaks if used
+        independently, so it is more of a mixin.
+
+    **Extends**
+
+    * :class:`django.views.generic.base.View`
+
+    **Methods and Attributes**
+
+    .. method:: get(request, *args, **kwargs)
+
+        Constructs a form, then renders a response using a context that
+        contains that form.
+
+    .. method:: post(request, *args, **kwargs)
+
+        Constructs a form, checks the form for validity, and handles it
+        accordingly.
+
+    The PUT action is also handled, as an analog of POST.
+
+.. class:: django.views.generic.edit.DeletionMixin
+
+    Enables handling of the ``DELETE`` http action.
+
+    **Methods and Attributes**
+
+    .. attribute:: success_url
+
+        The url to redirect to when the nominated object has been
+        successfully deleted.
+
+    .. method:: get_success_url(obj)
+
+        Returns the url to redirect to when the nominated object has been
+        successfully deleted. Returns
+        :attr:`~django.views.generic.edit.DeletionMixin.success_url` by
+        default.

+ 175 - 0
docs/ref/class-based-views/mixins-multiple-object.txt

@@ -0,0 +1,175 @@
+======================
+Multiple object mixins
+======================
+
+.. class:: django.views.generic.list.MultipleObjectMixin
+
+    A mixin that can be used to display a list of objects.
+
+    If ``paginate_by`` is specified, Django will paginate the results returned
+    by this. You can specify the page number in the URL in one of two ways:
+
+    * Use the ``page`` parameter in the URLconf. For example, this is what
+      your URLconf might look like::
+
+        (r'^objects/page(?P<page>[0-9]+)/$', PaginatedView.as_view())
+
+    * Pass the page number via the ``page`` query-string parameter. For
+      example, a URL would look like this::
+
+        /objects/?page=3
+
+    These values and lists are 1-based, not 0-based, so the first page would be
+    represented as page ``1``.
+
+    For more on pagination, read the :doc:`pagination documentation
+    </topics/pagination>`.
+
+    As a special case, you are also permitted to use ``last`` as a value for
+    ``page``::
+
+        /objects/?page=last
+
+    This allows you to access the final page of results without first having to
+    determine how many pages there are.
+
+    Note that ``page`` *must* be either a valid page number or the value
+    ``last``; any other value for ``page`` will result in a 404 error.
+
+    **Extends**
+
+    * :class:`django.views.generic.base.ContextMixin`
+
+    **Methods and Attributes**
+
+    .. attribute:: allow_empty
+
+        A boolean specifying whether to display the page if no objects are
+        available. If this is ``False`` and no objects are available, the view
+        will raise a 404 instead of displaying an empty page. By default, this
+        is ``True``.
+
+    .. attribute:: model
+
+        The model that this view will display data for. Specifying ``model
+        = Foo`` is effectively the same as specifying ``queryset =
+        Foo.objects.all()``.
+
+    .. attribute:: queryset
+
+        A ``QuerySet`` that represents the objects. If provided, the value of
+        :attr:`MultipleObjectMixin.queryset` supersedes the value provided for
+        :attr:`MultipleObjectMixin.model`.
+
+    .. attribute:: paginate_by
+
+        An integer specifying how many objects should be displayed per page. If
+        this is given, the view will paginate objects with
+        :attr:`MultipleObjectMixin.paginate_by` objects per page. The view will
+        expect either a ``page`` query string parameter (via ``GET``) or a
+        ``page`` variable specified in the URLconf.
+
+    .. attribute:: paginator_class
+
+       The paginator class to be used for pagination. By default,
+       :class:`django.core.paginator.Paginator` is used. If the custom paginator
+       class doesn't have the same constructor interface as
+       :class:`django.core.paginator.Paginator`, you will also need to
+       provide an implementation for :meth:`MultipleObjectMixin.get_paginator`.
+
+    .. attribute:: context_object_name
+
+        Designates the name of the variable to use in the context.
+
+    .. method:: get_queryset()
+
+        Returns the queryset that represents the data this view will display.
+
+    .. method:: paginate_queryset(queryset, page_size)
+
+        Returns a 4-tuple containing (``paginator``, ``page``, ``object_list``,
+        ``is_paginated``).
+
+        Constructed by paginating ``queryset`` into pages of size ``page_size``.
+        If the request contains a ``page`` argument, either as a captured URL
+        argument or as a GET argument, ``object_list`` will correspond to the
+        objects from that page.
+
+    .. method:: get_paginate_by(queryset)
+
+        Returns the number of items to paginate by, or ``None`` for no
+        pagination. By default this simply returns the value of
+        :attr:`MultipleObjectMixin.paginate_by`.
+
+    .. method:: get_paginator(queryset, per_page, orphans=0, allow_empty_first_page=True)
+
+        Returns an instance of the paginator to use for this view. By default,
+        instantiates an instance of :attr:`paginator_class`.
+
+    .. method:: get_allow_empty()
+
+        Return a boolean specifying whether to display the page if no objects
+        are available. If this method returns ``False`` and no objects are
+        available, the view will raise a 404 instead of displaying an empty
+        page. By default, this is ``True``.
+
+    .. method:: get_context_object_name(object_list)
+
+        Return the context variable name that will be used to contain
+        the list of data that this view is manipulating. If
+        ``object_list`` is a queryset of Django objects and
+        :attr:`~MultipleObjectMixin.context_object_name` is not set,
+        the context name will be the ``object_name`` of the model that
+        the queryset is composed from, with postfix ``'_list'``
+        appended. For example, the model ``Article`` would have a
+        context object named ``article_list``.
+
+    .. method:: get_context_data(**kwargs)
+
+        Returns context data for displaying the list of objects.
+
+    **Context**
+
+    * ``object_list``: The list of objects that this view is displaying. If
+      ``context_object_name`` is specified, that variable will also be set
+      in the context, with the same value as ``object_list``.
+
+    * ``is_paginated``: A boolean representing whether the results are
+      paginated. Specifically, this is set to ``False`` if no page size has
+      been specified, or if the available objects do not span multiple
+      pages.
+
+    * ``paginator``: An instance of
+      :class:`django.core.paginator.Paginator`. If the page is not
+      paginated, this context variable will be ``None``.
+
+    * ``page_obj``: An instance of
+      :class:`django.core.paginator.Page`. If the page is not paginated,
+      this context variable will be ``None``.
+
+
+.. class:: django.views.generic.list.MultipleObjectTemplateResponseMixin
+
+    A mixin class that performs template-based response rendering for views
+    that operate upon a list of object instances. Requires that the view it is
+    mixed with provides ``self.object_list``, the list of object instances that
+    the view is operating on. ``self.object_list`` may be, but is not required
+    to be, a :class:`~django.db.models.query.QuerySet`.
+
+    **Extends**
+
+    * :class:`~django.views.generic.base.TemplateResponseMixin`
+
+    **Methods and Attributes**
+
+    .. attribute:: template_name_suffix
+
+        The suffix to append to the auto-generated candidate template name.
+        Default suffix is ``_list``.
+
+    .. method:: get_template_names()
+
+        Returns a list of candidate template names. Returns the following list:
+
+        * the value of ``template_name`` on the view (if provided)
+        * ``<app_label>/<object_name><template_name_suffix>.html``

+ 60 - 0
docs/ref/class-based-views/mixins-simple.txt

@@ -0,0 +1,60 @@
+=============
+Simple mixins
+=============
+
+.. class:: django.views.generic.base.ContextMixin
+
+    .. versionadded:: 1.5
+
+    **classpath**
+
+    ``django.views.generic.base.ContextMixin``
+
+    **Methods**
+
+    .. method:: get_context_data(**kwargs)
+
+        Returns a dictionary representing the template context. The
+        keyword arguments provided will make up the returned context.
+
+.. class:: django.views.generic.base.TemplateResponseMixin
+
+    Provides a mechanism to construct a
+    :class:`~django.template.response.TemplateResponse`, given
+    suitable context. The template to use is configurable and can be
+    further customized by subclasses.
+
+    **Methods and Attributes**
+
+    .. attribute:: response_class
+
+        The response class to be returned by ``render_to_response`` method.
+        Default is
+        :class:`TemplateResponse <django.template.response.TemplateResponse>`.
+        The template and context of ``TemplateResponse`` instances can be
+        altered later (e.g. in
+        :ref:`template response middleware <template-response-middleware>`).
+
+        If you need custom template loading or custom context object
+        instantiation, create a ``TemplateResponse`` subclass and assign it to
+        ``response_class``.
+
+    .. method:: render_to_response(context, **response_kwargs)
+
+        Returns a ``self.response_class`` instance.
+
+        If any keyword arguments are provided, they will be
+        passed to the constructor of the response class.
+
+        Calls :meth:`~TemplateResponseMixin.get_template_names()` to obtain the
+        list of template names that will be searched looking for an existent
+        template.
+
+    .. method:: get_template_names()
+
+        Returns a list of template names to search for when rendering the
+        template.
+
+        If :attr:`TemplateResponseMixin.template_name` is specified, the
+        default implementation will return a list containing
+        :attr:`TemplateResponseMixin.template_name` (if it is specified).

+ 124 - 0
docs/ref/class-based-views/mixins-single-object.txt

@@ -0,0 +1,124 @@
+====================
+Single object mixins
+====================
+
+.. class:: django.views.generic.detail.SingleObjectMixin
+
+    Provides a mechanism for looking up an object associated with the
+    current HTTP request.
+
+    **Methods and Attributes**
+
+    .. attribute:: model
+
+        The model that this view will display data for. Specifying ``model
+        = Foo`` is effectively the same as specifying ``queryset =
+        Foo.objects.all()``.
+
+    .. attribute:: queryset
+
+        A ``QuerySet`` that represents the objects. If provided, the value of
+        :attr:`SingleObjectMixin.queryset` supersedes the value provided for
+        :attr:`SingleObjectMixin.model`.
+
+    .. attribute:: slug_field
+
+        The name of the field on the model that contains the slug. By default,
+        ``slug_field`` is ``'slug'``.
+
+    .. attribute:: slug_url_kwarg
+
+        .. versionadded:: 1.4
+
+        The name of the URLConf keyword argument that contains the slug. By
+        default, ``slug_url_kwarg`` is ``'slug'``.
+
+    .. attribute:: pk_url_kwarg
+
+        .. versionadded:: 1.4
+
+        The name of the URLConf keyword argument that contains the primary key.
+        By default, ``pk_url_kwarg`` is ``'pk'``.
+
+    .. attribute:: context_object_name
+
+        Designates the name of the variable to use in the context.
+
+    .. method:: get_object(queryset=None)
+
+        Returns the single object that this view will display. If
+        ``queryset`` is provided, that queryset will be used as the
+        source of objects; otherwise,
+        :meth:`~SingleObjectMixin.get_queryset` will be used.
+        ``get_object()`` looks for a
+        :attr:`SingleObjectMixin.pk_url_kwarg` argument in the arguments
+        to the view; if this argument is found, this method performs a
+        primary-key based lookup using that value. If this argument is not
+        found, it looks for a :attr:`SingleObjectMixin.slug_url_kwarg`
+        argument, and performs a slug lookup using the
+        :attr:`SingleObjectMixin.slug_field`.
+
+    .. method:: get_queryset()
+
+        Returns the queryset that will be used to retrieve the object that
+        this view will display. By default,
+        :meth:`~SingleObjectMixin.get_queryset` returns the value of the
+        :attr:`~SingleObjectMixin.queryset` attribute if it is set, otherwise
+        it constructs a :class:`QuerySet` by calling the `all()` method on the
+        :attr:`~SingleObjectMixin.model` attribute's default manager.
+
+    .. method:: get_context_object_name(obj)
+
+        Return the context variable name that will be used to contain the
+        data that this view is manipulating. If
+        :attr:`~SingleObjectMixin.context_object_name` is not set, the context
+        name will be constructed from the ``object_name`` of the model that
+        the queryset is composed from. For example, the model ``Article``
+        would have context object named ``'article'``.
+
+    .. method:: get_context_data(**kwargs)
+
+        Returns context data for displaying the list of objects.
+
+    **Context**
+
+    * ``object``: The object that this view is displaying. If
+      ``context_object_name`` is specified, that variable will also be
+      set in the context, with the same value as ``object``.
+
+.. class:: django.views.generic.detail.SingleObjectTemplateResponseMixin
+
+    A mixin class that performs template-based response rendering for views
+    that operate upon a single object instance. Requires that the view it is
+    mixed with provides ``self.object``, the object instance that the view is
+    operating on. ``self.object`` will usually be, but is not required to be,
+    an instance of a Django model. It may be ``None`` if the view is in the
+    process of constructing a new instance.
+
+    **Extends**
+
+    * :class:`~django.views.generic.base.TemplateResponseMixin`
+
+    **Methods and Attributes**
+
+    .. attribute:: template_name_field
+
+        The field on the current object instance that can be used to determine
+        the name of a candidate template. If either ``template_name_field``
+        itself or the value of the ``template_name_field`` on the current
+        object instance is ``None``, the object will not be used for a
+        candidate template name.
+
+    .. attribute:: template_name_suffix
+
+        The suffix to append to the auto-generated candidate template name.
+        Default suffix is ``_detail``.
+
+    .. method:: get_template_names()
+
+        Returns a list of candidate template names. Returns the following list:
+
+        * the value of ``template_name`` on the view (if provided)
+        * the contents of the ``template_name_field`` field on the
+          object instance that the view is operating upon (if available)
+        * ``<app_label>/<object_name><template_name_suffix>.html``

+ 14 - 0
docs/ref/class-based-views/mixins.txt

@@ -0,0 +1,14 @@
+========================
+Class-based views mixins
+========================
+
+Class-based views API reference. For introductory material, see :doc:`/topics/class-based-views/mixins`.
+
+.. toctree::
+   :maxdepth: 1
+
+   mixins-simple
+   mixins-single-object
+   mixins-multiple-object
+   mixins-editing
+   mixins-date-based

+ 1 - 1
docs/ref/index.txt

@@ -6,6 +6,7 @@ API Reference
    :maxdepth: 1
 
    authbackends
+   class-based-views/index   
    clickjacking
    contrib/index
    databases
@@ -13,7 +14,6 @@ API Reference
    exceptions
    files/index
    forms/index
-   class-based-views
    middleware
    models/index
    request-response

+ 1 - 1
docs/releases/1.3-alpha-1.txt

@@ -39,7 +39,7 @@ along with a completely generic view base class that can be used as
 the basis for reusable applications that can be easily extended.
 
 See :doc:`the documentation on Class-based Generic Views
-</topics/class-based-views>` for more details. There is also a document to
+</topics/class-based-views/index>` for more details. There is also a document to
 help you `convert your function-based generic views to class-based
 views <https://docs.djangoproject.com/en/1.4/topics/generic-views-migration/>`_.
 

+ 1 - 1
docs/releases/1.3.txt

@@ -80,7 +80,7 @@ provided, along with a completely generic view base class that can be
 used as the basis for reusable applications that can be easily
 extended.
 
-See :doc:`the documentation on class-based generic views</topics/class-based-views>`
+See :doc:`the documentation on class-based generic views</topics/class-based-views/index>`
 for more details. There is also a document to help you `convert
 your function-based generic views to class-based
 views <https://docs.djangoproject.com/en/1.4/topics/generic-views-migration/>`_.

+ 4 - 3
docs/topics/auth.txt

@@ -1450,9 +1450,10 @@ The permission_required decorator
 Limiting access to generic views
 --------------------------------
 
-To limit access to a :doc:`class-based generic view </ref/class-based-views>`,
-decorate the :meth:`View.dispatch <django.views.generic.base.View.dispatch>`
-method on the class. See :ref:`decorating-class-based-views` for details.
+To limit access to a :doc:`class-based generic view 
+</ref/class-based-views/index>`, decorate the :meth:`View.dispatch
+<django.views.generic.base.View.dispatch>` method on the class. See
+:ref:`decorating-class-based-views` for details.
 
 .. _permissions:
 

+ 0 - 624
docs/topics/class-based-views.txt

@@ -1,624 +0,0 @@
-=========================
-Class-based generic views
-=========================
-
-.. versionadded:: 1.3
-
-.. note::
-    Prior to Django 1.3, generic views were implemented as functions. The
-    function-based implementation has been removed in favor of the
-    class-based approach described here.
-
-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:
-
-* Perform common "simple" tasks: redirect to a different page and
-  render a given template.
-
-* 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.
-
-
-Simple usage
-============
-
-Class-based generic views (and any class-based views that inherit from
-the base classes Django provides) can be configured in two
-ways: subclassing, or passing in arguments directly in the URLconf.
-
-When you subclass a class-based view, you can 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 - :class:`~django.views.generic.base.TemplateView` -
-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. As the class-based
-views themselves are classes, we point the URL to the ``as_view`` class method
-instead, which is the entry point for class-based views::
-
-    # urls.py
-    from django.conf.urls import patterns, url, include
-    from some_app.views import AboutView
-
-    urlpatterns = patterns('',
-        (r'^about/', AboutView.as_view()),
-    )
-
-Alternatively, if you're only changing a few simple attributes on a
-class-based view, you can simply pass the new attributes into the ``as_view``
-method call itself::
-
-    from django.conf.urls import patterns, url, include
-    from django.views.generic import TemplateView
-
-    urlpatterns = patterns('',
-        (r'^about/', TemplateView.as_view(template_name="about.html")),
-    )
-
-A similar overriding pattern can be used for the ``url`` attribute on
-:class:`~django.views.generic.base.RedirectView`, another simple
-generic view.
-
-
-Generic views of objects
-========================
-
-:class:`~django.views.generic.base.TemplateView` 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 take a look at one of these generic views: the "object list" view. 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"]
-
-        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()
-
-To build a list page of all publishers, we'd use a URLconf along these lines::
-
-    from django.conf.urls import patterns, url, include
-    from django.views.generic import ListView
-    from books.models import Publisher
-
-    urlpatterns = patterns('',
-        (r'^publishers/$', ListView.as_view(
-            model=Publisher,
-        )),
-    )
-
-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 including a ``template_name`` key in the arguments to as_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 :class:`django.template.loaders.app_directories.Loader`
-    template loader is enabled in :setting:`TEMPLATE_LOADERS`, the template
-    location would be::
-
-        /path/to/project/books/templates/books/publisher_list.html
-
-.. highlightlang:: html+django
-
-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 "info" dictionary passed to the generic view. The
-:doc:`generic views reference</ref/class-based-views>` 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.
-
-
-Extending generic views
-=======================
-
-.. highlightlang:: python
-
-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.
-
-
-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 verbose name (or the plural verbose
-name, in the case of a list of objects) of the object being displayed.
-This is provided in addition to the default ``object_list`` entry, but
-contains exactly the same data.
-
-If the verbose name (or plural verbose name) 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. In this example, we'll override it in the
-URLconf, since it's a simple change:
-
-.. parsed-literal::
-
-    urlpatterns = patterns('',
-        (r'^publishers/$', ListView.as_view(
-            model=Publisher,
-            **context_object_name="publisher_list",**
-        )),
-    )
-
-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
-:class:`~django.views.generic.detail.DetailView` generic view provides
-the publisher to the context, but it seems there's no way to get
-additional information in that template.
-
-However, there is; you can subclass
-:class:`~django.views.generic.detail.DetailView` and provide your own
-implementation of the ``get_context_data`` method. The default
-implementation of this that comes with
-:class:`~django.views.generic.detail.DetailView` simply adds in the
-object being displayed to the template, but you can override it to show
-more::
-
-    from django.views.generic import DetailView
-    from books.models import Publisher, Book
-
-    class PublisherDetailView(DetailView):
-
-        context_object_name = "publisher"
-        model = Publisher
-
-        def get_context_data(self, **kwargs):
-            # Call the base implementation first to get a context
-            context = super(PublisherDetailView, 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 explictly set it after super if they
-    want to be sure to override all parents.
-
-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, Book
-
-    class PublisherDetailView(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 :doc:`/topics/db/queries`
-for more information about :class:`QuerySet` objects, and see the
-:doc:`class-based views reference </ref/class-based-views>` 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::
-
-    urlpatterns = patterns('',
-        (r'^publishers/$', ListView.as_view(
-            queryset=Publisher.objects.all(),
-            context_object_name="publisher_list",
-        )),
-        (r'^books/$', ListView.as_view(
-            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 (here, illustrated using subclassing rather than by passing arguments
-in the URLconf)::
-
-    from django.views.generic import ListView
-    from books.models import Book
-
-    class AcmeBookListView(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
-    :doc:`class-based-views reference</ref/class-based-views>` 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 arbitrary
-publisher?
-
-Handily, the ``ListView`` has a
-:meth:`~django.views.generic.detail.ListView.get_queryset` 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::
-
-    from books.views import PublisherBookListView
-
-    urlpatterns = patterns('',
-        (r'^books/(\w+)/$', PublisherBookListView.as_view()),
-    )
-
-Next, we'll write the ``PublisherBookListView`` view itself::
-
-    from django.shortcuts import get_object_or_404
-    from django.views.generic import ListView
-    from books.models import Book, Publisher
-
-    class PublisherBookListView(ListView):
-
-        context_object_name = "book_list"
-        template_name = "books/books_by_publisher.html"
-
-        def get_queryset(self):
-            publisher = get_object_or_404(Publisher, name__iexact=self.args[0])
-            return Book.objects.filter(publisher=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::
-
-    class PublisherBookListView(ListView):
-
-        context_object_name = "book_list"
-        template_name = "books/books_by_publisher.html"
-
-        def get_queryset(self):
-            self.publisher = get_object_or_404(Publisher, name__iexact=self.args[0])
-            return Book.objects.filter(publisher=self.publisher)
-
-        def get_context_data(self, **kwargs):
-            # Call the base implementation first to get a context
-            context = super(PublisherBookListView, 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`` object that we were
-using to keep track of the last time anybody looked at that author::
-
-    # models.py
-
-    class Author(models.Model):
-        salutation = models.CharField(max_length=10)
-        first_name = models.CharField(max_length=30)
-        last_name = models.CharField(max_length=40)
-        email = models.EmailField()
-        headshot = models.ImageField(upload_to='/tmp')
-        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:
-
-.. parsed-literal::
-
-    from books.views import AuthorDetailView
-
-    urlpatterns = patterns('',
-        #...
-        **(r'^authors/(?P<pk>\\d+)/$', AuthorDetailView.as_view()),**
-    )
-
-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::
-
-    import datetime
-    from books.models import Author
-    from django.views.generic import DetailView
-    from django.shortcuts import get_object_or_404
-
-    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 = datetime.datetime.now()
-            object.save()
-            # Return the object
-            return object
-
-.. note::
-
-    This code won't actually work unless you create a
-    ``books/author_detail.html`` template.
-
-.. 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 change it, you'll need to do your own ``get()`` call
-    on ``self.queryset`` using the new named parameter from ``self.kwargs``.
-
-More than just HTML
--------------------
-
-So far, we've been focusing on rendering templates to generate
-responses. However, that's not all generic views can do.
-
-Each generic view is composed out of a series of mixins, and each
-mixin contributes a little piece of the entire view. Some of these
-mixins -- such as
-:class:`~django.views.generic.base.TemplateResponseMixin` -- are
-specifically designed for rendering content to an HTML response using a
-template. However, you can write your own mixins that perform
-different rendering behavior.
-
-For example, a simple JSON mixin might look something like this::
-
-    import json
-    from django import http
-
-    class JSONResponseMixin(object):
-        def render_to_response(self, context):
-            "Returns a JSON response containing 'context' as payload"
-            return self.get_json_response(self.convert_context_to_json(context))
-
-        def get_json_response(self, content, **httpresponse_kwargs):
-            "Construct an `HttpResponse` object."
-            return http.HttpResponse(content,
-                                     content_type='application/json',
-                                     **httpresponse_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)
-
-Then, you could build a JSON-returning
-:class:`~django.views.generic.detail.DetailView` by mixing your
-:class:`JSONResponseMixin` with the
-:class:`~django.views.generic.detail.BaseDetailView` -- (the
-:class:`~django.views.generic.detail.DetailView` before template
-rendering behavior has been mixed in)::
-
-    class JSONDetailView(JSONResponseMixin, BaseDetailView):
-        pass
-
-This view can then be deployed in the same way as any other
-:class:`~django.views.generic.detail.DetailView`, with exactly the
-same behavior -- except for the format of the response.
-
-If you want to be really adventurous, you could even mix a
-:class:`~django.views.generic.detail.DetailView` 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 :class:`JSONResponseMixin` and a
-:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`,
-and override the implementation of :func:`render_to_response()` to defer
-to the appropriate subclass depending on the type of response that the user
-requested::
-
-    class HybridDetailView(JSONResponseMixin, SingleObjectTemplateResponseMixin, BaseDetailView):
-        def render_to_response(self, context):
-            # Look for a 'format=json' GET argument
-            if self.request.GET.get('format','html') == 'json':
-                return JSONResponseMixin.render_to_response(self, context)
-            else:
-                return SingleObjectTemplateResponseMixin.render_to_response(self, context)
-
-Because of the way that Python resolves method overloading, the local
-``render_to_response()`` implementation will override the versions provided by
-:class:`JSONResponseMixin` and
-:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`.
-
-Decorating class-based views
-============================
-
-.. highlightlang:: python
-
-The extension of class-based views isn't limited to using mixins. You
-can use also use decorators.
-
-Decorating in URLconf
----------------------
-
-The simplest way of decorating class-based views is to decorate the
-result of the :meth:`~django.views.generic.base.View.as_view` method.
-The easiest place to do this is in the URLconf where you deploy your
-view::
-
-    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-class-based-views:
-
-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
-:meth:`~django.views.generic.base.View.dispatch` 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.

+ 432 - 0
docs/topics/class-based-views/generic-display.txt

@@ -0,0 +1,432 @@
+.. _Generic views:
+
+=========================
+Class-based generic views
+=========================
+
+.. note::
+    Prior to Django 1.3, generic views were implemented as functions. The
+    function-based implementation has been removed in favor of the
+    class-based approach described here.
+
+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:
+
+* 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
+========================
+
+:class:`~django.views.generic.base.TemplateView` 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.
+
+.. comment: link here to the other topic pages (form handling, date based, mixins)
+
+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"]
+
+        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, include
+    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
+    :class:`django.template.loaders.app_directories.Loader` template loader is
+    enabled in :setting:`TEMPLATE_LOADERS`, a template location could be:
+    /path/to/project/books/templates/books/publisher_list.html
+
+.. highlightlang:: html+django
+
+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
+:doc:`generic views reference</ref/class-based-views/index>` 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.
+
+
+Making "friendly" template contexts
+-----------------------------------
+
+.. highlightlang:: python
+
+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 the 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
+:class:`~django.views.generic.detail.DetailView` generic view provides
+the publisher to the context, but how do we get additional information
+in that template.
+
+However, there is; you can subclass
+:class:`~django.views.generic.detail.DetailView` and provide your own
+implementation of the ``get_context_data`` method. The default
+implementation of this that comes with
+:class:`~django.views.generic.detail.DetailView` simply adds in 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(PublisherDetailView, 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 explictly 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.
+
+.. _generic-views-list-subsets:
+
+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, Book
+
+    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 :doc:`/topics/db/queries`
+for more information about :class:`QuerySet` objects, and see the
+:doc:`class-based views reference </ref/class-based-views/index>` 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 AcmeBookListView(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
+    :doc:`class-based-views reference</ref/class-based-views/index>` 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 arbitrary
+publisher?
+
+Handily, the ``ListView`` has a
+:meth:`~django.views.generic.detail.ListView.get_queryset` 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 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(PublisherBookListView, self).get_context_data(**kwargs)
+            # Add in the publisher
+            context['publisher'] = self.publisher
+            return context
+
+.. _generic-views-extra-work:
+
+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`` object that we were
+using to keep track of the last time anybody looked at that author::
+
+    # models.py
+
+    class Author(models.Model):
+        salutation = models.CharField(max_length=10)
+        name = models.CharField(max_length=200)
+        email = models.EmailField()
+        headshot = models.ImageField(upload_to='/tmp')
+        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 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.shortcuts import get_object_or_404
+    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
+    :class:`~django.views.generic.detail.DetailView`

+ 205 - 0
docs/topics/class-based-views/generic-editing.txt

@@ -0,0 +1,205 @@
+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 :ref:`Using a form in a
+view<using-a-form-in-a-view>`). To help avoid this, Django provides a
+collection of generic class-based views for form processing.
+
+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
+  :class:`~django.views.generic.base.TemplateResponseMixin` so
+  :attr:`~django.views.generic.base.TemplateResponseMixin.template_name`
+  can be used here
+* The default implementation for
+  :meth:`~django.views.generic.edit.FormView.form_valid` simply
+  redirects to the :attr:`success_url`
+
+Model Forms
+-----------
+
+Generic views really shine when working with models.  These generic
+views will automatically create a :class:`ModelForm`, so long as they
+can work out which model class to use:
+
+* If the :attr:`model` attribute is given, that model class will be used
+* If :meth:`get_object()` returns an object, the class of that object
+  will be used
+* If a :attr:`queryset` is given, the model for that queryset will be used
+
+Model form views provide a :meth:`form_valid()` 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 attr:`success_url` for
+:class:`~django.views.generic.edit.CreateView` or
+:class:`~django.views.generic.edit.UpdateView` - they will use
+:meth:`get_absolute_url()` on the model object if available.
+
+If you want to use a custom :class:`ModelForm` (for instance to add
+extra validation) simply set
+:attr:`~django.views.generic.edit.FormMixin.form_class` on your view.
+
+.. note::
+    When specifying a custom form class, you must still specify the model,
+    even though the :attr:`form_class` may be a :class:`ModelForm`.
+
+First we need to add :meth:`get_absolute_url()` to our :class:`Author`
+class:
+
+.. code-block:: python
+
+    # models.py
+    from django import models
+    from django.core.urlresolvers import reverse
+
+    class Author(models.Model):
+        name = models.CharField(max_length=200)
+
+        def get_absolute_url(self):
+            return reverse('author-detail', kwargs={'pk': self.pk})
+
+Then we can use :class:`CreateView` 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
+
+    class AuthorUpdate(UpdateView):
+        model = Author
+
+    class AuthorDelete(DeleteView):
+        model = Author
+        success_url = reverse_lazy('author-list')
+
+.. note::
+    We have to use :func:`~django.core.urlresolvers.reverse_lazy` here, not
+    just ``reverse`` as the urls are not loaded when the file is imported.
+
+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'),
+    )
+        
+.. note::
+
+    These views inherit :class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`
+    which uses :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_prefix`
+    to construct the
+    :attr:`~django.views.generic.base.TemplateResponseMixin.template_name`
+    based on the model.
+
+    In this example:
+
+    * :class:`CreateView` and :class:`UpdateView` use ``myapp/author_form.html``
+    * :class:`DeleteView` uses ``myapp/author_confirm_delete.html``
+
+    If you wish to have separate templates for :class:`CreateView` and
+    :class:1UpdateView`, you can set either :attr:`template_name` or
+    :attr:`template_name_suffix` on your view class.
+
+Models and request.user
+-----------------------
+
+To track the user that created an object using a :class:`CreateView`,
+you can use a custom :class:`ModelForm` to do this. First, add the
+foreign key relation to the model::
+
+    # models.py
+    from django import models
+    from django.contrib.auth import User
+
+    class Author(models.Model):
+        name = models.CharField(max_length=200)
+        created_by = models.ForeignKey(User)
+
+        # ...
+
+Create a custom :class:`ModelForm` in order to exclude the
+``created_by`` field and prevent the user from editing it:
+
+.. code-block:: python
+
+    # forms.py
+    from django import forms
+    from myapp.models import Author
+    
+    class AuthorForm(forms.ModelForm):
+        class Meta:
+            model = Author
+            exclude = ('created_by',)
+
+In the view, use the custom :attr:`form_class` and override
+:meth:`form_valid()` to add the user::
+
+    # views.py
+    from django.views.generic.edit import CreateView
+    from myapp.models import Author
+    from myapp.forms import AuthorForm
+    
+    class AuthorCreate(CreateView):
+        form_class = AuthorForm
+        model = Author
+
+        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 :ref:`decorate this
+view<decorating-class-based-views>` using
+:func:`~django.contrib.auth.decorators.login_required`, or
+alternatively handle unauthorised users in the :meth:`form_valid()`.

+ 233 - 0
docs/topics/class-based-views/index.txt

@@ -0,0 +1,233 @@
+=================
+Class-based views
+=================
+
+.. versionadded:: 1.3
+
+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
+:doc:`class-based views reference
+documentation</ref/class-based-views/index>`.
+
+.. toctree::
+   :maxdepth: 1
+
+   generic-display
+   generic-editing
+   mixins
+
+Basic examples
+==============
+
+Django provides base view classes which will suit a wide range of applications.
+All views inherit from the :class:`~django.views.generic.base.View` class, which
+handles linking the view in to the URLs, HTTP method dispatching and other
+simple features. :class:`~django.views.generic.base.RedirectView` is for a simple HTTP
+redirect, and :class:`~django.views.generic.base.TemplateView` extends the base class
+to make it also render a template.
+
+
+Simple usage
+============
+
+Class-based generic views (and any class-based views that inherit from
+the base classes Django provides) can be configured in two
+ways: subclassing, or passing in arguments directly in the URLconf.
+
+When you subclass a class-based view, you can 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 - :class:`~django.views.generic.base.TemplateView` -
+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. As the class-based
+views themselves are classes, we point the URL to the ``as_view`` class method
+instead, which is the entry point for class-based views::
+
+    # urls.py
+    from django.conf.urls import patterns, url, include
+    from some_app.views import AboutView
+
+    urlpatterns = patterns('',
+        (r'^about/', AboutView.as_view()),
+    )
+
+Alternatively, if you're only changing a few simple attributes on a
+class-based view, you can simply pass the new attributes into the ``as_view``
+method call itself::
+
+    from django.conf.urls import patterns, url, include
+    from django.views.generic import TemplateView
+
+    urlpatterns = patterns('',
+        (r'^about/', TemplateView.as_view(template_name="about.html")),
+    )
+
+A similar overriding pattern can be used for the ``url`` attribute on
+:class:`~django.views.generic.base.RedirectView`.
+
+.. _jsonresponsemixin-example:
+
+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 use 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::
+
+    import json
+    from django import http
+
+    class JSONResponseMixin(object):
+        """
+        A mixin that can be used to render a JSON response.
+        """
+        reponse_class = HTTPResponse
+
+        def render_to_response(self, context, **response_kwargs):
+            """
+            Returns a JSON response, transforming 'context' to make the payload.
+            """
+            response_kwargs['content_type'] = 'application/json'
+            return self.response_class(
+                self.convert_context_to_json(context),
+                **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)
+
+Now we mix this into the base view::
+
+    from django.views.generic import View
+
+    class JSONView(JSONResponseMixin, View):
+        pass
+
+Equally we could use our mixin with one of the generic views. We can make our
+own version of :class:`~django.views.generic.detail.DetailView` by mixing
+:class:`JSONResponseMixin` with the
+:class:`~django.views.generic.detail.BaseDetailView` -- (the
+:class:`~django.views.generic.detail.DetailView` before template
+rendering behavior has been mixed in)::
+
+    class JSONDetailView(JSONResponseMixin, BaseDetailView):
+        pass
+
+This view can then be deployed in the same way as any other
+:class:`~django.views.generic.detail.DetailView`, with exactly the
+same behavior -- except for the format of the response.
+
+If you want to be really adventurous, you could even mix a
+:class:`~django.views.generic.detail.DetailView` 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 :class:`JSONResponseMixin` and a
+:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`,
+and override the implementation of :func:`render_to_response()` to defer
+to the appropriate subclass depending on the type of response that the user
+requested::
+
+    class HybridDetailView(JSONResponseMixin, SingleObjectTemplateResponseMixin, BaseDetailView):
+        def render_to_response(self, context):
+            # Look for a 'format=json' GET argument
+            if self.request.GET.get('format','html') == 'json':
+                return JSONResponseMixin.render_to_response(self, context)
+            else:
+                return SingleObjectTemplateResponseMixin.render_to_response(self, context)
+
+Because of the way that Python resolves method overloading, the local
+``render_to_response()`` implementation will override the versions provided by
+:class:`JSONResponseMixin` and
+:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`.
+
+For more information on how to use the built in generic views, consult the next
+topic on :doc:`generic class based views</topics/class-based-views/generic-display>`.
+
+Decorating class-based views
+============================
+
+.. highlightlang:: python
+
+The extension of class-based views isn't limited to using mixins. You
+can use also use decorators.
+
+Decorating in URLconf
+---------------------
+
+The simplest way of decorating class-based views is to decorate the
+result of the :meth:`~django.views.generic.base.View.as_view` method.
+The easiest place to do this is in the URLconf where you deploy your
+view::
+
+    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-class-based-views:
+
+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
+:meth:`~django.views.generic.base.View.dispatch` 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.

+ 605 - 0
docs/topics/class-based-views/mixins.txt

@@ -0,0 +1,605 @@
+===================================
+Using mixins with class-based views
+===================================
+
+.. versionadded:: 1.3
+
+.. caution::
+
+    This is an advanced topic. A working knowledge of :doc:`Django's
+    class-based views<index>` 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
+:class:`~django.views.generic.base.TemplateView`; perhaps you need to
+render a template only on `POST`, with `GET` doing something else
+entirely. While you could use
+:class:`~django.template.response.TemplateResponse` 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
+:class:`~django.views.generic.base.TemplateResponseMixin`. The Django
+reference documentation contains :doc:`full documentation of all the
+mixins</ref/class-based-views/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.
+
+:class:`~django.views.generic.base.TemplateResponseMixin`
+    Every built in view which returns a
+    :class:`~django.template.response.TemplateResponse` will call the
+    :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
+    method that :class:`TemplateResponseMixin` provides. Most of the time this
+    will be called for you (for instance, it is called by the ``get()`` method
+    implemented by both :class:`~django.views.generic.base.TemplateView` and
+    :class:`~django.views.generic.base.DetailView`); 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 :ref:`JSONResponseMixin example
+    <jsonresponsemixin-example>`.
+
+    ``render_to_response`` itself calls
+    :meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`,
+    which by default will just look up
+    :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` on
+    the class-based view; two other mixins
+    (:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`
+    and
+    :class:`~django.views.generic.list.MultipleObjectTemplateResponseMixin`)
+    override this to provide more flexible defaults when dealing with actual
+    objects.
+
+.. versionadded:: 1.5
+
+:class:`~django.views.generic.base.ContextMixin`
+    Every built in view which needs context data, such as for rendering a
+    template (including :class:`TemplateResponseMixin` above), should call
+    :meth:`~django.views.generic.base.ContextMixin.get_context_data` passing
+    any data they want to ensure is in there as keyword arguments.
+    ``get_context_data`` returns a dictionary; in :class:`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
+:class:`~django.views.generic.detail.DetailView`, which renders a
+"detail" view of an object, and
+:class:`~django.views.generic.list.ListView`, which will render a list
+of 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
+(:class:`~django.views.generic.edit.FormView`, and the model-specific
+views :class:`~django.views.generic.edit.CreateView`,
+:class:`~django.views.generic.edit.UpdateView` and
+:class:`~django.views.generic.edit.DeleteView`), and in the
+date-based generic views. These are
+covered in the :doc:`mixin reference
+documentation</ref/class-based-views/mixins>`.
+
+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
+:class:`TemplateResponse` with a suitable template, and that object as
+context.
+
+To get the object, :class:`~django.views.generic.detail.DetailView`
+relies on :class:`~django.views.generic.detail.SingleObjectMixin`,
+which provides a
+:meth:`~django.views.generic.detail.SingleObjectMixin.get_object`
+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
+:attr:`~django.views.generic.detail.SingleObjectMixin.model` attribute
+on the view, or the
+:attr:`~django.views.generic.detail.SingleObjectMixin.queryset`
+attribute if that's provided). :class:`SingleObjectMixin` also overrides
+:meth:`~django.views.generic.base.ContextMixin.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 :class:`TemplateResponse`, :class:`DetailView` uses
+:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`,
+which extends
+:class:`~django.views.generic.base.TemplateResponseMixin`, overriding
+:meth:`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>/<object_name>_detail.html``. The ``_detail`` part can be
+changed by setting
+:attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_suffix`
+on a subclass to something else. (For instance, the :doc:`generic edit
+views<generic-editing>` 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 :class:`QuerySet`, and then we need
+to make a :class:`TemplateResponse` with a suitable template using
+that list of objects.
+
+To get the objects, :class:`~django.views.generic.list.ListView` uses
+:class:`~django.views.generic.list.MultipleObjectMixin`, which
+provides both
+:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`
+and
+:meth:`~django.views.generic.list.MultipleObjectMixin.paginate_queryset`. Unlike
+with :class:`SingleObjectMixin`, 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
+:attr:`~django.views.generic.list.MultipleObjectMixin.queryset` or
+:attr:`~django.views.generic.list.MultipleObjectMixin.model` attribute
+on the view class. A common reason to override
+:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`
+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.
+
+:class:`MultipleObjectMixin` also overrides
+:meth:`~django.views.generic.base.ContextMixin.get_context_data` 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 :class:`ListView` arranges for
+it.
+
+To make a :class:`TemplateResponse`, :class:`ListView` then uses
+:class:`~django.views.generic.list.MultipleObjectTemplateResponseMixin`;
+as with :class:`SingleObjectTemplateResponseMixin` above, this
+overrides :meth:`get_template_names()` to provide :meth:`a range of
+options
+<~django.views.generic.list.MultipleObjectTempalteResponseMixin>`,
+with the most commonly-used being
+``<app_label>/<object_name>_list.html``, with the ``_list`` part again
+being taken from the
+:attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix`
+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 :doc:`class-based
+    views</ref/class-based-views/index>` and :doc:`class-based view
+    mixins</ref/class-based-views/mixins>` 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
+    :class:`View` or :class:`TemplateView`, perhaps with
+    :class:`SimpleObjectMixin` and
+    :class:`MultipleObjectMixin`. 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.)
+
+.. _method resolution order: http://www.python.org/download/releases/2.3/mro/
+
+
+Using SingleObjectMixin with View
+---------------------------------
+
+If we want to write a simple class-based view that responds only to
+``POST``, we'll subclass :class:`~django.views.generic.base.View` 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
+:class:`~django.views.generic.detail.SingleObjectMixin`.
+
+We'll demonstrate this with the publisher modelling we used in the
+:doc:`generic class-based views
+introduction<generic-display>`.
+
+.. code-block:: python
+
+    # 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(View, SingleObjectMixin):
+        """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.
+            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
+:class:`SingleObjectMixin` 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:
+
+.. code-block:: python
+
+    # urls.py
+    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
+:meth:`~django.views.generic.detail.SingleObjectMixin.get_object` uses
+to look up the :class:`Author` instance. You could also use a slug, or
+any of the other features of :class:`SingleObjectMixin`.
+
+Using SingleObjectMixin with ListView
+-------------------------------------
+
+:class:`~django.views.generic.list.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 :class:`ListView` with
+:class:`SingleObjectMixin`, 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:
+
+**Publisher queryset for use in get_object**
+    We'll set that up directly when we call :meth:`get_object()`.
+
+**Book queryset for use by ListView**
+    We'll figure that out ourselves in :meth:`get_queryset()` so we
+    can take into account the Publisher we're looking at.
+
+.. highlightlang:: python
+
+.. note::
+
+    We have to think carefully about :meth:`get_context_data()`.
+    Since both :class:`SingleObjectMixin` and :class:`ListView` will
+    put things in the context data under the value of
+    :attr:`context_object_name` if it's set, we'll instead explictly
+    ensure the Publisher is in the context data. :class:`ListView`
+    will add in the suitable ``page_obj`` and ``paginator`` for us
+    providing we remember to call ``super()``.
+
+Now we can write a new :class:`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"
+    
+        def get_context_data(self, **kwargs):
+            kwargs['publisher'] = self.object
+            return super(PublisherDetail, self).get_context_data(**kwargs)
+    
+        def get_queryset(self):
+            self.object = self.get_object(Publisher.objects.all())
+            return self.object.book_set.all()
+
+Notice how we set ``self.object`` within :meth:`get_queryset` so we
+can use it again later in :meth:`get_context_data`. If you don't set
+:attr:`template_name`, the template will default to the normal
+:class:`ListView` choice, which in this case would be
+``"books/book_list.html"`` because it's a list of books;
+:class:`ListView` knows nothing about :class:`SingleObjectMixin`, so
+it doesn't have any clue this view is anything to do with a Publisher.
+
+.. highlightlang:: html+django
+
+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
+:class:`~django.views.generic.base.TemplateResponseMixin` and
+:class:`~django.views.generic.detail.SingleObjectMixin` when you need
+their functionality. As shown above, with a bit of care you can even
+combine :class:`SingleObjectMixin` with
+:class:`~django.views.generic.list.ListView`. However 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: :doc:`detail,
+    list<generic-display>`, :doc:`editing<generic-editing>` and
+    date. For example it's fine to combine
+    :class:`TemplateView` (built in view) with
+    :class:`MultipleObjectMixin` (generic list), but you're likely to
+    have problems combining :class:`SingleObjectMixin` (generic
+    detail) with :class:`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
+:class:`~django.views.generic.detail.DetailView` with
+:class:`~django.views.generic.edit.FormMixin` to enable use to
+``POST`` a Django :class:`Form` to the same URL as we're displaying an
+object using :class:`DetailView`.
+
+Using FormMixin with DetailView
+-------------------------------
+
+Think back to our earlier example of using :class:`View` and
+:class:`SingleObjectMixin` 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 :class:`Form` 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 :class:`AuthorDetailView` to do that.
+
+.. _REST: http://en.wikipedia.org/wiki/Representational_state_transfer
+
+We'll keep the ``GET`` handling from :class:`DetailView`, although
+we'll have to add a :class:`Form` into the context data so we can
+render it in the template. We'll also want to pull in form processing
+from :class:`~django.views.generic.edit.FormMixin`, and write a bit of
+code so that on ``POST`` the form gets called appropriately.
+
+.. note::
+
+    We use :class:`FormMixin` and implement :meth:`post()` ourselves
+    rather than try to mix :class:`DetailView` with :class:`FormView`
+    (which provides a suitable :meth:`post()` already) because both of
+    the views implement :meth:`get()`, and things would get much more
+    confusing.
+
+Our new :class:`AuthorDetail` looks like this:
+
+.. code-block:: python
+
+    # 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
+
+    class AuthorInterestForm(forms.Form):
+        message = forms.CharField()
+
+    class AuthorDetail(DetailView, FormMixin):
+        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):
+            form_class = self.get_form_class()
+            form = self.get_form(form_class)
+            context = {
+                'form': form
+            }
+            context.update(kwargs)
+            return super(AuthorDetail, self).get_context_data(**context)
+    
+        def post(self, request, *args, **kwargs):
+            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):
+            if not self.request.user.is_authenticated():
+                return HttpResponseForbidden()
+            self.object = self.get_object()
+            # record the interest using the message in form.cleaned_data
+            return super(AuthorDetail, self).form_valid(form)
+    
+:meth:`get_success_url()` is just providing somewhere to redirect to,
+which gets used in the default implementation of
+:meth:`form_valid()`. We have to provide our own :meth:`post()` as
+noted earlier, and override :meth:`get_context_data()` to make the
+:class:`Form` available in the context data.
+
+A better solution
+-----------------
+
+It should be obvious that the number of subtle interactions between
+:class:`FormMixin` and :class:`DetailView` 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 :meth:`post()`
+method yourself, keeping :class:`DetailView` as the only generic
+functionality, although writing :class:`Form` 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
+:class:`~django.views.generic.edit.FormView` distinct from
+:class:`DetailView` 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
+:class:`DetailView` (with the :class:`Form` added to the context
+data), and ``POST`` requests should get the :class:`FormView`. Let's
+set up those views first.
+
+The :class:`AuthorDisplay` view is almost the same as :ref:`when we
+first introduced AuthorDetail<generic-views-extra-work>`; we have to
+write our own :meth:`get_context_data()` to make the
+:class:`AuthorInterestForm` available to the template. We'll skip the
+:meth:`get_object()` override from before for clarity.
+
+.. code-block:: python
+
+    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):
+    
+        queryset = Author.objects.all()
+    
+        def get_context_data(self, **kwargs):
+            context = {
+                'form': AuthorInterestForm(),
+            }
+            context.update(kwargs)
+            return super(AuthorDisplay, self).get_context_data(**context)
+    
+Then the :class:`AuthorInterest` is a simple :class:`FormView`, but we
+have to bring in :class:`SingleObjectMixin` so we can find the author
+we're talking about, and we have to remember to set
+:attr:`template_name` to ensure that form errors will render the same
+template as :class:`AuthorDisplay` is using on ``GET``.
+
+.. code-block:: python
+
+    from django.views.generic import FormView
+    from django.views.generic.detail import SingleObjectMixin
+    
+    class AuthorInterest(FormView, SingleObjectMixin):
+        template_name = 'books/author_detail.html'
+        form_class = AuthorInterestForm
+        model = Author
+
+        def get_context_data(self, **kwargs):
+            context = {
+                'object': self.get_object(),
+            }
+            return super(AuthorInterest, self).get_context_data(**context)
+    
+        def get_success_url(self):
+            return reverse(
+                'author-detail',
+                kwargs = {'pk': self.object.pk},
+            )
+    
+        def form_valid(self, form):
+            if not self.request.user.is_authenticated():
+                return HttpResponseForbidden()
+            self.object = self.get_object()
+            # record the interest using the message in form.cleaned_data
+            return super(AuthorInterest, self).form_valid(form)
+
+Finally we bring this together in a new :class:`AuthorDetail` view. We
+already know that calling :meth:`as_view()` 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 :meth:`as_view()`
+in the same way you would in your URLconf, such as if you wanted the
+:class:`AuthorInterest` behaviour to also appear at another URL but
+using a different template.
+
+.. code-block:: python
+
+    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
+:class:`View` or :class:`TemplateView`, as it keeps the different
+views as separate as possible.

+ 2 - 0
docs/topics/forms/index.txt

@@ -82,6 +82,8 @@ If your form is going to be used to directly add or edit a Django model, you can
 use a :doc:`ModelForm </topics/forms/modelforms>` to avoid duplicating your model
 description.
 
+.. _using-a-form-in-a-view:
+
 Using a form in a view
 ----------------------
 

+ 1 - 1
docs/topics/http/generic-views.txt

@@ -2,4 +2,4 @@
 Generic views
 =============
 
-See :doc:`/ref/class-based-views`.
+See :doc:`/ref/class-based-views/index`.

+ 1 - 1
docs/topics/index.txt

@@ -11,7 +11,7 @@ Introductions to all the key parts of Django you'll need to know:
    http/index
    forms/index
    templates
-   class-based-views
+   class-based-views/index
    files
    testing
    auth