From c4c7fbcc0d9264beb931b45969fc0d8d655c4f83 Mon Sep 17 00:00:00 2001 From: Jannis Leidel Date: Mon, 11 Jun 2012 10:34:00 +0200 Subject: [PATCH] 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. --- AUTHORS | 5 + django/views/generic/base.py | 9 +- django/views/generic/dates.py | 14 +- django/views/generic/detail.py | 16 +- django/views/generic/edit.py | 39 +- django/views/generic/list.py | 9 + docs/glossary.txt | 2 +- docs/howto/static-files.txt | 2 +- docs/index.txt | 7 +- docs/internals/deprecation.txt | 4 +- docs/intro/tutorial04.txt | 2 +- docs/misc/api-stability.txt | 2 +- docs/ref/class-based-views.txt | 1367 ----------------- docs/ref/class-based-views/base.txt | 224 +++ .../class-based-views/generic-date-based.txt | 273 ++++ .../ref/class-based-views/generic-display.txt | 86 ++ .../ref/class-based-views/generic-editing.txt | 78 + docs/ref/class-based-views/index.txt | 59 + .../class-based-views/mixins-date-based.txt | 256 +++ docs/ref/class-based-views/mixins-editing.txt | 183 +++ .../mixins-multiple-object.txt | 175 +++ docs/ref/class-based-views/mixins-simple.txt | 60 + .../mixins-single-object.txt | 124 ++ docs/ref/class-based-views/mixins.txt | 14 + docs/ref/index.txt | 2 +- docs/releases/1.3-alpha-1.txt | 2 +- docs/releases/1.3.txt | 2 +- docs/topics/auth.txt | 7 +- docs/topics/class-based-views.txt | 624 -------- .../class-based-views/generic-display.txt | 432 ++++++ .../class-based-views/generic-editing.txt | 205 +++ docs/topics/class-based-views/index.txt | 233 +++ docs/topics/class-based-views/mixins.txt | 605 ++++++++ docs/topics/forms/index.txt | 2 + docs/topics/http/generic-views.txt | 2 +- docs/topics/index.txt | 2 +- 36 files changed, 3112 insertions(+), 2016 deletions(-) delete mode 100644 docs/ref/class-based-views.txt create mode 100644 docs/ref/class-based-views/base.txt create mode 100644 docs/ref/class-based-views/generic-date-based.txt create mode 100644 docs/ref/class-based-views/generic-display.txt create mode 100644 docs/ref/class-based-views/generic-editing.txt create mode 100644 docs/ref/class-based-views/index.txt create mode 100644 docs/ref/class-based-views/mixins-date-based.txt create mode 100644 docs/ref/class-based-views/mixins-editing.txt create mode 100644 docs/ref/class-based-views/mixins-multiple-object.txt create mode 100644 docs/ref/class-based-views/mixins-simple.txt create mode 100644 docs/ref/class-based-views/mixins-single-object.txt create mode 100644 docs/ref/class-based-views/mixins.txt delete mode 100644 docs/topics/class-based-views.txt create mode 100644 docs/topics/class-based-views/generic-display.txt create mode 100644 docs/topics/class-based-views/generic-editing.txt create mode 100644 docs/topics/class-based-views/index.txt create mode 100644 docs/topics/class-based-views/mixins.txt diff --git a/AUTHORS b/AUTHORS index 6c82d592c85..ea23ca45d01 100644 --- a/AUTHORS +++ b/AUTHORS @@ -51,6 +51,7 @@ answer newbie questions, and generally made Django that much better: Antoni Aloy Daniel Alves Barbosa de Oliveira Vaz AgarFu + James Aylett Dagur Páll Ammendrup Collin Anderson Jeff Anderson @@ -85,6 +86,7 @@ answer newbie questions, and generally made Django that much better: Esdras Beleza Chris Bennett James Bennett + Danilo Bargen Shai Berger Julian Bez Arvis Bickovskis @@ -222,6 +224,7 @@ answer newbie questions, and generally made Django that much better: pradeep.gowda@gmail.com Collin Grady Gabriel Grant + Daniel Greenfeld Simon Greenhill Owen Griffiths Espen Grindhaug @@ -507,6 +510,7 @@ answer newbie questions, and generally made Django that much better: Aaron Swartz Ville Säävuori Mart Sõmermaa + Marc Tamlyn Christian Tanzer Tyler Tarabula Tyson Tate @@ -555,6 +559,7 @@ answer newbie questions, and generally made Django that much better: Mike Wiacek Frank Wierzbicki charly.wilhelm@gmail.com + Simon Williams Derek Willis Rachel Willmer Jakub Wilk diff --git a/django/views/generic/base.py b/django/views/generic/base.py index c45bc32bf54..69751727bbf 100644 --- a/django/views/generic/base.py +++ b/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, diff --git a/django/views/generic/dates.py b/django/views/generic/dates.py index 26b172231c0..22a72bc4332 100644 --- a/django/views/generic/dates.py +++ b/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 diff --git a/django/views/generic/detail.py b/django/views/generic/detail.py index a5ddb5fe3e4..cc0b44512ea 100644 --- a/django/views/generic/detail.py +++ b/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) + * ``/.html`` """ try: names = super(SingleObjectTemplateResponseMixin, self).get_template_names() diff --git a/django/views/generic/edit.py b/django/views/generic/edit.py index b3ff3283448..e51cdf5a3d5 100644 --- a/django/views/generic/edit.py +++ b/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()) diff --git a/django/views/generic/list.py b/django/views/generic/list.py index 2e852699d54..ec30c58f294 100644 --- a/django/views/generic/list.py +++ b/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): diff --git a/docs/glossary.txt b/docs/glossary.txt index 4d0f8b99ea3..138b5d1cfc3 100644 --- a/docs/glossary.txt +++ b/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. diff --git a/docs/howto/static-files.txt b/docs/howto/static-files.txt index db76c247423..f8c591891de 100644 --- a/docs/howto/static-files.txt +++ b/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 `, +automatically if you're using a :doc:`generic view `, 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`. diff --git a/docs/index.txt b/docs/index.txt index b54951c064e..50e8471b147 100644 --- a/docs/index.txt +++ b/docs/index.txt @@ -105,9 +105,10 @@ The view layer :doc:`Managing files ` | :doc:`Custom storage ` -* **Generic views:** - :doc:`Overview` | - :doc:`Built-in generic views` +* **Class-based views:** + :doc:`Overview` | + :doc:`Built-in class-based views` | + :doc:`Built-in view mixins` * **Advanced:** :doc:`Generating CSV ` | diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index f3ce5f85393..342ef5266ae 100644 --- a/docs/internals/deprecation.txt +++ b/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 - `: + `: * 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 ` 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 diff --git a/docs/intro/tutorial04.txt b/docs/intro/tutorial04.txt index fac1433a8dd..44b9c16c2a0 100644 --- a/docs/intro/tutorial04.txt +++ b/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 -`. +`. Coming soon =========== diff --git a/docs/misc/api-stability.txt b/docs/misc/api-stability.txt index 55170262ec2..2839ee3594a 100644 --- a/docs/misc/api-stability.txt +++ b/docs/misc/api-stability.txt @@ -54,7 +54,7 @@ of 1.0. This includes these APIs: - :doc:`HTTP request/response handling `, including file uploads, middleware, sessions, URL resolution, view, and shortcut APIs. -- :doc:`Generic views `. +- :doc:`Generic views `. - :doc:`Internationalization `. diff --git a/docs/ref/class-based-views.txt b/docs/ref/class-based-views.txt deleted file mode 100644 index acd9db2d665..00000000000 --- a/docs/ref/class-based-views.txt +++ /dev/null @@ -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 `. - -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 `. - The template and context of ``TemplateResponse`` instances can be - altered later (e.g. in - :ref:`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) - * ``/.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[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 - `. - - 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) - * ``/.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 ` 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` diff --git a/docs/ref/class-based-views/base.txt b/docs/ref/class-based-views/base.txt new file mode 100644 index 00000000000..5e0360c88f0 --- /dev/null +++ b/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\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. diff --git a/docs/ref/class-based-views/generic-date-based.txt b/docs/ref/class-based-views/generic-date-based.txt new file mode 100644 index 00000000000..69a98df77be --- /dev/null +++ b/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` object object + containing all months that have objects available according to + ``queryset``, represented as + :class:`datetime.datetime` objects, in + ascending order. + + * ``year``: A :class:`datetime.date` object + representing the given year. + + * ``next_year``: A :class:`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` 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` object + containing all days that have objects available in the given month, + according to ``queryset``, represented as + :class:`datetime.datetime` objects, in + ascending order. + + * ``month``: A :class:`datetime.date` object + representing the given month. + + * ``next_month``: A :class:`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` 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` object + representing the first day of the given week. + + * ``next_week``: A :class:`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` 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` object + representing the given day. + + * ``next_day``: A :class:`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` object + representing the previous day. Unlike ``next_day``, this will never be + ``None``. + + * ``next_month``: A :class:`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` 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`. diff --git a/docs/ref/class-based-views/generic-display.txt b/docs/ref/class-based-views/generic-display.txt new file mode 100644 index 00000000000..8ec66a8cc17 --- /dev/null +++ b/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[-_\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():` diff --git a/docs/ref/class-based-views/generic-editing.txt b/docs/ref/class-based-views/generic-editing.txt new file mode 100644 index 00000000000..d5df369fb30 --- /dev/null +++ b/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'``. diff --git a/docs/ref/class-based-views/index.txt b/docs/ref/class-based-views/index.txt new file mode 100644 index 00000000000..c10e66b3969 --- /dev/null +++ b/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. diff --git a/docs/ref/class-based-views/mixins-date-based.txt b/docs/ref/class-based-views/mixins-date-based.txt new file mode 100644 index 00000000000..a65471e68d7 --- /dev/null +++ b/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 ` 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. diff --git a/docs/ref/class-based-views/mixins-editing.txt b/docs/ref/class-based-views/mixins-editing.txt new file mode 100644 index 00000000000..7258893d63a --- /dev/null +++ b/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. diff --git a/docs/ref/class-based-views/mixins-multiple-object.txt b/docs/ref/class-based-views/mixins-multiple-object.txt new file mode 100644 index 00000000000..b414355c6bf --- /dev/null +++ b/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[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 + `. + + 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) + * ``/.html`` diff --git a/docs/ref/class-based-views/mixins-simple.txt b/docs/ref/class-based-views/mixins-simple.txt new file mode 100644 index 00000000000..33d0db31346 --- /dev/null +++ b/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 `. + The template and context of ``TemplateResponse`` instances can be + altered later (e.g. in + :ref:`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). diff --git a/docs/ref/class-based-views/mixins-single-object.txt b/docs/ref/class-based-views/mixins-single-object.txt new file mode 100644 index 00000000000..a31608dbd42 --- /dev/null +++ b/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) + * ``/.html`` diff --git a/docs/ref/class-based-views/mixins.txt b/docs/ref/class-based-views/mixins.txt new file mode 100644 index 00000000000..661454f74d4 --- /dev/null +++ b/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 diff --git a/docs/ref/index.txt b/docs/ref/index.txt index 32b263199f5..01a8ab22d17 100644 --- a/docs/ref/index.txt +++ b/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 diff --git a/docs/releases/1.3-alpha-1.txt b/docs/releases/1.3-alpha-1.txt index 25ca3ce31f6..2f5124e52bb 100644 --- a/docs/releases/1.3-alpha-1.txt +++ b/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 -` for more details. There is also a document to +` for more details. There is also a document to help you `convert your function-based generic views to class-based views `_. diff --git a/docs/releases/1.3.txt b/docs/releases/1.3.txt index 2a612ed11f0..a2b54474763 100644 --- a/docs/releases/1.3.txt +++ b/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` +See :doc:`the documentation on class-based generic views` for more details. There is also a document to help you `convert your function-based generic views to class-based views `_. diff --git a/docs/topics/auth.txt b/docs/topics/auth.txt index 63986ea70fe..c0e56dbba06 100644 --- a/docs/topics/auth.txt +++ b/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 `, -decorate the :meth:`View.dispatch ` -method on the class. See :ref:`decorating-class-based-views` for details. +To limit access to a :doc:`class-based generic view +`, decorate the :meth:`View.dispatch +` method on the class. See +:ref:`decorating-class-based-views` for details. .. _permissions: diff --git a/docs/topics/class-based-views.txt b/docs/topics/class-based-views.txt deleted file mode 100644 index 13c2b994e47..00000000000 --- a/docs/topics/class-based-views.txt +++ /dev/null @@ -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 %} -

Publishers

-
    - {% for publisher in object_list %} -
  • {{ publisher.name }}
  • - {% endfor %} -
- {% 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` 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 ` 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` 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\\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. diff --git a/docs/topics/class-based-views/generic-display.txt b/docs/topics/class-based-views/generic-display.txt new file mode 100644 index 00000000000..39fb41df043 --- /dev/null +++ b/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 %} +

Publishers

+
    + {% for publisher in object_list %} +
  • {{ publisher.name }}
  • + {% endfor %} +
+ {% 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` 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 ` 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` 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\\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` diff --git a/docs/topics/class-based-views/generic-editing.txt b/docs/topics/class-based-views/generic-editing.txt new file mode 100644 index 00000000000..23d346a32a8 --- /dev/null +++ b/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`). 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\d+)/$', AuthorUpdate.as_view(), name='author_update'), + url(r'author/(?P\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` using +:func:`~django.contrib.auth.decorators.login_required`, or +alternatively handle unauthorised users in the :meth:`form_valid()`. diff --git a/docs/topics/class-based-views/index.txt b/docs/topics/class-based-views/index.txt new file mode 100644 index 00000000000..bdf649da48c --- /dev/null +++ b/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`. + +.. 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`. + +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. diff --git a/docs/topics/class-based-views/mixins.txt b/docs/topics/class-based-views/mixins.txt new file mode 100644 index 00000000000..0c19d60e350 --- /dev/null +++ b/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` 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`. + +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 + `. + + ``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`. + +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 +``/_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` 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 +``/_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` and :doc:`class-based view + 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`. + +.. 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\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 %} +

Publisher {{ publisher.name }}

+ +
    + {% for book in page_obj %} +
  1. {{ book.title }}
  2. + {% endfor %} +
+ + + {% 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`, :doc:`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`; 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. diff --git a/docs/topics/forms/index.txt b/docs/topics/forms/index.txt index ff0665b13af..b8431078713 100644 --- a/docs/topics/forms/index.txt +++ b/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 ` to avoid duplicating your model description. +.. _using-a-form-in-a-view: + Using a form in a view ---------------------- diff --git a/docs/topics/http/generic-views.txt b/docs/topics/http/generic-views.txt index fdaa27dcd07..afdd014411b 100644 --- a/docs/topics/http/generic-views.txt +++ b/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`. diff --git a/docs/topics/index.txt b/docs/topics/index.txt index 00647bc8b79..72f5090b150 100644 --- a/docs/topics/index.txt +++ b/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