mirror of https://github.com/django/django.git
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.
This commit is contained in:
parent
1a10a06b9f
commit
c4c7fbcc0d
5
AUTHORS
5
AUTHORS
|
@ -51,6 +51,7 @@ answer newbie questions, and generally made Django that much better:
|
|||
Antoni Aloy
|
||||
Daniel Alves Barbosa de Oliveira Vaz <danielvaz@gmail.com>
|
||||
AgarFu <heaven@croasanaso.sytes.net>
|
||||
James Aylett
|
||||
Dagur Páll Ammendrup <dagurp@gmail.com>
|
||||
Collin Anderson <cmawebsite@gmail.com>
|
||||
Jeff Anderson <jefferya@programmerq.net>
|
||||
|
@ -85,6 +86,7 @@ answer newbie questions, and generally made Django that much better:
|
|||
Esdras Beleza <linux@esdrasbeleza.com>
|
||||
Chris Bennett <chrisrbennett@yahoo.com>
|
||||
James Bennett
|
||||
Danilo Bargen
|
||||
Shai Berger <shai@platonix.com>
|
||||
Julian Bez
|
||||
Arvis Bickovskis <viestards.lists@gmail.com>
|
||||
|
@ -222,6 +224,7 @@ answer newbie questions, and generally made Django that much better:
|
|||
pradeep.gowda@gmail.com
|
||||
Collin Grady <collin@collingrady.com>
|
||||
Gabriel Grant <g@briel.ca>
|
||||
Daniel Greenfeld
|
||||
Simon Greenhill <dev@simon.net.nz>
|
||||
Owen Griffiths
|
||||
Espen Grindhaug <http://grindhaug.org/>
|
||||
|
@ -507,6 +510,7 @@ answer newbie questions, and generally made Django that much better:
|
|||
Aaron Swartz <http://www.aaronsw.com/>
|
||||
Ville Säävuori <http://www.unessa.net/>
|
||||
Mart Sõmermaa <http://mrts.pri.ee/>
|
||||
Marc Tamlyn
|
||||
Christian Tanzer <tanzer@swing.co.at>
|
||||
Tyler Tarabula <tyler.tarabula@gmail.com>
|
||||
Tyson Tate <tyson@fallingbullets.com>
|
||||
|
@ -555,6 +559,7 @@ answer newbie questions, and generally made Django that much better:
|
|||
Mike Wiacek <mjwiacek@google.com>
|
||||
Frank Wierzbicki
|
||||
charly.wilhelm@gmail.com
|
||||
Simon Williams
|
||||
Derek Willis <http://blog.thescoop.org/>
|
||||
Rachel Willmer <http://www.willmer.com/kb/>
|
||||
Jakub Wilk <ubanus@users.sf.net>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ class SingleObjectMixin(ContextMixin):
|
|||
% self.__class__.__name__)
|
||||
|
||||
try:
|
||||
# Get the single item from the filtered queryset
|
||||
obj = queryset.get()
|
||||
except ObjectDoesNotExist:
|
||||
raise Http404(_("No %(verbose_name)s found matching the query") %
|
||||
|
@ -88,6 +89,9 @@ class SingleObjectMixin(ContextMixin):
|
|||
return None
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""
|
||||
Insert the single object into the context dict.
|
||||
"""
|
||||
context = {}
|
||||
context_object_name = self.get_context_object_name(self.object)
|
||||
if context_object_name:
|
||||
|
@ -97,6 +101,9 @@ class SingleObjectMixin(ContextMixin):
|
|||
|
||||
|
||||
class BaseDetailView(SingleObjectMixin, View):
|
||||
"""
|
||||
A base view for displaying a single object
|
||||
"""
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
context = self.get_context_data(object=self.object)
|
||||
|
@ -109,8 +116,13 @@ class SingleObjectTemplateResponseMixin(TemplateResponseMixin):
|
|||
|
||||
def get_template_names(self):
|
||||
"""
|
||||
Return a list of template names to be used for the request. Must return
|
||||
a list. May not be called if render_to_response is overridden.
|
||||
Return a list of template names to be used for the request. May not be
|
||||
called if render_to_response is overridden. Returns the following list:
|
||||
|
||||
* the value of ``template_name`` on the view (if provided)
|
||||
* the contents of the ``template_name_field`` field on the
|
||||
object instance that the view is operating upon (if available)
|
||||
* ``<app_label>/<object_name><template_name_suffix>.html``
|
||||
"""
|
||||
try:
|
||||
names = super(SingleObjectTemplateResponseMixin, self).get_template_names()
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -175,7 +175,7 @@ using :class:`~django.template.RequestContext` when rendering the template.
|
|||
As a brief refresher, context processors add variables into the contexts of
|
||||
every template. However, context processors require that you use
|
||||
:class:`~django.template.RequestContext` when rendering templates. This happens
|
||||
automatically if you're using a :doc:`generic view </ref/class-based-views>`,
|
||||
automatically if you're using a :doc:`generic view </ref/class-based-views/index>`,
|
||||
but in views written by hand you'll need to explicitly use ``RequestContext``
|
||||
To see how that works, and to read more details, check out
|
||||
:ref:`subclassing-context-requestcontext`.
|
||||
|
|
|
@ -105,9 +105,10 @@ The view layer
|
|||
:doc:`Managing files <topics/files>` |
|
||||
:doc:`Custom storage <howto/custom-file-storage>`
|
||||
|
||||
* **Generic views:**
|
||||
:doc:`Overview<topics/class-based-views>` |
|
||||
:doc:`Built-in generic views<ref/class-based-views>`
|
||||
* **Class-based views:**
|
||||
:doc:`Overview<topics/class-based-views/index>` |
|
||||
:doc:`Built-in class-based views<ref/class-based-views/index>` |
|
||||
:doc:`Built-in view mixins<ref/class-based-views/mixins>`
|
||||
|
||||
* **Advanced:**
|
||||
:doc:`Generating CSV <howto/outputting-csv>` |
|
||||
|
|
|
@ -134,7 +134,7 @@ these changes.
|
|||
|
||||
* The function-based generic view modules will be removed in favor of their
|
||||
class-based equivalents, outlined :doc:`here
|
||||
</topics/class-based-views>`:
|
||||
</topics/class-based-views/index>`:
|
||||
|
||||
* The :class:`~django.core.servers.basehttp.AdminMediaHandler` will be
|
||||
removed. In its place use
|
||||
|
@ -221,7 +221,7 @@ these changes.
|
|||
the 1.4 release. They will be removed.
|
||||
|
||||
* The :doc:`form wizard </ref/contrib/formtools/form-wizard>` has been
|
||||
refactored to use class based views with pluggable backends in 1.4.
|
||||
refactored to use class-based views with pluggable backends in 1.4.
|
||||
The previous implementation will be removed.
|
||||
|
||||
* Legacy ways of calling
|
||||
|
|
|
@ -320,7 +320,7 @@ function anymore -- generic views can be (and are) used multiple times
|
|||
Run the server, and use your new polling app based on generic views.
|
||||
|
||||
For full details on generic views, see the :doc:`generic views documentation
|
||||
</topics/class-based-views>`.
|
||||
</topics/class-based-views/index>`.
|
||||
|
||||
Coming soon
|
||||
===========
|
||||
|
|
|
@ -54,7 +54,7 @@ of 1.0. This includes these APIs:
|
|||
- :doc:`HTTP request/response handling </topics/http/index>`, including file
|
||||
uploads, middleware, sessions, URL resolution, view, and shortcut APIs.
|
||||
|
||||
- :doc:`Generic views </topics/class-based-views>`.
|
||||
- :doc:`Generic views </topics/class-based-views/index>`.
|
||||
|
||||
- :doc:`Internationalization </topics/i18n/index>`.
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,224 @@
|
|||
==========
|
||||
Base views
|
||||
==========
|
||||
|
||||
The following three classes provide much of the functionality needed to create
|
||||
Django views. You may think of them as *parent* views, which can be used by
|
||||
themselves or inherited from. They may not provide all the capabilities
|
||||
required for projects, in which case there are Mixins and Generic class-based
|
||||
views.
|
||||
|
||||
.. class:: django.views.generic.base.View
|
||||
|
||||
The master class-based base view. All other class-based views inherit from
|
||||
this base class.
|
||||
|
||||
**Method Flowchart**
|
||||
|
||||
1. :meth:`dispatch()`
|
||||
2. :meth:`http_method_not_allowed()`
|
||||
|
||||
**Example views.py**::
|
||||
|
||||
from django.http import HttpResponse
|
||||
from django.views.generic import View
|
||||
|
||||
class MyView(View):
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
return HttpResponse('Hello, World!')
|
||||
|
||||
**Example urls.py**::
|
||||
|
||||
from django.conf.urls import patterns, url
|
||||
|
||||
from myapp.views import MyView
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^mine/$', MyView.as_view(), name='my-view'),
|
||||
)
|
||||
|
||||
**Methods**
|
||||
|
||||
.. method:: dispatch(request, *args, **kwargs)
|
||||
|
||||
The ``view`` part of the view -- the method that accepts a ``request``
|
||||
argument plus arguments, and returns a HTTP response.
|
||||
|
||||
The default implementation will inspect the HTTP method and attempt to
|
||||
delegate to a method that matches the HTTP method; a ``GET`` will be
|
||||
delegated to :meth:`~View.get()`, a ``POST`` to :meth:`~View.post()`,
|
||||
and so on.
|
||||
|
||||
The default implementation also sets ``request``, ``args`` and
|
||||
``kwargs`` as instance variables, so any method on the view can know
|
||||
the full details of the request that was made to invoke the view.
|
||||
|
||||
.. method:: http_method_not_allowed(request, *args, **kwargs)
|
||||
|
||||
If the view was called with a HTTP method it doesn't support, this
|
||||
method is called instead.
|
||||
|
||||
The default implementation returns ``HttpResponseNotAllowed`` with list
|
||||
of allowed methods in plain text.
|
||||
|
||||
.. note::
|
||||
|
||||
Documentation on class-based views is a work in progress. As yet, only the
|
||||
methods defined directly on the class are documented here, not methods
|
||||
defined on superclasses.
|
||||
|
||||
.. class:: django.views.generic.base.TemplateView
|
||||
|
||||
Renders a given template, passing it a ``{{ params }}`` template variable,
|
||||
which is a dictionary of the parameters captured in the URL.
|
||||
|
||||
**Ancestors (MRO)**
|
||||
|
||||
* :class:`django.views.generic.base.TemplateView`
|
||||
* :class:`django.views.generic.base.TemplateResponseMixin`
|
||||
* :class:`django.views.generic.base.View`
|
||||
|
||||
**Method Flowchart**
|
||||
|
||||
1. :meth:`dispatch()`
|
||||
2. :meth:`http_method_not_allowed()`
|
||||
3. :meth:`get_context_data()`
|
||||
|
||||
**Example views.py**::
|
||||
|
||||
from django.views.generic.base import TemplateView
|
||||
|
||||
from articles.models import Article
|
||||
|
||||
class HomePageView(TemplateView):
|
||||
|
||||
template_name = "home.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(HomePageView, self).get_context_data(**kwargs)
|
||||
context['latest_articles'] = Article.objects.all()[:5]
|
||||
return context
|
||||
|
||||
**Example urls.py**::
|
||||
|
||||
from django.conf.urls import patterns, url
|
||||
|
||||
from myapp.views import HomePageView
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^$', HomePageView.as_view(), name='home'),
|
||||
)
|
||||
|
||||
**Methods and Attributes**
|
||||
|
||||
.. attribute:: template_name
|
||||
|
||||
The full name of a template to use.
|
||||
|
||||
.. method:: get_context_data(**kwargs)
|
||||
|
||||
Return a context data dictionary consisting of the contents of
|
||||
``kwargs`` stored in the context variable ``params``.
|
||||
|
||||
**Context**
|
||||
|
||||
* ``params``: The dictionary of keyword arguments captured from the URL
|
||||
pattern that served the view.
|
||||
|
||||
.. note::
|
||||
|
||||
Documentation on class-based views is a work in progress. As yet, only the
|
||||
methods defined directly on the class are documented here, not methods
|
||||
defined on superclasses.
|
||||
|
||||
.. class:: django.views.generic.base.RedirectView
|
||||
|
||||
Redirects to a given URL.
|
||||
|
||||
The given URL may contain dictionary-style string formatting, which will be
|
||||
interpolated against the parameters captured in the URL. Because keyword
|
||||
interpolation is *always* done (even if no arguments are passed in), any
|
||||
``"%"`` characters in the URL must be written as ``"%%"`` so that Python
|
||||
will convert them to a single percent sign on output.
|
||||
|
||||
If the given URL is ``None``, Django will return an ``HttpResponseGone``
|
||||
(410).
|
||||
|
||||
**Ancestors (MRO)**
|
||||
|
||||
* :class:`django.views.generic.base.View`
|
||||
|
||||
**Method Flowchart**
|
||||
|
||||
1. :meth:`dispatch()`
|
||||
2. :meth:`http_method_not_allowed()`
|
||||
3. :meth:`get_redirect_url()`
|
||||
|
||||
**Example views.py**::
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.views.generic.base import RedirectView
|
||||
|
||||
from articles.models import Article
|
||||
|
||||
class ArticleCounterRedirectView(RedirectView):
|
||||
|
||||
permanent = False
|
||||
query_string = True
|
||||
|
||||
def get_redirect_url(self, pk):
|
||||
article = get_object_or_404(Article, pk=pk)
|
||||
article.update_counter()
|
||||
return reverse('product_detail', args=(pk,))
|
||||
|
||||
**Example urls.py**::
|
||||
|
||||
from django.conf.urls import patterns, url
|
||||
from django.views.generic.base import RedirectView
|
||||
|
||||
from article.views import ArticleCounterRedirectView
|
||||
|
||||
urlpatterns = patterns('',
|
||||
|
||||
url(r'r^(?P<pk>\d+)/$', ArticleCounterRedirectView.as_view(), name='article-counter'),
|
||||
url(r'^go-to-django/$', RedirectView.as_view(url='http://djangoproject.com'), name='go-to-django'),
|
||||
)
|
||||
|
||||
**Methods and Attributes**
|
||||
|
||||
.. attribute:: url
|
||||
|
||||
The URL to redirect to, as a string. Or ``None`` to raise a 410 (Gone)
|
||||
HTTP error.
|
||||
|
||||
.. attribute:: permanent
|
||||
|
||||
Whether the redirect should be permanent. The only difference here is
|
||||
the HTTP status code returned. If ``True``, then the redirect will use
|
||||
status code 301. If ``False``, then the redirect will use status code
|
||||
302. By default, ``permanent`` is ``True``.
|
||||
|
||||
.. attribute:: query_string
|
||||
|
||||
Whether to pass along the GET query string to the new location. If
|
||||
``True``, then the query string is appended to the URL. If ``False``,
|
||||
then the query string is discarded. By default, ``query_string`` is
|
||||
``False``.
|
||||
|
||||
.. method:: get_redirect_url(**kwargs)
|
||||
|
||||
Constructs the target URL for redirection.
|
||||
|
||||
The default implementation uses :attr:`~RedirectView.url` as a starting
|
||||
string, performs expansion of ``%`` parameters in that string, as well
|
||||
as the appending of query string if requested by
|
||||
:attr:`~RedirectView.query_string`. Subclasses may implement any
|
||||
behavior they wish, as long as the method returns a redirect-ready URL
|
||||
string.
|
||||
|
||||
.. note::
|
||||
|
||||
Documentation on class-based views is a work in progress. As yet, only the
|
||||
methods defined directly on the class are documented here, not methods
|
||||
defined on superclasses.
|
|
@ -0,0 +1,273 @@
|
|||
==================
|
||||
Generic date views
|
||||
==================
|
||||
|
||||
Date-based generic views (in the module :mod:`django.views.generic.dates`)
|
||||
are views for displaying drilldown pages for date-based data.
|
||||
|
||||
.. class:: django.views.generic.dates.ArchiveIndexView
|
||||
|
||||
A top-level index page showing the "latest" objects, by date. Objects with
|
||||
a date in the *future* are not included unless you set ``allow_future`` to
|
||||
``True``.
|
||||
|
||||
**Ancestors (MRO)**
|
||||
|
||||
* :class:`django.views.generic.dates.ArchiveIndexView`
|
||||
* :class:`django.views.generic.list.MultipleObjectTemplateResponseMixin`
|
||||
* :class:`django.views.generic.base.TemplateResponseMixin`
|
||||
* :class:`django.views.generic.dates.BaseArchiveIndexView`
|
||||
* :class:`django.views.generic.dates.BaseDateListView`
|
||||
* :class:`django.views.generic.list.MultipleObjectMixin`
|
||||
* :class:`django.views.generic.dates.DateMixin`
|
||||
* :class:`django.views.generic.base.View`
|
||||
|
||||
**Notes**
|
||||
|
||||
* Uses a default ``context_object_name`` of ``latest``.
|
||||
* Uses a default ``template_name_suffix`` of ``_archive``.
|
||||
|
||||
.. class:: django.views.generic.dates.YearArchiveView
|
||||
|
||||
A yearly archive page showing all available months in a given year. Objects
|
||||
with a date in the *future* are not displayed unless you set
|
||||
``allow_future`` to ``True``.
|
||||
|
||||
**Ancestors (MRO)**
|
||||
|
||||
* :class:`django.views.generic.dates.YearArchiveView`
|
||||
* :class:`django.views.generic.list.MultipleObjectTemplateResponseMixin`
|
||||
* :class:`django.views.generic.base.TemplateResponseMixin`
|
||||
* :class:`django.views.generic.dates.BaseYearArchiveView`
|
||||
* :class:`django.views.generic.dates.YearMixin`
|
||||
* :class:`django.views.generic.dates.BaseDateListView`
|
||||
* :class:`django.views.generic.list.MultipleObjectMixin`
|
||||
* :class:`django.views.generic.dates.DateMixin`
|
||||
* :class:`django.views.generic.base.View`
|
||||
|
||||
.. attribute:: make_object_list
|
||||
|
||||
A boolean specifying whether to retrieve the full list of objects for
|
||||
this year and pass those to the template. If ``True``, the list of
|
||||
objects will be made available to the context. By default, this is
|
||||
``False``.
|
||||
|
||||
.. method:: get_make_object_list()
|
||||
|
||||
Determine if an object list will be returned as part of the context. If
|
||||
``False``, the ``None`` queryset will be used as the object list.
|
||||
|
||||
**Context**
|
||||
|
||||
In addition to the context provided by
|
||||
:class:`django.views.generic.list.MultipleObjectMixin` (via
|
||||
:class:`django.views.generic.dates.BaseDateListView`), the template's
|
||||
context will be:
|
||||
|
||||
* ``date_list``: A
|
||||
:meth:`DateQuerySet<django.db.models.query.QuerySet.dates>` object object
|
||||
containing all months that have objects available according to
|
||||
``queryset``, represented as
|
||||
:class:`datetime.datetime<python:datetime.datetime>` objects, in
|
||||
ascending order.
|
||||
|
||||
* ``year``: A :class:`datetime.date<python:datetime.date>` object
|
||||
representing the given year.
|
||||
|
||||
* ``next_year``: A :class:`datetime.date<python:datetime.date>` object
|
||||
representing the first day of the next year. If the next year is in the
|
||||
future, this will be ``None``.
|
||||
|
||||
* ``previous_year``: A :class:`datetime.date<python:datetime.date>` object
|
||||
representing the first day of the previous year. Unlike ``next_year``,
|
||||
this will never be ``None``.
|
||||
|
||||
**Notes**
|
||||
|
||||
* Uses a default ``template_name_suffix`` of ``_archive_year``.
|
||||
|
||||
.. class:: django.views.generic.dates.MonthArchiveView
|
||||
|
||||
A monthly archive page showing all objects in a given month. Objects with a
|
||||
date in the *future* are not displayed unless you set ``allow_future`` to
|
||||
``True``.
|
||||
|
||||
**Ancestors (MRO)**
|
||||
|
||||
* :class:`django.views.generic.dates.MonthArchiveView`
|
||||
* :class:`django.views.generic.list.MultipleObjectTemplateResponseMixin`
|
||||
* :class:`django.views.generic.base.TemplateResponseMixin`
|
||||
* :class:`django.views.generic.dates.BaseMonthArchiveView`
|
||||
* :class:`django.views.generic.dates.YearMixin`
|
||||
* :class:`django.views.generic.dates.MonthMixin`
|
||||
* :class:`django.views.generic.dates.BaseDateListView`
|
||||
* :class:`django.views.generic.list.MultipleObjectMixin`
|
||||
* :class:`django.views.generic.dates.DateMixin`
|
||||
* :class:`django.views.generic.base.View`
|
||||
|
||||
**Context**
|
||||
|
||||
In addition to the context provided by
|
||||
:class:`~django.views.generic.list.MultipleObjectMixin` (via
|
||||
:class:`~django.views.generic.dates.BaseDateListView`), the template's
|
||||
context will be:
|
||||
|
||||
* ``date_list``: A
|
||||
:meth:`DateQuerySet<django.db.models.query.QuerySet.dates>` object
|
||||
containing all days that have objects available in the given month,
|
||||
according to ``queryset``, represented as
|
||||
:class:`datetime.datetime<python:datetime.datetime>` objects, in
|
||||
ascending order.
|
||||
|
||||
* ``month``: A :class:`datetime.date<python:datetime.date>` object
|
||||
representing the given month.
|
||||
|
||||
* ``next_month``: A :class:`datetime.date<python:datetime.date>` object
|
||||
representing the first day of the next month. If the next month is in the
|
||||
future, this will be ``None``.
|
||||
|
||||
* ``previous_month``: A :class:`datetime.date<python:datetime.date>` object
|
||||
representing the first day of the previous month. Unlike ``next_month``,
|
||||
this will never be ``None``.
|
||||
|
||||
**Notes**
|
||||
|
||||
* Uses a default ``template_name_suffix`` of ``_archive_month``.
|
||||
|
||||
.. class:: django.views.generic.dates.WeekArchiveView
|
||||
|
||||
A weekly archive page showing all objects in a given week. Objects with a
|
||||
date in the *future* are not displayed unless you set ``allow_future`` to
|
||||
``True``.
|
||||
|
||||
**Ancestors (MRO)**
|
||||
|
||||
* :class:`django.views.generic.dates.WeekArchiveView`
|
||||
* :class:`django.views.generic.list.MultipleObjectTemplateResponseMixin`
|
||||
* :class:`django.views.generic.base.TemplateResponseMixin`
|
||||
* :class:`django.views.generic.dates.BaseWeekArchiveView`
|
||||
* :class:`django.views.generic.dates.YearMixin`
|
||||
* :class:`django.views.generic.dates.WeekMixin`
|
||||
* :class:`django.views.generic.dates.BaseDateListView`
|
||||
* :class:`django.views.generic.list.MultipleObjectMixin`
|
||||
* :class:`django.views.generic.dates.DateMixin`
|
||||
* :class:`django.views.generic.base.View`
|
||||
|
||||
**Context**
|
||||
|
||||
In addition to the context provided by
|
||||
:class:`~django.views.generic.list.MultipleObjectMixin` (via
|
||||
:class:`~django.views.generic.dates.BaseDateListView`), the template's
|
||||
context will be:
|
||||
|
||||
* ``week``: A :class:`datetime.date<python:datetime.date>` object
|
||||
representing the first day of the given week.
|
||||
|
||||
* ``next_week``: A :class:`datetime.date<python:datetime.date>` object
|
||||
representing the first day of the next week. If the next week is in the
|
||||
future, this will be ``None``.
|
||||
|
||||
* ``previous_week``: A :class:`datetime.date<python:datetime.date>` object
|
||||
representing the first day of the previous week. Unlike ``next_week``,
|
||||
this will never be ``None``.
|
||||
|
||||
**Notes**
|
||||
|
||||
* Uses a default ``template_name_suffix`` of ``_archive_week``.
|
||||
|
||||
.. class:: django.views.generic.dates.DayArchiveView
|
||||
|
||||
A day archive page showing all objects in a given day. Days in the future
|
||||
throw a 404 error, regardless of whether any objects exist for future days,
|
||||
unless you set ``allow_future`` to ``True``.
|
||||
|
||||
**Ancestors (MRO)**
|
||||
|
||||
* :class:`django.views.generic.dates.DayArchiveView`
|
||||
* :class:`django.views.generic.list.MultipleObjectTemplateResponseMixin`
|
||||
* :class:`django.views.generic.base.TemplateResponseMixin`
|
||||
* :class:`django.views.generic.dates.BaseDayArchiveView`
|
||||
* :class:`django.views.generic.dates.YearMixin`
|
||||
* :class:`django.views.generic.dates.MonthMixin`
|
||||
* :class:`django.views.generic.dates.DayMixin`
|
||||
* :class:`django.views.generic.dates.BaseDateListView`
|
||||
* :class:`django.views.generic.list.MultipleObjectMixin`
|
||||
* :class:`django.views.generic.dates.DateMixin`
|
||||
* :class:`django.views.generic.base.View`
|
||||
|
||||
**Context**
|
||||
|
||||
In addition to the context provided by
|
||||
:class:`~django.views.generic.list.MultipleObjectMixin` (via
|
||||
:class:`~django.views.generic.dates.BaseDateListView`), the template's
|
||||
context will be:
|
||||
|
||||
* ``day``: A :class:`datetime.date<python:datetime.date>` object
|
||||
representing the given day.
|
||||
|
||||
* ``next_day``: A :class:`datetime.date<python:datetime.date>` object
|
||||
representing the next day. If the next day is in the future, this will be
|
||||
``None``.
|
||||
|
||||
* ``previous_day``: A :class:`datetime.date<python:datetime.date>` object
|
||||
representing the previous day. Unlike ``next_day``, this will never be
|
||||
``None``.
|
||||
|
||||
* ``next_month``: A :class:`datetime.date<python:datetime.date>` object
|
||||
representing the first day of the next month. If the next month is in the
|
||||
future, this will be ``None``.
|
||||
|
||||
* ``previous_month``: A :class:`datetime.date<python:datetime.date>` object
|
||||
representing the first day of the previous month. Unlike ``next_month``,
|
||||
this will never be ``None``.
|
||||
|
||||
**Notes**
|
||||
|
||||
* Uses a default ``template_name_suffix`` of ``_archive_day``.
|
||||
|
||||
.. class:: django.views.generic.dates.TodayArchiveView
|
||||
|
||||
A day archive page showing all objects for *today*. This is exactly the
|
||||
same as :class:`django.views.generic.dates.DayArchiveView`, except today's
|
||||
date is used instead of the ``year``/``month``/``day`` arguments.
|
||||
|
||||
**Ancestors (MRO)**
|
||||
|
||||
* :class:`django.views.generic.dates.TodayArchiveView`
|
||||
* :class:`django.views.generic.list.MultipleObjectTemplateResponseMixin`
|
||||
* :class:`django.views.generic.base.TemplateResponseMixin`
|
||||
* :class:`django.views.generic.dates.BaseTodayArchiveView`
|
||||
* :class:`django.views.generic.dates.BaseDayArchiveView`
|
||||
* :class:`django.views.generic.dates.YearMixin`
|
||||
* :class:`django.views.generic.dates.MonthMixin`
|
||||
* :class:`django.views.generic.dates.DayMixin`
|
||||
* :class:`django.views.generic.dates.BaseDateListView`
|
||||
* :class:`django.views.generic.list.MultipleObjectMixin`
|
||||
* :class:`django.views.generic.dates.DateMixin`
|
||||
* :class:`django.views.generic.base.View`
|
||||
|
||||
.. class:: django.views.generic.dates.DateDetailView
|
||||
|
||||
A page representing an individual object. If the object has a date value in
|
||||
the future, the view will throw a 404 error by default, unless you set
|
||||
``allow_future`` to ``True``.
|
||||
|
||||
**Ancestors (MRO)**
|
||||
|
||||
* :class:`django.views.generic.dates.DateDetailView`
|
||||
* :class:`django.views.generic.detail.SingleObjectTemplateResponseMixin`
|
||||
* :class:`django.views.generic.base.TemplateResponseMixin`
|
||||
* :class:`django.views.generic.dates.BaseDateDetailView`
|
||||
* :class:`django.views.generic.dates.YearMixin`
|
||||
* :class:`django.views.generic.dates.MonthMixin`
|
||||
* :class:`django.views.generic.dates.DayMixin`
|
||||
* :class:`django.views.generic.dates.DateMixin`
|
||||
* :class:`django.views.generic.detail.BaseDetailView`
|
||||
* :class:`django.views.generic.detail.SingleObjectMixin`
|
||||
* :class:`django.views.generic.base.View`
|
||||
|
||||
.. note::
|
||||
|
||||
All of the generic views listed above have matching Base* views that only
|
||||
differ in that the they do not include the
|
||||
:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`.
|
|
@ -0,0 +1,86 @@
|
|||
=====================
|
||||
Generic display views
|
||||
=====================
|
||||
|
||||
The two following generic class-based views are designed to display data. On
|
||||
many projects they are typically the most commonly used views.
|
||||
|
||||
.. class:: django.views.generic.detail.DetailView
|
||||
|
||||
While this view is executing, ``self.object`` will contain the object that
|
||||
the view is operating upon.
|
||||
|
||||
**Ancestors (MRO)**
|
||||
|
||||
* :class:`django.views.generic.detail.SingleObjectTemplateResponseMixin`
|
||||
* :class:`django.views.generic.base.TemplateResponseMixin`
|
||||
* :class:`django.views.generic.detail.BaseDetailView`
|
||||
* :class:`django.views.generic.detail.SingleObjectMixin`
|
||||
* :class:`django.views.generic.base.View`
|
||||
|
||||
**Method Flowchart**
|
||||
|
||||
1. :meth:`dispatch()`
|
||||
2. :meth:`http_method_not_allowed()`
|
||||
3. :meth:`get_template_names()`
|
||||
4. :meth:`get_slug_field()`
|
||||
5. :meth:`get_queryset()`
|
||||
6. :meth:`get_object()`
|
||||
7. :meth:`get_context_object_name()`
|
||||
8. :meth:`get_context_data()`
|
||||
9. :meth:`get()`
|
||||
10. :meth:`render_to_response()`
|
||||
|
||||
**Example views.py**::
|
||||
|
||||
from django.views.generic.detail import DetailView
|
||||
from django.utils import timezone
|
||||
|
||||
from articles.models import Article
|
||||
|
||||
class ArticleDetailView(DetailView):
|
||||
|
||||
model = Article
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(ArticleDetailView, self).get_context_data(**kwargs)
|
||||
context['now'] = timezone.now()
|
||||
return context
|
||||
|
||||
**Example urls.py**::
|
||||
|
||||
from django.conf.urls import patterns, url
|
||||
|
||||
from article.views import ArticleDetailView
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^(?P<slug>[-_\w]+)/$', ArticleDetailView.as_view(), 'article-detail'),
|
||||
)
|
||||
|
||||
.. class:: django.views.generic.list.ListView
|
||||
|
||||
A page representing a list of objects.
|
||||
|
||||
While this view is executing, ``self.object_list`` will contain the list of
|
||||
objects (usually, but not necessarily a queryset) that the view is
|
||||
operating upon.
|
||||
|
||||
**Mixins**
|
||||
|
||||
* :class:`django.views.generic.list.ListView`
|
||||
* :class:`django.views.generic.list.MultipleObjectTemplateResponseMixin`
|
||||
* :class:`django.views.generic.base.TemplateResponseMixin`
|
||||
* :class:`django.views.generic.list.BaseListView`
|
||||
* :class:`django.views.generic.list.MultipleObjectMixin`
|
||||
* :class:`django.views.generic.base.View`
|
||||
|
||||
**Method Flowchart**
|
||||
|
||||
1. :meth:`dispatch():`
|
||||
2. :meth:`http_method_not_allowed():`
|
||||
3. :meth:`get_template_names():`
|
||||
4. :meth:`get_queryset():`
|
||||
5. :meth:`get_objects():`
|
||||
6. :meth:`get_context_data():`
|
||||
7. :meth:`get():`
|
||||
8. :meth:`render_to_response():`
|
|
@ -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'``.
|
|
@ -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.
|
|
@ -0,0 +1,256 @@
|
|||
=================
|
||||
Date-based mixins
|
||||
=================
|
||||
|
||||
|
||||
.. class:: django.views.generic.dates.YearMixin
|
||||
|
||||
A mixin that can be used to retrieve and provide parsing information for a
|
||||
year component of a date.
|
||||
|
||||
**Methods and Attributes**
|
||||
|
||||
.. attribute:: year_format
|
||||
|
||||
The :func:`~time.strftime` format to use when parsing the year.
|
||||
By default, this is ``'%Y'``.
|
||||
|
||||
.. attribute:: year
|
||||
|
||||
**Optional** The value for the year (as a string). By default, set to
|
||||
``None``, which means the year will be determined using other means.
|
||||
|
||||
.. method:: get_year_format()
|
||||
|
||||
Returns the :func:`~time.strftime` format to use when parsing the year. Returns
|
||||
:attr:`YearMixin.year_format` by default.
|
||||
|
||||
.. method:: get_year()
|
||||
|
||||
Returns the year for which this view will display data. Tries the
|
||||
following sources, in order:
|
||||
|
||||
* The value of the :attr:`YearMixin.year` attribute.
|
||||
* The value of the `year` argument captured in the URL pattern
|
||||
* The value of the `year` GET query argument.
|
||||
|
||||
Raises a 404 if no valid year specification can be found.
|
||||
|
||||
.. class:: django.views.generic.dates.MonthMixin
|
||||
|
||||
A mixin that can be used to retrieve and provide parsing information for a
|
||||
month component of a date.
|
||||
|
||||
**Methods and Attributes**
|
||||
|
||||
.. attribute:: month_format
|
||||
|
||||
The :func:`~time.strftime` format to use when parsing the month. By default, this is
|
||||
``'%b'``.
|
||||
|
||||
.. attribute:: month
|
||||
|
||||
**Optional** The value for the month (as a string). By default, set to
|
||||
``None``, which means the month will be determined using other means.
|
||||
|
||||
.. method:: get_month_format()
|
||||
|
||||
Returns the :func:`~time.strftime` format to use when parsing the month. Returns
|
||||
:attr:`MonthMixin.month_format` by default.
|
||||
|
||||
.. method:: get_month()
|
||||
|
||||
Returns the month for which this view will display data. Tries the
|
||||
following sources, in order:
|
||||
|
||||
* The value of the :attr:`MonthMixin.month` attribute.
|
||||
* The value of the `month` argument captured in the URL pattern
|
||||
* The value of the `month` GET query argument.
|
||||
|
||||
Raises a 404 if no valid month specification can be found.
|
||||
|
||||
.. method:: get_next_month(date)
|
||||
|
||||
Returns a date object containing the first day of the month after the
|
||||
date provided. Returns ``None`` if mixed with a view that sets
|
||||
``allow_future = False``, and the next month is in the future. If
|
||||
``allow_empty = False``, returns the next month that contains data.
|
||||
|
||||
.. method:: get_prev_month(date)
|
||||
|
||||
Returns a date object containing the first day of the month before the
|
||||
date provided. If ``allow_empty = False``, returns the previous month
|
||||
that contained data.
|
||||
|
||||
.. class:: django.views.generic.dates.DayMixin
|
||||
|
||||
A mixin that can be used to retrieve and provide parsing information for a
|
||||
day component of a date.
|
||||
|
||||
**Methods and Attributes**
|
||||
|
||||
.. attribute:: day_format
|
||||
|
||||
The :func:`~time.strftime` format to use when parsing the day. By default, this is
|
||||
``'%d'``.
|
||||
|
||||
.. attribute:: day
|
||||
|
||||
**Optional** The value for the day (as a string). By default, set to
|
||||
``None``, which means the day will be determined using other means.
|
||||
|
||||
.. method:: get_day_format()
|
||||
|
||||
Returns the :func:`~time.strftime` format to use when parsing the day. Returns
|
||||
:attr:`DayMixin.day_format` by default.
|
||||
|
||||
.. method:: get_day()
|
||||
|
||||
Returns the day for which this view will display data. Tries the
|
||||
following sources, in order:
|
||||
|
||||
* The value of the :attr:`DayMixin.day` attribute.
|
||||
* The value of the `day` argument captured in the URL pattern
|
||||
* The value of the `day` GET query argument.
|
||||
|
||||
Raises a 404 if no valid day specification can be found.
|
||||
|
||||
.. method:: get_next_day(date)
|
||||
|
||||
Returns a date object containing the next day after the date provided.
|
||||
Returns ``None`` if mixed with a view that sets ``allow_future = False``,
|
||||
and the next day is in the future. If ``allow_empty = False``, returns
|
||||
the next day that contains data.
|
||||
|
||||
.. method:: get_prev_day(date)
|
||||
|
||||
Returns a date object containing the previous day. If
|
||||
``allow_empty = False``, returns the previous day that contained data.
|
||||
|
||||
.. class:: django.views.generic.dates.WeekMixin
|
||||
|
||||
A mixin that can be used to retrieve and provide parsing information for a
|
||||
week component of a date.
|
||||
|
||||
**Methods and Attributes**
|
||||
|
||||
.. attribute:: week_format
|
||||
|
||||
The :func:`~time.strftime` format to use when parsing the week. By default, this is
|
||||
``'%U'``.
|
||||
|
||||
.. attribute:: week
|
||||
|
||||
**Optional** The value for the week (as a string). By default, set to
|
||||
``None``, which means the week will be determined using other means.
|
||||
|
||||
.. method:: get_week_format()
|
||||
|
||||
Returns the :func:`~time.strftime` format to use when parsing the week. Returns
|
||||
:attr:`WeekMixin.week_format` by default.
|
||||
|
||||
.. method:: get_week()
|
||||
|
||||
Returns the week for which this view will display data. Tries the
|
||||
following sources, in order:
|
||||
|
||||
* The value of the :attr:`WeekMixin.week` attribute.
|
||||
* The value of the `week` argument captured in the URL pattern
|
||||
* The value of the `week` GET query argument.
|
||||
|
||||
Raises a 404 if no valid week specification can be found.
|
||||
|
||||
|
||||
.. class:: django.views.generic.dates.DateMixin
|
||||
|
||||
A mixin class providing common behavior for all date-based views.
|
||||
|
||||
**Methods and Attributes**
|
||||
|
||||
.. attribute:: date_field
|
||||
|
||||
The name of the ``DateField`` or ``DateTimeField`` in the
|
||||
``QuerySet``'s model that the date-based archive should use to
|
||||
determine the objects on the page.
|
||||
|
||||
When :doc:`time zone support </topics/i18n/timezones>` is enabled and
|
||||
``date_field`` is a ``DateTimeField``, dates are assumed to be in the
|
||||
current time zone. Otherwise, the queryset could include objects from
|
||||
the previous or the next day in the end user's time zone.
|
||||
|
||||
.. warning::
|
||||
|
||||
In this situation, if you have implemented per-user time zone
|
||||
selection, the same URL may show a different set of objects,
|
||||
depending on the end user's time zone. To avoid this, you should
|
||||
use a ``DateField`` as the ``date_field`` attribute.
|
||||
|
||||
.. attribute:: allow_future
|
||||
|
||||
A boolean specifying whether to include "future" objects on this page,
|
||||
where "future" means objects in which the field specified in
|
||||
``date_field`` is greater than the current date/time. By default, this
|
||||
is ``False``.
|
||||
|
||||
.. method:: get_date_field()
|
||||
|
||||
Returns the name of the field that contains the date data that this
|
||||
view will operate on. Returns :attr:`DateMixin.date_field` by default.
|
||||
|
||||
.. method:: get_allow_future()
|
||||
|
||||
Determine whether to include "future" objects on this page, where
|
||||
"future" means objects in which the field specified in ``date_field``
|
||||
is greater than the current date/time. Returns
|
||||
:attr:`DateMixin.allow_future` by default.
|
||||
|
||||
.. class:: django.views.generic.dates.BaseDateListView
|
||||
|
||||
A base class that provides common behavior for all date-based views. There
|
||||
won't normally be a reason to instantiate
|
||||
:class:`~django.views.generic.dates.BaseDateListView`; instantiate one of
|
||||
the subclasses instead.
|
||||
|
||||
While this view (and it's subclasses) are executing, ``self.object_list``
|
||||
will contain the list of objects that the view is operating upon, and
|
||||
``self.date_list`` will contain the list of dates for which data is
|
||||
available.
|
||||
|
||||
**Mixins**
|
||||
|
||||
* :class:`~django.views.generic.dates.DateMixin`
|
||||
* :class:`~django.views.generic.list.MultipleObjectMixin`
|
||||
|
||||
**Methods and Attributes**
|
||||
|
||||
.. attribute:: allow_empty
|
||||
|
||||
A boolean specifying whether to display the page if no objects are
|
||||
available. If this is ``True`` and no objects are available, the view
|
||||
will display an empty page instead of raising a 404. By default, this
|
||||
is ``False``.
|
||||
|
||||
.. method:: get_dated_items():
|
||||
|
||||
Returns a 3-tuple containing (``date_list``, ``object_list``,
|
||||
``extra_context``).
|
||||
|
||||
``date_list`` is the list of dates for which data is available.
|
||||
``object_list`` is the list of objects. ``extra_context`` is a
|
||||
dictionary of context data that will be added to any context data
|
||||
provided by the
|
||||
:class:`~django.views.generic.list.MultipleObjectMixin`.
|
||||
|
||||
.. method:: get_dated_queryset(**lookup)
|
||||
|
||||
Returns a queryset, filtered using the query arguments defined by
|
||||
``lookup``. Enforces any restrictions on the queryset, such as
|
||||
``allow_empty`` and ``allow_future``.
|
||||
|
||||
.. method:: get_date_list(queryset, date_type)
|
||||
|
||||
Returns the list of dates of type ``date_type`` for which
|
||||
``queryset`` contains entries. For example, ``get_date_list(qs,
|
||||
'year')`` will return the list of years for which ``qs`` has entries.
|
||||
See :meth:`~django.db.models.query.QuerySet.dates()` for the
|
||||
ways that the ``date_type`` argument can be used.
|
|
@ -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.
|
|
@ -0,0 +1,175 @@
|
|||
======================
|
||||
Multiple object mixins
|
||||
======================
|
||||
|
||||
.. class:: django.views.generic.list.MultipleObjectMixin
|
||||
|
||||
A mixin that can be used to display a list of objects.
|
||||
|
||||
If ``paginate_by`` is specified, Django will paginate the results returned
|
||||
by this. You can specify the page number in the URL in one of two ways:
|
||||
|
||||
* Use the ``page`` parameter in the URLconf. For example, this is what
|
||||
your URLconf might look like::
|
||||
|
||||
(r'^objects/page(?P<page>[0-9]+)/$', PaginatedView.as_view())
|
||||
|
||||
* Pass the page number via the ``page`` query-string parameter. For
|
||||
example, a URL would look like this::
|
||||
|
||||
/objects/?page=3
|
||||
|
||||
These values and lists are 1-based, not 0-based, so the first page would be
|
||||
represented as page ``1``.
|
||||
|
||||
For more on pagination, read the :doc:`pagination documentation
|
||||
</topics/pagination>`.
|
||||
|
||||
As a special case, you are also permitted to use ``last`` as a value for
|
||||
``page``::
|
||||
|
||||
/objects/?page=last
|
||||
|
||||
This allows you to access the final page of results without first having to
|
||||
determine how many pages there are.
|
||||
|
||||
Note that ``page`` *must* be either a valid page number or the value
|
||||
``last``; any other value for ``page`` will result in a 404 error.
|
||||
|
||||
**Extends**
|
||||
|
||||
* :class:`django.views.generic.base.ContextMixin`
|
||||
|
||||
**Methods and Attributes**
|
||||
|
||||
.. attribute:: allow_empty
|
||||
|
||||
A boolean specifying whether to display the page if no objects are
|
||||
available. If this is ``False`` and no objects are available, the view
|
||||
will raise a 404 instead of displaying an empty page. By default, this
|
||||
is ``True``.
|
||||
|
||||
.. attribute:: model
|
||||
|
||||
The model that this view will display data for. Specifying ``model
|
||||
= Foo`` is effectively the same as specifying ``queryset =
|
||||
Foo.objects.all()``.
|
||||
|
||||
.. attribute:: queryset
|
||||
|
||||
A ``QuerySet`` that represents the objects. If provided, the value of
|
||||
:attr:`MultipleObjectMixin.queryset` supersedes the value provided for
|
||||
:attr:`MultipleObjectMixin.model`.
|
||||
|
||||
.. attribute:: paginate_by
|
||||
|
||||
An integer specifying how many objects should be displayed per page. If
|
||||
this is given, the view will paginate objects with
|
||||
:attr:`MultipleObjectMixin.paginate_by` objects per page. The view will
|
||||
expect either a ``page`` query string parameter (via ``GET``) or a
|
||||
``page`` variable specified in the URLconf.
|
||||
|
||||
.. attribute:: paginator_class
|
||||
|
||||
The paginator class to be used for pagination. By default,
|
||||
:class:`django.core.paginator.Paginator` is used. If the custom paginator
|
||||
class doesn't have the same constructor interface as
|
||||
:class:`django.core.paginator.Paginator`, you will also need to
|
||||
provide an implementation for :meth:`MultipleObjectMixin.get_paginator`.
|
||||
|
||||
.. attribute:: context_object_name
|
||||
|
||||
Designates the name of the variable to use in the context.
|
||||
|
||||
.. method:: get_queryset()
|
||||
|
||||
Returns the queryset that represents the data this view will display.
|
||||
|
||||
.. method:: paginate_queryset(queryset, page_size)
|
||||
|
||||
Returns a 4-tuple containing (``paginator``, ``page``, ``object_list``,
|
||||
``is_paginated``).
|
||||
|
||||
Constructed by paginating ``queryset`` into pages of size ``page_size``.
|
||||
If the request contains a ``page`` argument, either as a captured URL
|
||||
argument or as a GET argument, ``object_list`` will correspond to the
|
||||
objects from that page.
|
||||
|
||||
.. method:: get_paginate_by(queryset)
|
||||
|
||||
Returns the number of items to paginate by, or ``None`` for no
|
||||
pagination. By default this simply returns the value of
|
||||
:attr:`MultipleObjectMixin.paginate_by`.
|
||||
|
||||
.. method:: get_paginator(queryset, per_page, orphans=0, allow_empty_first_page=True)
|
||||
|
||||
Returns an instance of the paginator to use for this view. By default,
|
||||
instantiates an instance of :attr:`paginator_class`.
|
||||
|
||||
.. method:: get_allow_empty()
|
||||
|
||||
Return a boolean specifying whether to display the page if no objects
|
||||
are available. If this method returns ``False`` and no objects are
|
||||
available, the view will raise a 404 instead of displaying an empty
|
||||
page. By default, this is ``True``.
|
||||
|
||||
.. method:: get_context_object_name(object_list)
|
||||
|
||||
Return the context variable name that will be used to contain
|
||||
the list of data that this view is manipulating. If
|
||||
``object_list`` is a queryset of Django objects and
|
||||
:attr:`~MultipleObjectMixin.context_object_name` is not set,
|
||||
the context name will be the ``object_name`` of the model that
|
||||
the queryset is composed from, with postfix ``'_list'``
|
||||
appended. For example, the model ``Article`` would have a
|
||||
context object named ``article_list``.
|
||||
|
||||
.. method:: get_context_data(**kwargs)
|
||||
|
||||
Returns context data for displaying the list of objects.
|
||||
|
||||
**Context**
|
||||
|
||||
* ``object_list``: The list of objects that this view is displaying. If
|
||||
``context_object_name`` is specified, that variable will also be set
|
||||
in the context, with the same value as ``object_list``.
|
||||
|
||||
* ``is_paginated``: A boolean representing whether the results are
|
||||
paginated. Specifically, this is set to ``False`` if no page size has
|
||||
been specified, or if the available objects do not span multiple
|
||||
pages.
|
||||
|
||||
* ``paginator``: An instance of
|
||||
:class:`django.core.paginator.Paginator`. If the page is not
|
||||
paginated, this context variable will be ``None``.
|
||||
|
||||
* ``page_obj``: An instance of
|
||||
:class:`django.core.paginator.Page`. If the page is not paginated,
|
||||
this context variable will be ``None``.
|
||||
|
||||
|
||||
.. class:: django.views.generic.list.MultipleObjectTemplateResponseMixin
|
||||
|
||||
A mixin class that performs template-based response rendering for views
|
||||
that operate upon a list of object instances. Requires that the view it is
|
||||
mixed with provides ``self.object_list``, the list of object instances that
|
||||
the view is operating on. ``self.object_list`` may be, but is not required
|
||||
to be, a :class:`~django.db.models.query.QuerySet`.
|
||||
|
||||
**Extends**
|
||||
|
||||
* :class:`~django.views.generic.base.TemplateResponseMixin`
|
||||
|
||||
**Methods and Attributes**
|
||||
|
||||
.. attribute:: template_name_suffix
|
||||
|
||||
The suffix to append to the auto-generated candidate template name.
|
||||
Default suffix is ``_list``.
|
||||
|
||||
.. method:: get_template_names()
|
||||
|
||||
Returns a list of candidate template names. Returns the following list:
|
||||
|
||||
* the value of ``template_name`` on the view (if provided)
|
||||
* ``<app_label>/<object_name><template_name_suffix>.html``
|
|
@ -0,0 +1,60 @@
|
|||
=============
|
||||
Simple mixins
|
||||
=============
|
||||
|
||||
.. class:: django.views.generic.base.ContextMixin
|
||||
|
||||
.. versionadded:: 1.5
|
||||
|
||||
**classpath**
|
||||
|
||||
``django.views.generic.base.ContextMixin``
|
||||
|
||||
**Methods**
|
||||
|
||||
.. method:: get_context_data(**kwargs)
|
||||
|
||||
Returns a dictionary representing the template context. The
|
||||
keyword arguments provided will make up the returned context.
|
||||
|
||||
.. class:: django.views.generic.base.TemplateResponseMixin
|
||||
|
||||
Provides a mechanism to construct a
|
||||
:class:`~django.template.response.TemplateResponse`, given
|
||||
suitable context. The template to use is configurable and can be
|
||||
further customized by subclasses.
|
||||
|
||||
**Methods and Attributes**
|
||||
|
||||
.. attribute:: response_class
|
||||
|
||||
The response class to be returned by ``render_to_response`` method.
|
||||
Default is
|
||||
:class:`TemplateResponse <django.template.response.TemplateResponse>`.
|
||||
The template and context of ``TemplateResponse`` instances can be
|
||||
altered later (e.g. in
|
||||
:ref:`template response middleware <template-response-middleware>`).
|
||||
|
||||
If you need custom template loading or custom context object
|
||||
instantiation, create a ``TemplateResponse`` subclass and assign it to
|
||||
``response_class``.
|
||||
|
||||
.. method:: render_to_response(context, **response_kwargs)
|
||||
|
||||
Returns a ``self.response_class`` instance.
|
||||
|
||||
If any keyword arguments are provided, they will be
|
||||
passed to the constructor of the response class.
|
||||
|
||||
Calls :meth:`~TemplateResponseMixin.get_template_names()` to obtain the
|
||||
list of template names that will be searched looking for an existent
|
||||
template.
|
||||
|
||||
.. method:: get_template_names()
|
||||
|
||||
Returns a list of template names to search for when rendering the
|
||||
template.
|
||||
|
||||
If :attr:`TemplateResponseMixin.template_name` is specified, the
|
||||
default implementation will return a list containing
|
||||
:attr:`TemplateResponseMixin.template_name` (if it is specified).
|
|
@ -0,0 +1,124 @@
|
|||
====================
|
||||
Single object mixins
|
||||
====================
|
||||
|
||||
.. class:: django.views.generic.detail.SingleObjectMixin
|
||||
|
||||
Provides a mechanism for looking up an object associated with the
|
||||
current HTTP request.
|
||||
|
||||
**Methods and Attributes**
|
||||
|
||||
.. attribute:: model
|
||||
|
||||
The model that this view will display data for. Specifying ``model
|
||||
= Foo`` is effectively the same as specifying ``queryset =
|
||||
Foo.objects.all()``.
|
||||
|
||||
.. attribute:: queryset
|
||||
|
||||
A ``QuerySet`` that represents the objects. If provided, the value of
|
||||
:attr:`SingleObjectMixin.queryset` supersedes the value provided for
|
||||
:attr:`SingleObjectMixin.model`.
|
||||
|
||||
.. attribute:: slug_field
|
||||
|
||||
The name of the field on the model that contains the slug. By default,
|
||||
``slug_field`` is ``'slug'``.
|
||||
|
||||
.. attribute:: slug_url_kwarg
|
||||
|
||||
.. versionadded:: 1.4
|
||||
|
||||
The name of the URLConf keyword argument that contains the slug. By
|
||||
default, ``slug_url_kwarg`` is ``'slug'``.
|
||||
|
||||
.. attribute:: pk_url_kwarg
|
||||
|
||||
.. versionadded:: 1.4
|
||||
|
||||
The name of the URLConf keyword argument that contains the primary key.
|
||||
By default, ``pk_url_kwarg`` is ``'pk'``.
|
||||
|
||||
.. attribute:: context_object_name
|
||||
|
||||
Designates the name of the variable to use in the context.
|
||||
|
||||
.. method:: get_object(queryset=None)
|
||||
|
||||
Returns the single object that this view will display. If
|
||||
``queryset`` is provided, that queryset will be used as the
|
||||
source of objects; otherwise,
|
||||
:meth:`~SingleObjectMixin.get_queryset` will be used.
|
||||
``get_object()`` looks for a
|
||||
:attr:`SingleObjectMixin.pk_url_kwarg` argument in the arguments
|
||||
to the view; if this argument is found, this method performs a
|
||||
primary-key based lookup using that value. If this argument is not
|
||||
found, it looks for a :attr:`SingleObjectMixin.slug_url_kwarg`
|
||||
argument, and performs a slug lookup using the
|
||||
:attr:`SingleObjectMixin.slug_field`.
|
||||
|
||||
.. method:: get_queryset()
|
||||
|
||||
Returns the queryset that will be used to retrieve the object that
|
||||
this view will display. By default,
|
||||
:meth:`~SingleObjectMixin.get_queryset` returns the value of the
|
||||
:attr:`~SingleObjectMixin.queryset` attribute if it is set, otherwise
|
||||
it constructs a :class:`QuerySet` by calling the `all()` method on the
|
||||
:attr:`~SingleObjectMixin.model` attribute's default manager.
|
||||
|
||||
.. method:: get_context_object_name(obj)
|
||||
|
||||
Return the context variable name that will be used to contain the
|
||||
data that this view is manipulating. If
|
||||
:attr:`~SingleObjectMixin.context_object_name` is not set, the context
|
||||
name will be constructed from the ``object_name`` of the model that
|
||||
the queryset is composed from. For example, the model ``Article``
|
||||
would have context object named ``'article'``.
|
||||
|
||||
.. method:: get_context_data(**kwargs)
|
||||
|
||||
Returns context data for displaying the list of objects.
|
||||
|
||||
**Context**
|
||||
|
||||
* ``object``: The object that this view is displaying. If
|
||||
``context_object_name`` is specified, that variable will also be
|
||||
set in the context, with the same value as ``object``.
|
||||
|
||||
.. class:: django.views.generic.detail.SingleObjectTemplateResponseMixin
|
||||
|
||||
A mixin class that performs template-based response rendering for views
|
||||
that operate upon a single object instance. Requires that the view it is
|
||||
mixed with provides ``self.object``, the object instance that the view is
|
||||
operating on. ``self.object`` will usually be, but is not required to be,
|
||||
an instance of a Django model. It may be ``None`` if the view is in the
|
||||
process of constructing a new instance.
|
||||
|
||||
**Extends**
|
||||
|
||||
* :class:`~django.views.generic.base.TemplateResponseMixin`
|
||||
|
||||
**Methods and Attributes**
|
||||
|
||||
.. attribute:: template_name_field
|
||||
|
||||
The field on the current object instance that can be used to determine
|
||||
the name of a candidate template. If either ``template_name_field``
|
||||
itself or the value of the ``template_name_field`` on the current
|
||||
object instance is ``None``, the object will not be used for a
|
||||
candidate template name.
|
||||
|
||||
.. attribute:: template_name_suffix
|
||||
|
||||
The suffix to append to the auto-generated candidate template name.
|
||||
Default suffix is ``_detail``.
|
||||
|
||||
.. method:: get_template_names()
|
||||
|
||||
Returns a list of candidate template names. Returns the following list:
|
||||
|
||||
* the value of ``template_name`` on the view (if provided)
|
||||
* the contents of the ``template_name_field`` field on the
|
||||
object instance that the view is operating upon (if available)
|
||||
* ``<app_label>/<object_name><template_name_suffix>.html``
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -39,7 +39,7 @@ along with a completely generic view base class that can be used as
|
|||
the basis for reusable applications that can be easily extended.
|
||||
|
||||
See :doc:`the documentation on Class-based Generic Views
|
||||
</topics/class-based-views>` for more details. There is also a document to
|
||||
</topics/class-based-views/index>` for more details. There is also a document to
|
||||
help you `convert your function-based generic views to class-based
|
||||
views <https://docs.djangoproject.com/en/1.4/topics/generic-views-migration/>`_.
|
||||
|
||||
|
|
|
@ -80,7 +80,7 @@ provided, along with a completely generic view base class that can be
|
|||
used as the basis for reusable applications that can be easily
|
||||
extended.
|
||||
|
||||
See :doc:`the documentation on class-based generic views</topics/class-based-views>`
|
||||
See :doc:`the documentation on class-based generic views</topics/class-based-views/index>`
|
||||
for more details. There is also a document to help you `convert
|
||||
your function-based generic views to class-based
|
||||
views <https://docs.djangoproject.com/en/1.4/topics/generic-views-migration/>`_.
|
||||
|
|
|
@ -1450,9 +1450,10 @@ The permission_required decorator
|
|||
Limiting access to generic views
|
||||
--------------------------------
|
||||
|
||||
To limit access to a :doc:`class-based generic view </ref/class-based-views>`,
|
||||
decorate the :meth:`View.dispatch <django.views.generic.base.View.dispatch>`
|
||||
method on the class. See :ref:`decorating-class-based-views` for details.
|
||||
To limit access to a :doc:`class-based generic view
|
||||
</ref/class-based-views/index>`, decorate the :meth:`View.dispatch
|
||||
<django.views.generic.base.View.dispatch>` method on the class. See
|
||||
:ref:`decorating-class-based-views` for details.
|
||||
|
||||
.. _permissions:
|
||||
|
||||
|
|
|
@ -1,624 +0,0 @@
|
|||
=========================
|
||||
Class-based generic views
|
||||
=========================
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
.. note::
|
||||
Prior to Django 1.3, generic views were implemented as functions. The
|
||||
function-based implementation has been removed in favor of the
|
||||
class-based approach described here.
|
||||
|
||||
Writing Web applications can be monotonous, because we repeat certain patterns
|
||||
again and again. Django tries to take away some of that monotony at the model
|
||||
and template layers, but Web developers also experience this boredom at the view
|
||||
level.
|
||||
|
||||
Django's *generic views* were developed to ease that pain. They take certain
|
||||
common idioms and patterns found in view development and abstract them so that
|
||||
you can quickly write common views of data without having to write too much
|
||||
code.
|
||||
|
||||
We can recognize certain common tasks, like displaying a list of objects, and
|
||||
write code that displays a list of *any* object. Then the model in question can
|
||||
be passed as an extra argument to the URLconf.
|
||||
|
||||
Django ships with generic views to do the following:
|
||||
|
||||
* Perform common "simple" tasks: redirect to a different page and
|
||||
render a given template.
|
||||
|
||||
* Display list and detail pages for a single object. If we were creating an
|
||||
application to manage conferences then a ``TalkListView`` and a
|
||||
``RegisteredUserListView`` would be examples of list views. A single
|
||||
talk page is an example of what we call a "detail" view.
|
||||
|
||||
* Present date-based objects in year/month/day archive pages,
|
||||
associated detail, and "latest" pages.
|
||||
|
||||
* Allow users to create, update, and delete objects -- with or
|
||||
without authorization.
|
||||
|
||||
Taken together, these views provide easy interfaces to perform the most common
|
||||
tasks developers encounter.
|
||||
|
||||
|
||||
Simple usage
|
||||
============
|
||||
|
||||
Class-based generic views (and any class-based views that inherit from
|
||||
the base classes Django provides) can be configured in two
|
||||
ways: subclassing, or passing in arguments directly in the URLconf.
|
||||
|
||||
When you subclass a class-based view, you can override attributes
|
||||
(such as the ``template_name``) or methods (such as ``get_context_data``)
|
||||
in your subclass to provide new values or methods. Consider, for example,
|
||||
a view that just displays one template, ``about.html``. Django has a
|
||||
generic view to do this - :class:`~django.views.generic.base.TemplateView` -
|
||||
so we can just subclass it, and override the template name::
|
||||
|
||||
# some_app/views.py
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
class AboutView(TemplateView):
|
||||
template_name = "about.html"
|
||||
|
||||
Then, we just need to add this new view into our URLconf. As the class-based
|
||||
views themselves are classes, we point the URL to the ``as_view`` class method
|
||||
instead, which is the entry point for class-based views::
|
||||
|
||||
# urls.py
|
||||
from django.conf.urls import patterns, url, include
|
||||
from some_app.views import AboutView
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^about/', AboutView.as_view()),
|
||||
)
|
||||
|
||||
Alternatively, if you're only changing a few simple attributes on a
|
||||
class-based view, you can simply pass the new attributes into the ``as_view``
|
||||
method call itself::
|
||||
|
||||
from django.conf.urls import patterns, url, include
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^about/', TemplateView.as_view(template_name="about.html")),
|
||||
)
|
||||
|
||||
A similar overriding pattern can be used for the ``url`` attribute on
|
||||
:class:`~django.views.generic.base.RedirectView`, another simple
|
||||
generic view.
|
||||
|
||||
|
||||
Generic views of objects
|
||||
========================
|
||||
|
||||
:class:`~django.views.generic.base.TemplateView` certainly is useful,
|
||||
but Django's generic views really shine when it comes to presenting
|
||||
views of your database content. Because it's such a common task,
|
||||
Django comes with a handful of built-in generic views that make
|
||||
generating list and detail views of objects incredibly easy.
|
||||
|
||||
Let's take a look at one of these generic views: the "object list" view. We'll
|
||||
be using these models::
|
||||
|
||||
# models.py
|
||||
from django.db import models
|
||||
|
||||
class Publisher(models.Model):
|
||||
name = models.CharField(max_length=30)
|
||||
address = models.CharField(max_length=50)
|
||||
city = models.CharField(max_length=60)
|
||||
state_province = models.CharField(max_length=30)
|
||||
country = models.CharField(max_length=50)
|
||||
website = models.URLField()
|
||||
|
||||
class Meta:
|
||||
ordering = ["-name"]
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
class Book(models.Model):
|
||||
title = models.CharField(max_length=100)
|
||||
authors = models.ManyToManyField('Author')
|
||||
publisher = models.ForeignKey(Publisher)
|
||||
publication_date = models.DateField()
|
||||
|
||||
To build a list page of all publishers, we'd use a URLconf along these lines::
|
||||
|
||||
from django.conf.urls import patterns, url, include
|
||||
from django.views.generic import ListView
|
||||
from books.models import Publisher
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^publishers/$', ListView.as_view(
|
||||
model=Publisher,
|
||||
)),
|
||||
)
|
||||
|
||||
That's all the Python code we need to write. We still need to write a template,
|
||||
however. We could explicitly tell the view which template to use
|
||||
by including a ``template_name`` key in the arguments to as_view, but in
|
||||
the absence of an explicit template Django will infer one from the object's
|
||||
name. In this case, the inferred template will be
|
||||
``"books/publisher_list.html"`` -- the "books" part comes from the name of the
|
||||
app that defines the model, while the "publisher" bit is just the lowercased
|
||||
version of the model's name.
|
||||
|
||||
.. note::
|
||||
Thus, when (for example) the :class:`django.template.loaders.app_directories.Loader`
|
||||
template loader is enabled in :setting:`TEMPLATE_LOADERS`, the template
|
||||
location would be::
|
||||
|
||||
/path/to/project/books/templates/books/publisher_list.html
|
||||
|
||||
.. highlightlang:: html+django
|
||||
|
||||
This template will be rendered against a context containing a variable called
|
||||
``object_list`` that contains all the publisher objects. A very simple template
|
||||
might look like the following::
|
||||
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Publishers</h2>
|
||||
<ul>
|
||||
{% for publisher in object_list %}
|
||||
<li>{{ publisher.name }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
||||
That's really all there is to it. All the cool features of generic views come
|
||||
from changing the "info" dictionary passed to the generic view. The
|
||||
:doc:`generic views reference</ref/class-based-views>` documents all the generic
|
||||
views and their options in detail; the rest of this document will consider
|
||||
some of the common ways you might customize and extend generic views.
|
||||
|
||||
|
||||
Extending generic views
|
||||
=======================
|
||||
|
||||
.. highlightlang:: python
|
||||
|
||||
There's no question that using generic views can speed up development
|
||||
substantially. In most projects, however, there comes a moment when the
|
||||
generic views no longer suffice. Indeed, the most common question asked by new
|
||||
Django developers is how to make generic views handle a wider array of
|
||||
situations.
|
||||
|
||||
This is one of the reasons generic views were redesigned for the 1.3 release -
|
||||
previously, they were just view functions with a bewildering array of options;
|
||||
now, rather than passing in a large amount of configuration in the URLconf,
|
||||
the recommended way to extend generic views is to subclass them, and override
|
||||
their attributes or methods.
|
||||
|
||||
|
||||
Making "friendly" template contexts
|
||||
-----------------------------------
|
||||
|
||||
You might have noticed that our sample publisher list template stores
|
||||
all the publishers in a variable named ``object_list``. While this
|
||||
works just fine, it isn't all that "friendly" to template authors:
|
||||
they have to "just know" that they're dealing with publishers here.
|
||||
|
||||
Well, if you're dealing with a model object, this is already done for
|
||||
you. When you are dealing with an object or queryset, Django is able
|
||||
to populate the context using the verbose name (or the plural verbose
|
||||
name, in the case of a list of objects) of the object being displayed.
|
||||
This is provided in addition to the default ``object_list`` entry, but
|
||||
contains exactly the same data.
|
||||
|
||||
If the verbose name (or plural verbose name) still isn't a good match,
|
||||
you can manually set the name of the context variable. The
|
||||
``context_object_name`` attribute on a generic view specifies the
|
||||
context variable to use. In this example, we'll override it in the
|
||||
URLconf, since it's a simple change:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^publishers/$', ListView.as_view(
|
||||
model=Publisher,
|
||||
**context_object_name="publisher_list",**
|
||||
)),
|
||||
)
|
||||
|
||||
Providing a useful ``context_object_name`` is always a good idea. Your
|
||||
coworkers who design templates will thank you.
|
||||
|
||||
|
||||
Adding extra context
|
||||
--------------------
|
||||
|
||||
Often you simply need to present some extra information beyond that
|
||||
provided by the generic view. For example, think of showing a list of
|
||||
all the books on each publisher detail page. The
|
||||
:class:`~django.views.generic.detail.DetailView` generic view provides
|
||||
the publisher to the context, but it seems there's no way to get
|
||||
additional information in that template.
|
||||
|
||||
However, there is; you can subclass
|
||||
:class:`~django.views.generic.detail.DetailView` and provide your own
|
||||
implementation of the ``get_context_data`` method. The default
|
||||
implementation of this that comes with
|
||||
:class:`~django.views.generic.detail.DetailView` simply adds in the
|
||||
object being displayed to the template, but you can override it to show
|
||||
more::
|
||||
|
||||
from django.views.generic import DetailView
|
||||
from books.models import Publisher, Book
|
||||
|
||||
class PublisherDetailView(DetailView):
|
||||
|
||||
context_object_name = "publisher"
|
||||
model = Publisher
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
# Call the base implementation first to get a context
|
||||
context = super(PublisherDetailView, self).get_context_data(**kwargs)
|
||||
# Add in a QuerySet of all the books
|
||||
context['book_list'] = Book.objects.all()
|
||||
return context
|
||||
|
||||
.. note::
|
||||
|
||||
Generally, get_context_data will merge the context data of all parent classes
|
||||
with those of the current class. To preserve this behavior in your own classes
|
||||
where you want to alter the context, you should be sure to call
|
||||
get_context_data on the super class. When no two classes try to define the same
|
||||
key, this will give the expected results. However if any class attempts to
|
||||
override a key after parent classes have set it (after the call to super), any
|
||||
children of that class will also need to explictly set it after super if they
|
||||
want to be sure to override all parents.
|
||||
|
||||
Viewing subsets of objects
|
||||
--------------------------
|
||||
|
||||
Now let's take a closer look at the ``model`` argument we've been
|
||||
using all along. The ``model`` argument, which specifies the database
|
||||
model that the view will operate upon, is available on all the
|
||||
generic views that operate on a single object or a collection of
|
||||
objects. However, the ``model`` argument is not the only way to
|
||||
specify the objects that the view will operate upon -- you can also
|
||||
specify the list of objects using the ``queryset`` argument::
|
||||
|
||||
from django.views.generic import DetailView
|
||||
from books.models import Publisher, Book
|
||||
|
||||
class PublisherDetailView(DetailView):
|
||||
|
||||
context_object_name = "publisher"
|
||||
queryset = Publisher.objects.all()
|
||||
|
||||
Specifying ``model = Publisher`` is really just shorthand for saying
|
||||
``queryset = Publisher.objects.all()``. However, by using ``queryset``
|
||||
to define a filtered list of objects you can be more specific about the
|
||||
objects that will be visible in the view (see :doc:`/topics/db/queries`
|
||||
for more information about :class:`QuerySet` objects, and see the
|
||||
:doc:`class-based views reference </ref/class-based-views>` for the complete
|
||||
details).
|
||||
|
||||
To pick a simple example, we might want to order a list of books by
|
||||
publication date, with the most recent first::
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^publishers/$', ListView.as_view(
|
||||
queryset=Publisher.objects.all(),
|
||||
context_object_name="publisher_list",
|
||||
)),
|
||||
(r'^books/$', ListView.as_view(
|
||||
queryset=Book.objects.order_by("-publication_date"),
|
||||
context_object_name="book_list",
|
||||
)),
|
||||
)
|
||||
|
||||
|
||||
That's a pretty simple example, but it illustrates the idea nicely. Of course,
|
||||
you'll usually want to do more than just reorder objects. If you want to
|
||||
present a list of books by a particular publisher, you can use the same
|
||||
technique (here, illustrated using subclassing rather than by passing arguments
|
||||
in the URLconf)::
|
||||
|
||||
from django.views.generic import ListView
|
||||
from books.models import Book
|
||||
|
||||
class AcmeBookListView(ListView):
|
||||
|
||||
context_object_name = "book_list"
|
||||
queryset = Book.objects.filter(publisher__name="Acme Publishing")
|
||||
template_name = "books/acme_list.html"
|
||||
|
||||
Notice that along with a filtered ``queryset``, we're also using a custom
|
||||
template name. If we didn't, the generic view would use the same template as the
|
||||
"vanilla" object list, which might not be what we want.
|
||||
|
||||
Also notice that this isn't a very elegant way of doing publisher-specific
|
||||
books. If we want to add another publisher page, we'd need another handful of
|
||||
lines in the URLconf, and more than a few publishers would get unreasonable.
|
||||
We'll deal with this problem in the next section.
|
||||
|
||||
.. note::
|
||||
|
||||
If you get a 404 when requesting ``/books/acme/``, check to ensure you
|
||||
actually have a Publisher with the name 'ACME Publishing'. Generic
|
||||
views have an ``allow_empty`` parameter for this case. See the
|
||||
:doc:`class-based-views reference</ref/class-based-views>` for more details.
|
||||
|
||||
|
||||
Dynamic filtering
|
||||
-----------------
|
||||
|
||||
Another common need is to filter down the objects given in a list page by some
|
||||
key in the URL. Earlier we hard-coded the publisher's name in the URLconf, but
|
||||
what if we wanted to write a view that displayed all the books by some arbitrary
|
||||
publisher?
|
||||
|
||||
Handily, the ``ListView`` has a
|
||||
:meth:`~django.views.generic.detail.ListView.get_queryset` method we can
|
||||
override. Previously, it has just been returning the value of the ``queryset``
|
||||
attribute, but now we can add more logic.
|
||||
|
||||
The key part to making this work is that when class-based views are called,
|
||||
various useful things are stored on ``self``; as well as the request
|
||||
(``self.request``) this includes the positional (``self.args``) and name-based
|
||||
(``self.kwargs``) arguments captured according to the URLconf.
|
||||
|
||||
Here, we have a URLconf with a single captured group::
|
||||
|
||||
from books.views import PublisherBookListView
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^books/(\w+)/$', PublisherBookListView.as_view()),
|
||||
)
|
||||
|
||||
Next, we'll write the ``PublisherBookListView`` view itself::
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.views.generic import ListView
|
||||
from books.models import Book, Publisher
|
||||
|
||||
class PublisherBookListView(ListView):
|
||||
|
||||
context_object_name = "book_list"
|
||||
template_name = "books/books_by_publisher.html"
|
||||
|
||||
def get_queryset(self):
|
||||
publisher = get_object_or_404(Publisher, name__iexact=self.args[0])
|
||||
return Book.objects.filter(publisher=publisher)
|
||||
|
||||
As you can see, it's quite easy to add more logic to the queryset selection;
|
||||
if we wanted, we could use ``self.request.user`` to filter using the current
|
||||
user, or other more complex logic.
|
||||
|
||||
We can also add the publisher into the context at the same time, so we can
|
||||
use it in the template::
|
||||
|
||||
class PublisherBookListView(ListView):
|
||||
|
||||
context_object_name = "book_list"
|
||||
template_name = "books/books_by_publisher.html"
|
||||
|
||||
def get_queryset(self):
|
||||
self.publisher = get_object_or_404(Publisher, name__iexact=self.args[0])
|
||||
return Book.objects.filter(publisher=self.publisher)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
# Call the base implementation first to get a context
|
||||
context = super(PublisherBookListView, self).get_context_data(**kwargs)
|
||||
# Add in the publisher
|
||||
context['publisher'] = self.publisher
|
||||
return context
|
||||
|
||||
Performing extra work
|
||||
---------------------
|
||||
|
||||
The last common pattern we'll look at involves doing some extra work before
|
||||
or after calling the generic view.
|
||||
|
||||
Imagine we had a ``last_accessed`` field on our ``Author`` object that we were
|
||||
using to keep track of the last time anybody looked at that author::
|
||||
|
||||
# models.py
|
||||
|
||||
class Author(models.Model):
|
||||
salutation = models.CharField(max_length=10)
|
||||
first_name = models.CharField(max_length=30)
|
||||
last_name = models.CharField(max_length=40)
|
||||
email = models.EmailField()
|
||||
headshot = models.ImageField(upload_to='/tmp')
|
||||
last_accessed = models.DateTimeField()
|
||||
|
||||
The generic ``DetailView`` class, of course, wouldn't know anything about this
|
||||
field, but once again we could easily write a custom view to keep that field
|
||||
updated.
|
||||
|
||||
First, we'd need to add an author detail bit in the URLconf to point to a
|
||||
custom view:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
from books.views import AuthorDetailView
|
||||
|
||||
urlpatterns = patterns('',
|
||||
#...
|
||||
**(r'^authors/(?P<pk>\\d+)/$', AuthorDetailView.as_view()),**
|
||||
)
|
||||
|
||||
Then we'd write our new view -- ``get_object`` is the method that retrieves the
|
||||
object -- so we simply override it and wrap the call::
|
||||
|
||||
import datetime
|
||||
from books.models import Author
|
||||
from django.views.generic import DetailView
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
class AuthorDetailView(DetailView):
|
||||
|
||||
queryset = Author.objects.all()
|
||||
|
||||
def get_object(self):
|
||||
# Call the superclass
|
||||
object = super(AuthorDetailView, self).get_object()
|
||||
# Record the last accessed date
|
||||
object.last_accessed = datetime.datetime.now()
|
||||
object.save()
|
||||
# Return the object
|
||||
return object
|
||||
|
||||
.. note::
|
||||
|
||||
This code won't actually work unless you create a
|
||||
``books/author_detail.html`` template.
|
||||
|
||||
.. note::
|
||||
|
||||
The URLconf here uses the named group ``pk`` - this name is the default
|
||||
name that ``DetailView`` uses to find the value of the primary key used to
|
||||
filter the queryset.
|
||||
|
||||
If you want to change it, you'll need to do your own ``get()`` call
|
||||
on ``self.queryset`` using the new named parameter from ``self.kwargs``.
|
||||
|
||||
More than just HTML
|
||||
-------------------
|
||||
|
||||
So far, we've been focusing on rendering templates to generate
|
||||
responses. However, that's not all generic views can do.
|
||||
|
||||
Each generic view is composed out of a series of mixins, and each
|
||||
mixin contributes a little piece of the entire view. Some of these
|
||||
mixins -- such as
|
||||
:class:`~django.views.generic.base.TemplateResponseMixin` -- are
|
||||
specifically designed for rendering content to an HTML response using a
|
||||
template. However, you can write your own mixins that perform
|
||||
different rendering behavior.
|
||||
|
||||
For example, a simple JSON mixin might look something like this::
|
||||
|
||||
import json
|
||||
from django import http
|
||||
|
||||
class JSONResponseMixin(object):
|
||||
def render_to_response(self, context):
|
||||
"Returns a JSON response containing 'context' as payload"
|
||||
return self.get_json_response(self.convert_context_to_json(context))
|
||||
|
||||
def get_json_response(self, content, **httpresponse_kwargs):
|
||||
"Construct an `HttpResponse` object."
|
||||
return http.HttpResponse(content,
|
||||
content_type='application/json',
|
||||
**httpresponse_kwargs)
|
||||
|
||||
def convert_context_to_json(self, context):
|
||||
"Convert the context dictionary into a JSON object"
|
||||
# Note: This is *EXTREMELY* naive; in reality, you'll need
|
||||
# to do much more complex handling to ensure that arbitrary
|
||||
# objects -- such as Django model instances or querysets
|
||||
# -- can be serialized as JSON.
|
||||
return json.dumps(context)
|
||||
|
||||
Then, you could build a JSON-returning
|
||||
:class:`~django.views.generic.detail.DetailView` by mixing your
|
||||
:class:`JSONResponseMixin` with the
|
||||
:class:`~django.views.generic.detail.BaseDetailView` -- (the
|
||||
:class:`~django.views.generic.detail.DetailView` before template
|
||||
rendering behavior has been mixed in)::
|
||||
|
||||
class JSONDetailView(JSONResponseMixin, BaseDetailView):
|
||||
pass
|
||||
|
||||
This view can then be deployed in the same way as any other
|
||||
:class:`~django.views.generic.detail.DetailView`, with exactly the
|
||||
same behavior -- except for the format of the response.
|
||||
|
||||
If you want to be really adventurous, you could even mix a
|
||||
:class:`~django.views.generic.detail.DetailView` subclass that is able
|
||||
to return *both* HTML and JSON content, depending on some property of
|
||||
the HTTP request, such as a query argument or a HTTP header. Just mix
|
||||
in both the :class:`JSONResponseMixin` and a
|
||||
:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`,
|
||||
and override the implementation of :func:`render_to_response()` to defer
|
||||
to the appropriate subclass depending on the type of response that the user
|
||||
requested::
|
||||
|
||||
class HybridDetailView(JSONResponseMixin, SingleObjectTemplateResponseMixin, BaseDetailView):
|
||||
def render_to_response(self, context):
|
||||
# Look for a 'format=json' GET argument
|
||||
if self.request.GET.get('format','html') == 'json':
|
||||
return JSONResponseMixin.render_to_response(self, context)
|
||||
else:
|
||||
return SingleObjectTemplateResponseMixin.render_to_response(self, context)
|
||||
|
||||
Because of the way that Python resolves method overloading, the local
|
||||
``render_to_response()`` implementation will override the versions provided by
|
||||
:class:`JSONResponseMixin` and
|
||||
:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`.
|
||||
|
||||
Decorating class-based views
|
||||
============================
|
||||
|
||||
.. highlightlang:: python
|
||||
|
||||
The extension of class-based views isn't limited to using mixins. You
|
||||
can use also use decorators.
|
||||
|
||||
Decorating in URLconf
|
||||
---------------------
|
||||
|
||||
The simplest way of decorating class-based views is to decorate the
|
||||
result of the :meth:`~django.views.generic.base.View.as_view` method.
|
||||
The easiest place to do this is in the URLconf where you deploy your
|
||||
view::
|
||||
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
from .views import VoteView
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^about/', login_required(TemplateView.as_view(template_name="secret.html"))),
|
||||
(r'^vote/', permission_required('polls.can_vote')(VoteView.as_view())),
|
||||
)
|
||||
|
||||
This approach applies the decorator on a per-instance basis. If you
|
||||
want every instance of a view to be decorated, you need to take a
|
||||
different approach.
|
||||
|
||||
.. _decorating-class-based-views:
|
||||
|
||||
Decorating the class
|
||||
--------------------
|
||||
|
||||
To decorate every instance of a class-based view, you need to decorate
|
||||
the class definition itself. To do this you apply the decorator to the
|
||||
:meth:`~django.views.generic.base.View.dispatch` method of the class.
|
||||
|
||||
A method on a class isn't quite the same as a standalone function, so
|
||||
you can't just apply a function decorator to the method -- you need to
|
||||
transform it into a method decorator first. The ``method_decorator``
|
||||
decorator transforms a function decorator into a method decorator so
|
||||
that it can be used on an instance method. For example::
|
||||
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
class ProtectedView(TemplateView):
|
||||
template_name = 'secret.html'
|
||||
|
||||
@method_decorator(login_required)
|
||||
def dispatch(self, *args, **kwargs):
|
||||
return super(ProtectedView, self).dispatch(*args, **kwargs)
|
||||
|
||||
In this example, every instance of ``ProtectedView`` will have
|
||||
login protection.
|
||||
|
||||
.. note::
|
||||
|
||||
``method_decorator`` passes ``*args`` and ``**kwargs``
|
||||
as parameters to the decorated method on the class. If your method
|
||||
does not accept a compatible set of parameters it will raise a
|
||||
``TypeError`` exception.
|
|
@ -0,0 +1,432 @@
|
|||
.. _Generic views:
|
||||
|
||||
=========================
|
||||
Class-based generic views
|
||||
=========================
|
||||
|
||||
.. note::
|
||||
Prior to Django 1.3, generic views were implemented as functions. The
|
||||
function-based implementation has been removed in favor of the
|
||||
class-based approach described here.
|
||||
|
||||
Writing Web applications can be monotonous, because we repeat certain patterns
|
||||
again and again. Django tries to take away some of that monotony at the model
|
||||
and template layers, but Web developers also experience this boredom at the view
|
||||
level.
|
||||
|
||||
Django's *generic views* were developed to ease that pain. They take certain
|
||||
common idioms and patterns found in view development and abstract them so that
|
||||
you can quickly write common views of data without having to write too much
|
||||
code.
|
||||
|
||||
We can recognize certain common tasks, like displaying a list of objects, and
|
||||
write code that displays a list of *any* object. Then the model in question can
|
||||
be passed as an extra argument to the URLconf.
|
||||
|
||||
Django ships with generic views to do the following:
|
||||
|
||||
* Display list and detail pages for a single object. If we were creating an
|
||||
application to manage conferences then a ``TalkListView`` and a
|
||||
``RegisteredUserListView`` would be examples of list views. A single
|
||||
talk page is an example of what we call a "detail" view.
|
||||
|
||||
* Present date-based objects in year/month/day archive pages,
|
||||
associated detail, and "latest" pages.
|
||||
|
||||
* Allow users to create, update, and delete objects -- with or
|
||||
without authorization.
|
||||
|
||||
Taken together, these views provide easy interfaces to perform the most common
|
||||
tasks developers encounter.
|
||||
|
||||
|
||||
Extending generic views
|
||||
=======================
|
||||
|
||||
There's no question that using generic views can speed up development
|
||||
substantially. In most projects, however, there comes a moment when the
|
||||
generic views no longer suffice. Indeed, the most common question asked by new
|
||||
Django developers is how to make generic views handle a wider array of
|
||||
situations.
|
||||
|
||||
This is one of the reasons generic views were redesigned for the 1.3 release -
|
||||
previously, they were just view functions with a bewildering array of options;
|
||||
now, rather than passing in a large amount of configuration in the URLconf,
|
||||
the recommended way to extend generic views is to subclass them, and override
|
||||
their attributes or methods.
|
||||
|
||||
That said, generic views will have a limit. If you find you're struggling to
|
||||
implement your view as a subclass of a generic view, then you may find it more
|
||||
effective to write just the code you need, using your own class-based or
|
||||
functional views.
|
||||
|
||||
More examples of generic views are available in some third party applications,
|
||||
or you could write your own as needed.
|
||||
|
||||
|
||||
Generic views of objects
|
||||
========================
|
||||
|
||||
:class:`~django.views.generic.base.TemplateView` certainly is useful, but
|
||||
Django's generic views really shine when it comes to presenting views of your
|
||||
database content. Because it's such a common task, Django comes with a handful
|
||||
of built-in generic views that make generating list and detail views of objects
|
||||
incredibly easy.
|
||||
|
||||
Let's start by looking at some examples of showing a list of objects or an
|
||||
individual object.
|
||||
|
||||
.. comment: link here to the other topic pages (form handling, date based, mixins)
|
||||
|
||||
We'll be using these models::
|
||||
|
||||
# models.py
|
||||
from django.db import models
|
||||
|
||||
class Publisher(models.Model):
|
||||
name = models.CharField(max_length=30)
|
||||
address = models.CharField(max_length=50)
|
||||
city = models.CharField(max_length=60)
|
||||
state_province = models.CharField(max_length=30)
|
||||
country = models.CharField(max_length=50)
|
||||
website = models.URLField()
|
||||
|
||||
class Meta:
|
||||
ordering = ["-name"]
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
class Book(models.Model):
|
||||
title = models.CharField(max_length=100)
|
||||
authors = models.ManyToManyField('Author')
|
||||
publisher = models.ForeignKey(Publisher)
|
||||
publication_date = models.DateField()
|
||||
|
||||
Now we need to define a view::
|
||||
|
||||
# views.py
|
||||
from django.views.generic import ListView
|
||||
from books.models import Publisher
|
||||
|
||||
class PublisherList(ListView):
|
||||
model = Publisher
|
||||
|
||||
Finally hook that view into your urls::
|
||||
|
||||
# urls.py
|
||||
from django.conf.urls import patterns, url, include
|
||||
from books.views import PublisherList
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^publishers/$', PublisherList.as_view()),
|
||||
)
|
||||
|
||||
That's all the Python code we need to write. We still need to write a template,
|
||||
however. We could explicitly tell the view which template to use by adding a
|
||||
``template_name`` attribute to the view, but in the absence of an explicit
|
||||
template Django will infer one from the object's name. In this case, the
|
||||
inferred template will be ``"books/publisher_list.html"`` -- the "books" part
|
||||
comes from the name of the app that defines the model, while the "publisher"
|
||||
bit is just the lowercased version of the model's name.
|
||||
|
||||
.. note::
|
||||
|
||||
Thus, when (for example) the
|
||||
:class:`django.template.loaders.app_directories.Loader` template loader is
|
||||
enabled in :setting:`TEMPLATE_LOADERS`, a template location could be:
|
||||
/path/to/project/books/templates/books/publisher_list.html
|
||||
|
||||
.. highlightlang:: html+django
|
||||
|
||||
This template will be rendered against a context containing a variable called
|
||||
``object_list`` that contains all the publisher objects. A very simple template
|
||||
might look like the following::
|
||||
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Publishers</h2>
|
||||
<ul>
|
||||
{% for publisher in object_list %}
|
||||
<li>{{ publisher.name }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
||||
That's really all there is to it. All the cool features of generic views come
|
||||
from changing the attributes set on the generic view. The
|
||||
:doc:`generic views reference</ref/class-based-views/index>` documents all the
|
||||
generic views and their options in detail; the rest of this document will
|
||||
consider some of the common ways you might customize and extend generic views.
|
||||
|
||||
|
||||
Making "friendly" template contexts
|
||||
-----------------------------------
|
||||
|
||||
.. highlightlang:: python
|
||||
|
||||
You might have noticed that our sample publisher list template stores all the
|
||||
publishers in a variable named ``object_list``. While this works just fine, it
|
||||
isn't all that "friendly" to template authors: they have to "just know" that
|
||||
they're dealing with publishers here.
|
||||
|
||||
Well, if you're dealing with a model object, this is already done for you. When
|
||||
you are dealing with an object or queryset, Django is able to populate the
|
||||
context using the lower cased version of the model class' name. This is
|
||||
provided in addition to the default ``object_list`` entry, but contains exactly
|
||||
the same data, i.e. ``publisher_list``.
|
||||
|
||||
If the this still isn't a good match, you can manually set the name of the
|
||||
context variable. The ``context_object_name`` attribute on a generic view
|
||||
specifies the context variable to use::
|
||||
|
||||
# views.py
|
||||
from django.views.generic import ListView
|
||||
from books.models import Publisher
|
||||
|
||||
class PublisherList(ListView):
|
||||
model = Publisher
|
||||
context_object_name = 'my_favourite_publishers'
|
||||
|
||||
Providing a useful ``context_object_name`` is always a good idea. Your
|
||||
coworkers who design templates will thank you.
|
||||
|
||||
|
||||
Adding extra context
|
||||
--------------------
|
||||
|
||||
Often you simply need to present some extra information beyond that
|
||||
provided by the generic view. For example, think of showing a list of
|
||||
all the books on each publisher detail page. The
|
||||
:class:`~django.views.generic.detail.DetailView` generic view provides
|
||||
the publisher to the context, but how do we get additional information
|
||||
in that template.
|
||||
|
||||
However, there is; you can subclass
|
||||
:class:`~django.views.generic.detail.DetailView` and provide your own
|
||||
implementation of the ``get_context_data`` method. The default
|
||||
implementation of this that comes with
|
||||
:class:`~django.views.generic.detail.DetailView` simply adds in the
|
||||
object being displayed to the template, but you can override it to send
|
||||
more::
|
||||
|
||||
from django.views.generic import DetailView
|
||||
from books.models import Publisher, Book
|
||||
|
||||
class PublisherDetail(DetailView):
|
||||
|
||||
model = Publisher
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
# Call the base implementation first to get a context
|
||||
context = super(PublisherDetailView, self).get_context_data(**kwargs)
|
||||
# Add in a QuerySet of all the books
|
||||
context['book_list'] = Book.objects.all()
|
||||
return context
|
||||
|
||||
.. note::
|
||||
|
||||
Generally, get_context_data will merge the context data of all parent
|
||||
classes with those of the current class. To preserve this behavior in your
|
||||
own classes where you want to alter the context, you should be sure to call
|
||||
get_context_data on the super class. When no two classes try to define the
|
||||
same key, this will give the expected results. However if any class
|
||||
attempts to override a key after parent classes have set it (after the call
|
||||
to super), any children of that class will also need to explictly set it
|
||||
after super if they want to be sure to override all parents. If you're
|
||||
having trouble, review the method resolution order of your view.
|
||||
|
||||
.. _generic-views-list-subsets:
|
||||
|
||||
Viewing subsets of objects
|
||||
--------------------------
|
||||
|
||||
Now let's take a closer look at the ``model`` argument we've been
|
||||
using all along. The ``model`` argument, which specifies the database
|
||||
model that the view will operate upon, is available on all the
|
||||
generic views that operate on a single object or a collection of
|
||||
objects. However, the ``model`` argument is not the only way to
|
||||
specify the objects that the view will operate upon -- you can also
|
||||
specify the list of objects using the ``queryset`` argument::
|
||||
|
||||
from django.views.generic import DetailView
|
||||
from books.models import Publisher, Book
|
||||
|
||||
class PublisherDetail(DetailView):
|
||||
|
||||
context_object_name = 'publisher'
|
||||
queryset = Publisher.objects.all()
|
||||
|
||||
Specifying ``model = Publisher`` is really just shorthand for saying
|
||||
``queryset = Publisher.objects.all()``. However, by using ``queryset``
|
||||
to define a filtered list of objects you can be more specific about the
|
||||
objects that will be visible in the view (see :doc:`/topics/db/queries`
|
||||
for more information about :class:`QuerySet` objects, and see the
|
||||
:doc:`class-based views reference </ref/class-based-views/index>` for the
|
||||
complete details).
|
||||
|
||||
To pick a simple example, we might want to order a list of books by
|
||||
publication date, with the most recent first::
|
||||
|
||||
from django.views.generic import ListView
|
||||
from books.models import Book
|
||||
|
||||
class BookList(ListView):
|
||||
queryset = Book.objects.order_by('-publication_date')
|
||||
context_object_name = 'book_list'
|
||||
|
||||
That's a pretty simple example, but it illustrates the idea nicely. Of course,
|
||||
you'll usually want to do more than just reorder objects. If you want to
|
||||
present a list of books by a particular publisher, you can use the same
|
||||
technique::
|
||||
|
||||
from django.views.generic import ListView
|
||||
from books.models import Book
|
||||
|
||||
class AcmeBookListView(ListView):
|
||||
|
||||
context_object_name = 'book_list'
|
||||
queryset = Book.objects.filter(publisher__name='Acme Publishing')
|
||||
template_name = 'books/acme_list.html'
|
||||
|
||||
Notice that along with a filtered ``queryset``, we're also using a custom
|
||||
template name. If we didn't, the generic view would use the same template as the
|
||||
"vanilla" object list, which might not be what we want.
|
||||
|
||||
Also notice that this isn't a very elegant way of doing publisher-specific
|
||||
books. If we want to add another publisher page, we'd need another handful of
|
||||
lines in the URLconf, and more than a few publishers would get unreasonable.
|
||||
We'll deal with this problem in the next section.
|
||||
|
||||
.. note::
|
||||
|
||||
If you get a 404 when requesting ``/books/acme/``, check to ensure you
|
||||
actually have a Publisher with the name 'ACME Publishing'. Generic
|
||||
views have an ``allow_empty`` parameter for this case. See the
|
||||
:doc:`class-based-views reference</ref/class-based-views/index>` for more
|
||||
details.
|
||||
|
||||
|
||||
Dynamic filtering
|
||||
-----------------
|
||||
|
||||
Another common need is to filter down the objects given in a list page by some
|
||||
key in the URL. Earlier we hard-coded the publisher's name in the URLconf, but
|
||||
what if we wanted to write a view that displayed all the books by some arbitrary
|
||||
publisher?
|
||||
|
||||
Handily, the ``ListView`` has a
|
||||
:meth:`~django.views.generic.detail.ListView.get_queryset` method we can
|
||||
override. Previously, it has just been returning the value of the ``queryset``
|
||||
attribute, but now we can add more logic.
|
||||
|
||||
The key part to making this work is that when class-based views are called,
|
||||
various useful things are stored on ``self``; as well as the request
|
||||
(``self.request``) this includes the positional (``self.args``) and name-based
|
||||
(``self.kwargs``) arguments captured according to the URLconf.
|
||||
|
||||
Here, we have a URLconf with a single captured group::
|
||||
|
||||
# urls.py
|
||||
from books.views import PublisherBookList
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^books/([\w-]+)/$', PublisherBookList.as_view()),
|
||||
)
|
||||
|
||||
Next, we'll write the ``PublisherBookList`` view itself::
|
||||
|
||||
# views.py
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.views.generic import ListView
|
||||
from books.models import Book, Publisher
|
||||
|
||||
class PublisherBookList(ListView):
|
||||
|
||||
template_name = 'books/books_by_publisher.html'
|
||||
|
||||
def get_queryset(self):
|
||||
self.publisher = get_object_or_404(Publisher, name=self.args[0])
|
||||
return Book.objects.filter(publisher=self.publisher)
|
||||
|
||||
As you can see, it's quite easy to add more logic to the queryset selection;
|
||||
if we wanted, we could use ``self.request.user`` to filter using the current
|
||||
user, or other more complex logic.
|
||||
|
||||
We can also add the publisher into the context at the same time, so we can
|
||||
use it in the template::
|
||||
|
||||
# ...
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
# Call the base implementation first to get a context
|
||||
context = super(PublisherBookListView, self).get_context_data(**kwargs)
|
||||
# Add in the publisher
|
||||
context['publisher'] = self.publisher
|
||||
return context
|
||||
|
||||
.. _generic-views-extra-work:
|
||||
|
||||
Performing extra work
|
||||
---------------------
|
||||
|
||||
The last common pattern we'll look at involves doing some extra work before
|
||||
or after calling the generic view.
|
||||
|
||||
Imagine we had a ``last_accessed`` field on our ``Author`` object that we were
|
||||
using to keep track of the last time anybody looked at that author::
|
||||
|
||||
# models.py
|
||||
|
||||
class Author(models.Model):
|
||||
salutation = models.CharField(max_length=10)
|
||||
name = models.CharField(max_length=200)
|
||||
email = models.EmailField()
|
||||
headshot = models.ImageField(upload_to='/tmp')
|
||||
last_accessed = models.DateTimeField()
|
||||
|
||||
The generic ``DetailView`` class, of course, wouldn't know anything about this
|
||||
field, but once again we could easily write a custom view to keep that field
|
||||
updated.
|
||||
|
||||
First, we'd need to add an author detail bit in the URLconf to point to a
|
||||
custom view::
|
||||
|
||||
from books.views import AuthorDetailView
|
||||
|
||||
urlpatterns = patterns('',
|
||||
#...
|
||||
url(r'^authors/(?P<pk>\\d+)/$', AuthorDetailView.as_view(), name='author-detail'),
|
||||
)
|
||||
|
||||
Then we'd write our new view -- ``get_object`` is the method that retrieves the
|
||||
object -- so we simply override it and wrap the call::
|
||||
|
||||
from django.views.generic import DetailView
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.utils import timezone
|
||||
from books.models import Author
|
||||
|
||||
class AuthorDetailView(DetailView):
|
||||
|
||||
queryset = Author.objects.all()
|
||||
|
||||
def get_object(self):
|
||||
# Call the superclass
|
||||
object = super(AuthorDetailView, self).get_object()
|
||||
# Record the last accessed date
|
||||
object.last_accessed = timezone.now()
|
||||
object.save()
|
||||
# Return the object
|
||||
return object
|
||||
|
||||
.. note::
|
||||
|
||||
The URLconf here uses the named group ``pk`` - this name is the default
|
||||
name that ``DetailView`` uses to find the value of the primary key used to
|
||||
filter the queryset.
|
||||
|
||||
If you want to call the group something else, you can set ``pk_url_kwarg``
|
||||
on the view. More details can be found in the reference for
|
||||
:class:`~django.views.generic.detail.DetailView`
|
|
@ -0,0 +1,205 @@
|
|||
Form handling with class-based views
|
||||
====================================
|
||||
|
||||
Form processing generally has 3 paths:
|
||||
|
||||
* Initial GET (blank or prepopulated form)
|
||||
* POST with invalid data (typically redisplay form with errors)
|
||||
* POST with valid data (process the data and typically redirect)
|
||||
|
||||
Implementing this yourself often results in a lot of repeated
|
||||
boilerplate code (see :ref:`Using a form in a
|
||||
view<using-a-form-in-a-view>`). To help avoid this, Django provides a
|
||||
collection of generic class-based views for form processing.
|
||||
|
||||
Basic Forms
|
||||
-----------
|
||||
|
||||
Given a simple contact form::
|
||||
|
||||
# forms.py
|
||||
from django import forms
|
||||
|
||||
class ContactForm(forms.Form):
|
||||
name = forms.CharField()
|
||||
message = forms.CharField(widget=forms.Textarea)
|
||||
|
||||
def send_email(self):
|
||||
# send email using the self.cleaned_data dictionary
|
||||
pass
|
||||
|
||||
The view can be constructed using a FormView::
|
||||
|
||||
# views.py
|
||||
from myapp.forms import ContactForm
|
||||
from django.views.generic.edit import FormView
|
||||
|
||||
class ContactView(FormView):
|
||||
template_name = 'contact.html'
|
||||
form_class = ContactForm
|
||||
success_url = '/thanks/'
|
||||
|
||||
def form_valid(self, form):
|
||||
# This method is called when valid form data has been POSTed.
|
||||
# It should return an HttpResponse.
|
||||
form.send_email()
|
||||
return super(ContactView, self).form_valid(form)
|
||||
|
||||
Notes:
|
||||
|
||||
* FormView inherits
|
||||
:class:`~django.views.generic.base.TemplateResponseMixin` so
|
||||
:attr:`~django.views.generic.base.TemplateResponseMixin.template_name`
|
||||
can be used here
|
||||
* The default implementation for
|
||||
:meth:`~django.views.generic.edit.FormView.form_valid` simply
|
||||
redirects to the :attr:`success_url`
|
||||
|
||||
Model Forms
|
||||
-----------
|
||||
|
||||
Generic views really shine when working with models. These generic
|
||||
views will automatically create a :class:`ModelForm`, so long as they
|
||||
can work out which model class to use:
|
||||
|
||||
* If the :attr:`model` attribute is given, that model class will be used
|
||||
* If :meth:`get_object()` returns an object, the class of that object
|
||||
will be used
|
||||
* If a :attr:`queryset` is given, the model for that queryset will be used
|
||||
|
||||
Model form views provide a :meth:`form_valid()` implementation that
|
||||
saves the model automatically. You can override this if you have any
|
||||
special requirements; see below for examples.
|
||||
|
||||
You don't even need to provide a attr:`success_url` for
|
||||
:class:`~django.views.generic.edit.CreateView` or
|
||||
:class:`~django.views.generic.edit.UpdateView` - they will use
|
||||
:meth:`get_absolute_url()` on the model object if available.
|
||||
|
||||
If you want to use a custom :class:`ModelForm` (for instance to add
|
||||
extra validation) simply set
|
||||
:attr:`~django.views.generic.edit.FormMixin.form_class` on your view.
|
||||
|
||||
.. note::
|
||||
When specifying a custom form class, you must still specify the model,
|
||||
even though the :attr:`form_class` may be a :class:`ModelForm`.
|
||||
|
||||
First we need to add :meth:`get_absolute_url()` to our :class:`Author`
|
||||
class:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# models.py
|
||||
from django import models
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
class Author(models.Model):
|
||||
name = models.CharField(max_length=200)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('author-detail', kwargs={'pk': self.pk})
|
||||
|
||||
Then we can use :class:`CreateView` and friends to do the actual
|
||||
work. Notice how we're just configuring the generic class-based views
|
||||
here; we don't have to write any logic ourselves::
|
||||
|
||||
# views.py
|
||||
from django.views.generic.edit import CreateView, UpdateView, DeleteView
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from myapp.models import Author
|
||||
|
||||
class AuthorCreate(CreateView):
|
||||
model = Author
|
||||
|
||||
class AuthorUpdate(UpdateView):
|
||||
model = Author
|
||||
|
||||
class AuthorDelete(DeleteView):
|
||||
model = Author
|
||||
success_url = reverse_lazy('author-list')
|
||||
|
||||
.. note::
|
||||
We have to use :func:`~django.core.urlresolvers.reverse_lazy` here, not
|
||||
just ``reverse`` as the urls are not loaded when the file is imported.
|
||||
|
||||
Finally, we hook these new views into the URLconf::
|
||||
|
||||
# urls.py
|
||||
from django.conf.urls import patterns, url
|
||||
from myapp.views import AuthorCreate, AuthorUpdate, AuthorDelete
|
||||
|
||||
urlpatterns = patterns('',
|
||||
# ...
|
||||
url(r'author/add/$', AuthorCreate.as_view(), name='author_add'),
|
||||
url(r'author/(?P<pk>\d+)/$', AuthorUpdate.as_view(), name='author_update'),
|
||||
url(r'author/(?P<pk>\d+)/delete/$', AuthorDelete.as_view(), name='author_delete'),
|
||||
)
|
||||
|
||||
.. note::
|
||||
|
||||
These views inherit :class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`
|
||||
which uses :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_prefix`
|
||||
to construct the
|
||||
:attr:`~django.views.generic.base.TemplateResponseMixin.template_name`
|
||||
based on the model.
|
||||
|
||||
In this example:
|
||||
|
||||
* :class:`CreateView` and :class:`UpdateView` use ``myapp/author_form.html``
|
||||
* :class:`DeleteView` uses ``myapp/author_confirm_delete.html``
|
||||
|
||||
If you wish to have separate templates for :class:`CreateView` and
|
||||
:class:1UpdateView`, you can set either :attr:`template_name` or
|
||||
:attr:`template_name_suffix` on your view class.
|
||||
|
||||
Models and request.user
|
||||
-----------------------
|
||||
|
||||
To track the user that created an object using a :class:`CreateView`,
|
||||
you can use a custom :class:`ModelForm` to do this. First, add the
|
||||
foreign key relation to the model::
|
||||
|
||||
# models.py
|
||||
from django import models
|
||||
from django.contrib.auth import User
|
||||
|
||||
class Author(models.Model):
|
||||
name = models.CharField(max_length=200)
|
||||
created_by = models.ForeignKey(User)
|
||||
|
||||
# ...
|
||||
|
||||
Create a custom :class:`ModelForm` in order to exclude the
|
||||
``created_by`` field and prevent the user from editing it:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# forms.py
|
||||
from django import forms
|
||||
from myapp.models import Author
|
||||
|
||||
class AuthorForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Author
|
||||
exclude = ('created_by',)
|
||||
|
||||
In the view, use the custom :attr:`form_class` and override
|
||||
:meth:`form_valid()` to add the user::
|
||||
|
||||
# views.py
|
||||
from django.views.generic.edit import CreateView
|
||||
from myapp.models import Author
|
||||
from myapp.forms import AuthorForm
|
||||
|
||||
class AuthorCreate(CreateView):
|
||||
form_class = AuthorForm
|
||||
model = Author
|
||||
|
||||
def form_valid(self, form):
|
||||
form.instance.created_by = self.request.user
|
||||
return super(AuthorCreate, self).form_valid(form)
|
||||
|
||||
Note that you'll need to :ref:`decorate this
|
||||
view<decorating-class-based-views>` using
|
||||
:func:`~django.contrib.auth.decorators.login_required`, or
|
||||
alternatively handle unauthorised users in the :meth:`form_valid()`.
|
|
@ -0,0 +1,233 @@
|
|||
=================
|
||||
Class-based views
|
||||
=================
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
A view is a callable which takes a request and returns a
|
||||
response. This can be more than just a function, and Django provides
|
||||
an example of some classes which can be used as views. These allow you
|
||||
to structure your views and reuse code by harnessing inheritance and
|
||||
mixins. There are also some generic views for simple tasks which we'll
|
||||
get to later, but you may want to design your own structure of
|
||||
reusable views which suits your use case. For full details, see the
|
||||
:doc:`class-based views reference
|
||||
documentation</ref/class-based-views/index>`.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
generic-display
|
||||
generic-editing
|
||||
mixins
|
||||
|
||||
Basic examples
|
||||
==============
|
||||
|
||||
Django provides base view classes which will suit a wide range of applications.
|
||||
All views inherit from the :class:`~django.views.generic.base.View` class, which
|
||||
handles linking the view in to the URLs, HTTP method dispatching and other
|
||||
simple features. :class:`~django.views.generic.base.RedirectView` is for a simple HTTP
|
||||
redirect, and :class:`~django.views.generic.base.TemplateView` extends the base class
|
||||
to make it also render a template.
|
||||
|
||||
|
||||
Simple usage
|
||||
============
|
||||
|
||||
Class-based generic views (and any class-based views that inherit from
|
||||
the base classes Django provides) can be configured in two
|
||||
ways: subclassing, or passing in arguments directly in the URLconf.
|
||||
|
||||
When you subclass a class-based view, you can override attributes
|
||||
(such as the ``template_name``) or methods (such as ``get_context_data``)
|
||||
in your subclass to provide new values or methods. Consider, for example,
|
||||
a view that just displays one template, ``about.html``. Django has a
|
||||
generic view to do this - :class:`~django.views.generic.base.TemplateView` -
|
||||
so we can just subclass it, and override the template name::
|
||||
|
||||
# some_app/views.py
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
class AboutView(TemplateView):
|
||||
template_name = "about.html"
|
||||
|
||||
Then, we just need to add this new view into our URLconf. As the class-based
|
||||
views themselves are classes, we point the URL to the ``as_view`` class method
|
||||
instead, which is the entry point for class-based views::
|
||||
|
||||
# urls.py
|
||||
from django.conf.urls import patterns, url, include
|
||||
from some_app.views import AboutView
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^about/', AboutView.as_view()),
|
||||
)
|
||||
|
||||
Alternatively, if you're only changing a few simple attributes on a
|
||||
class-based view, you can simply pass the new attributes into the ``as_view``
|
||||
method call itself::
|
||||
|
||||
from django.conf.urls import patterns, url, include
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^about/', TemplateView.as_view(template_name="about.html")),
|
||||
)
|
||||
|
||||
A similar overriding pattern can be used for the ``url`` attribute on
|
||||
:class:`~django.views.generic.base.RedirectView`.
|
||||
|
||||
.. _jsonresponsemixin-example:
|
||||
|
||||
More than just HTML
|
||||
-------------------
|
||||
|
||||
Where class based views shine is when you want to do the same thing many times.
|
||||
Suppose you're writing an API, and every view should return JSON instead of
|
||||
rendered HTML.
|
||||
|
||||
We can use create a mixin class to use in all of our views, handling the
|
||||
conversion to JSON once.
|
||||
|
||||
For example, a simple JSON mixin might look something like this::
|
||||
|
||||
import json
|
||||
from django import http
|
||||
|
||||
class JSONResponseMixin(object):
|
||||
"""
|
||||
A mixin that can be used to render a JSON response.
|
||||
"""
|
||||
reponse_class = HTTPResponse
|
||||
|
||||
def render_to_response(self, context, **response_kwargs):
|
||||
"""
|
||||
Returns a JSON response, transforming 'context' to make the payload.
|
||||
"""
|
||||
response_kwargs['content_type'] = 'application/json'
|
||||
return self.response_class(
|
||||
self.convert_context_to_json(context),
|
||||
**response_kwargs
|
||||
)
|
||||
|
||||
def convert_context_to_json(self, context):
|
||||
"Convert the context dictionary into a JSON object"
|
||||
# Note: This is *EXTREMELY* naive; in reality, you'll need
|
||||
# to do much more complex handling to ensure that arbitrary
|
||||
# objects -- such as Django model instances or querysets
|
||||
# -- can be serialized as JSON.
|
||||
return json.dumps(context)
|
||||
|
||||
Now we mix this into the base view::
|
||||
|
||||
from django.views.generic import View
|
||||
|
||||
class JSONView(JSONResponseMixin, View):
|
||||
pass
|
||||
|
||||
Equally we could use our mixin with one of the generic views. We can make our
|
||||
own version of :class:`~django.views.generic.detail.DetailView` by mixing
|
||||
:class:`JSONResponseMixin` with the
|
||||
:class:`~django.views.generic.detail.BaseDetailView` -- (the
|
||||
:class:`~django.views.generic.detail.DetailView` before template
|
||||
rendering behavior has been mixed in)::
|
||||
|
||||
class JSONDetailView(JSONResponseMixin, BaseDetailView):
|
||||
pass
|
||||
|
||||
This view can then be deployed in the same way as any other
|
||||
:class:`~django.views.generic.detail.DetailView`, with exactly the
|
||||
same behavior -- except for the format of the response.
|
||||
|
||||
If you want to be really adventurous, you could even mix a
|
||||
:class:`~django.views.generic.detail.DetailView` subclass that is able
|
||||
to return *both* HTML and JSON content, depending on some property of
|
||||
the HTTP request, such as a query argument or a HTTP header. Just mix
|
||||
in both the :class:`JSONResponseMixin` and a
|
||||
:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`,
|
||||
and override the implementation of :func:`render_to_response()` to defer
|
||||
to the appropriate subclass depending on the type of response that the user
|
||||
requested::
|
||||
|
||||
class HybridDetailView(JSONResponseMixin, SingleObjectTemplateResponseMixin, BaseDetailView):
|
||||
def render_to_response(self, context):
|
||||
# Look for a 'format=json' GET argument
|
||||
if self.request.GET.get('format','html') == 'json':
|
||||
return JSONResponseMixin.render_to_response(self, context)
|
||||
else:
|
||||
return SingleObjectTemplateResponseMixin.render_to_response(self, context)
|
||||
|
||||
Because of the way that Python resolves method overloading, the local
|
||||
``render_to_response()`` implementation will override the versions provided by
|
||||
:class:`JSONResponseMixin` and
|
||||
:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`.
|
||||
|
||||
For more information on how to use the built in generic views, consult the next
|
||||
topic on :doc:`generic class based views</topics/class-based-views/generic-display>`.
|
||||
|
||||
Decorating class-based views
|
||||
============================
|
||||
|
||||
.. highlightlang:: python
|
||||
|
||||
The extension of class-based views isn't limited to using mixins. You
|
||||
can use also use decorators.
|
||||
|
||||
Decorating in URLconf
|
||||
---------------------
|
||||
|
||||
The simplest way of decorating class-based views is to decorate the
|
||||
result of the :meth:`~django.views.generic.base.View.as_view` method.
|
||||
The easiest place to do this is in the URLconf where you deploy your
|
||||
view::
|
||||
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
from .views import VoteView
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^about/', login_required(TemplateView.as_view(template_name="secret.html"))),
|
||||
(r'^vote/', permission_required('polls.can_vote')(VoteView.as_view())),
|
||||
)
|
||||
|
||||
This approach applies the decorator on a per-instance basis. If you
|
||||
want every instance of a view to be decorated, you need to take a
|
||||
different approach.
|
||||
|
||||
.. _decorating-class-based-views:
|
||||
|
||||
Decorating the class
|
||||
--------------------
|
||||
|
||||
To decorate every instance of a class-based view, you need to decorate
|
||||
the class definition itself. To do this you apply the decorator to the
|
||||
:meth:`~django.views.generic.base.View.dispatch` method of the class.
|
||||
|
||||
A method on a class isn't quite the same as a standalone function, so
|
||||
you can't just apply a function decorator to the method -- you need to
|
||||
transform it into a method decorator first. The ``method_decorator``
|
||||
decorator transforms a function decorator into a method decorator so
|
||||
that it can be used on an instance method. For example::
|
||||
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
class ProtectedView(TemplateView):
|
||||
template_name = 'secret.html'
|
||||
|
||||
@method_decorator(login_required)
|
||||
def dispatch(self, *args, **kwargs):
|
||||
return super(ProtectedView, self).dispatch(*args, **kwargs)
|
||||
|
||||
In this example, every instance of ``ProtectedView`` will have
|
||||
login protection.
|
||||
|
||||
.. note::
|
||||
|
||||
``method_decorator`` passes ``*args`` and ``**kwargs``
|
||||
as parameters to the decorated method on the class. If your method
|
||||
does not accept a compatible set of parameters it will raise a
|
||||
``TypeError`` exception.
|
|
@ -0,0 +1,605 @@
|
|||
===================================
|
||||
Using mixins with class-based views
|
||||
===================================
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
.. caution::
|
||||
|
||||
This is an advanced topic. A working knowledge of :doc:`Django's
|
||||
class-based views<index>` is advised before exploring these
|
||||
techniques.
|
||||
|
||||
Django's built-in class-based views provide a lot of functionality,
|
||||
but some of it you may want to use separately. For instance, you may
|
||||
want to write a view that renders a template to make the HTTP
|
||||
response, but you can't use
|
||||
:class:`~django.views.generic.base.TemplateView`; perhaps you need to
|
||||
render a template only on `POST`, with `GET` doing something else
|
||||
entirely. While you could use
|
||||
:class:`~django.template.response.TemplateResponse` directly, this
|
||||
will likely result in duplicate code.
|
||||
|
||||
For this reason, Django also provides a number of mixins that provide
|
||||
more discrete functionality. Template rendering, for instance, is
|
||||
encapsulated in the
|
||||
:class:`~django.views.generic.base.TemplateResponseMixin`. The Django
|
||||
reference documentation contains :doc:`full documentation of all the
|
||||
mixins</ref/class-based-views/mixins>`.
|
||||
|
||||
Context and template responses
|
||||
==============================
|
||||
|
||||
Two central mixins are provided that help in providing a consistent
|
||||
interface to working with templates in class-based views.
|
||||
|
||||
:class:`~django.views.generic.base.TemplateResponseMixin`
|
||||
Every built in view which returns a
|
||||
:class:`~django.template.response.TemplateResponse` will call the
|
||||
:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
|
||||
method that :class:`TemplateResponseMixin` provides. Most of the time this
|
||||
will be called for you (for instance, it is called by the ``get()`` method
|
||||
implemented by both :class:`~django.views.generic.base.TemplateView` and
|
||||
:class:`~django.views.generic.base.DetailView`); similarly, it's unlikely
|
||||
that you'll need to override it, although if you want your response to
|
||||
return something not rendered via a Django template then you'll want to do
|
||||
it. For an example of this, see the :ref:`JSONResponseMixin example
|
||||
<jsonresponsemixin-example>`.
|
||||
|
||||
``render_to_response`` itself calls
|
||||
:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`,
|
||||
which by default will just look up
|
||||
:attr:`~django.views.generic.base.TemplateResponseMixin.template_name` on
|
||||
the class-based view; two other mixins
|
||||
(:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`
|
||||
and
|
||||
:class:`~django.views.generic.list.MultipleObjectTemplateResponseMixin`)
|
||||
override this to provide more flexible defaults when dealing with actual
|
||||
objects.
|
||||
|
||||
.. versionadded:: 1.5
|
||||
|
||||
:class:`~django.views.generic.base.ContextMixin`
|
||||
Every built in view which needs context data, such as for rendering a
|
||||
template (including :class:`TemplateResponseMixin` above), should call
|
||||
:meth:`~django.views.generic.base.ContextMixin.get_context_data` passing
|
||||
any data they want to ensure is in there as keyword arguments.
|
||||
``get_context_data`` returns a dictionary; in :class:`ContextMixin` it
|
||||
simply returns its keyword arguments, but it is common to override this to
|
||||
add more members to the dictionary.
|
||||
|
||||
Building up Django's generic class-based views
|
||||
===============================================
|
||||
|
||||
Let's look at how two of Django's generic class-based views are built
|
||||
out of mixins providing discrete functionality. We'll consider
|
||||
:class:`~django.views.generic.detail.DetailView`, which renders a
|
||||
"detail" view of an object, and
|
||||
:class:`~django.views.generic.list.ListView`, which will render a list
|
||||
of objects, typically from a queryset, and optionally paginate
|
||||
them. This will introduce us to four mixins which between them provide
|
||||
useful functionality when working with either a single Django object,
|
||||
or multiple objects.
|
||||
|
||||
There are also mixins involved in the generic edit views
|
||||
(:class:`~django.views.generic.edit.FormView`, and the model-specific
|
||||
views :class:`~django.views.generic.edit.CreateView`,
|
||||
:class:`~django.views.generic.edit.UpdateView` and
|
||||
:class:`~django.views.generic.edit.DeleteView`), and in the
|
||||
date-based generic views. These are
|
||||
covered in the :doc:`mixin reference
|
||||
documentation</ref/class-based-views/mixins>`.
|
||||
|
||||
DetailView: working with a single Django object
|
||||
-----------------------------------------------
|
||||
|
||||
To show the detail of an object, we basically need to do two things:
|
||||
we need to look up the object and then we need to make a
|
||||
:class:`TemplateResponse` with a suitable template, and that object as
|
||||
context.
|
||||
|
||||
To get the object, :class:`~django.views.generic.detail.DetailView`
|
||||
relies on :class:`~django.views.generic.detail.SingleObjectMixin`,
|
||||
which provides a
|
||||
:meth:`~django.views.generic.detail.SingleObjectMixin.get_object`
|
||||
method that figures out the object based on the URL of the request (it
|
||||
looks for ``pk`` and ``slug`` keyword arguments as declared in the
|
||||
URLConf, and looks the object up either from the
|
||||
:attr:`~django.views.generic.detail.SingleObjectMixin.model` attribute
|
||||
on the view, or the
|
||||
:attr:`~django.views.generic.detail.SingleObjectMixin.queryset`
|
||||
attribute if that's provided). :class:`SingleObjectMixin` also overrides
|
||||
:meth:`~django.views.generic.base.ContextMixin.get_context_data`,
|
||||
which is used across all Django's built in class-based views to supply
|
||||
context data for template renders.
|
||||
|
||||
To then make a :class:`TemplateResponse`, :class:`DetailView` uses
|
||||
:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`,
|
||||
which extends
|
||||
:class:`~django.views.generic.base.TemplateResponseMixin`, overriding
|
||||
:meth:`get_template_names()` as discussed above. It actually provides
|
||||
a fairly sophisticated set of options, but the main one that most
|
||||
people are going to use is
|
||||
``<app_label>/<object_name>_detail.html``. The ``_detail`` part can be
|
||||
changed by setting
|
||||
:attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_suffix`
|
||||
on a subclass to something else. (For instance, the :doc:`generic edit
|
||||
views<generic-editing>` use ``_form`` for create and update views, and
|
||||
``_confirm_delete`` for delete views.)
|
||||
|
||||
ListView: working with many Django objects
|
||||
------------------------------------------
|
||||
|
||||
Lists of objects follow roughly the same pattern: we need a (possibly
|
||||
paginated) list of objects, typically a :class:`QuerySet`, and then we need
|
||||
to make a :class:`TemplateResponse` with a suitable template using
|
||||
that list of objects.
|
||||
|
||||
To get the objects, :class:`~django.views.generic.list.ListView` uses
|
||||
:class:`~django.views.generic.list.MultipleObjectMixin`, which
|
||||
provides both
|
||||
:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`
|
||||
and
|
||||
:meth:`~django.views.generic.list.MultipleObjectMixin.paginate_queryset`. Unlike
|
||||
with :class:`SingleObjectMixin`, there's no need to key off parts of
|
||||
the URL to figure out the queryset to work with, so the default just
|
||||
uses the
|
||||
:attr:`~django.views.generic.list.MultipleObjectMixin.queryset` or
|
||||
:attr:`~django.views.generic.list.MultipleObjectMixin.model` attribute
|
||||
on the view class. A common reason to override
|
||||
:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`
|
||||
here would be to dynamically vary the objects, such as depending on
|
||||
the current user or to exclude posts in the future for a blog.
|
||||
|
||||
:class:`MultipleObjectMixin` also overrides
|
||||
:meth:`~django.views.generic.base.ContextMixin.get_context_data` to
|
||||
include appropriate context variables for pagination (providing
|
||||
dummies if pagination is disabled). It relies on ``object_list`` being
|
||||
passed in as a keyword argument, which :class:`ListView` arranges for
|
||||
it.
|
||||
|
||||
To make a :class:`TemplateResponse`, :class:`ListView` then uses
|
||||
:class:`~django.views.generic.list.MultipleObjectTemplateResponseMixin`;
|
||||
as with :class:`SingleObjectTemplateResponseMixin` above, this
|
||||
overrides :meth:`get_template_names()` to provide :meth:`a range of
|
||||
options
|
||||
<~django.views.generic.list.MultipleObjectTempalteResponseMixin>`,
|
||||
with the most commonly-used being
|
||||
``<app_label>/<object_name>_list.html``, with the ``_list`` part again
|
||||
being taken from the
|
||||
:attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix`
|
||||
attribute. (The date based generic views use suffixes such as ``_archive``,
|
||||
``_archive_year`` and so on to use different templates for the various
|
||||
specialised date-based list views.)
|
||||
|
||||
Using Django's class-based view mixins
|
||||
======================================
|
||||
|
||||
Now we've seen how Django's generic class-based views use the provided
|
||||
mixins, let's look at other ways we can combine them. Of course we're
|
||||
still going to be combining them with either built-in class-based
|
||||
views, or other generic class-based views, but there are a range of
|
||||
rarer problems you can solve than are provided for by Django out of
|
||||
the box.
|
||||
|
||||
.. warning::
|
||||
|
||||
Not all mixins can be used together, and not all generic class
|
||||
based views can be used with all other mixins. Here we present a
|
||||
few examples that do work; if you want to bring together other
|
||||
functionality then you'll have to consider interactions between
|
||||
attributes and methods that overlap between the different classes
|
||||
you're using, and how `method resolution order`_ will affect which
|
||||
versions of the methods will be called in what order.
|
||||
|
||||
The reference documentation for Django's :doc:`class-based
|
||||
views</ref/class-based-views/index>` and :doc:`class-based view
|
||||
mixins</ref/class-based-views/mixins>` will help you in
|
||||
understanding which attributes and methods are likely to cause
|
||||
conflict between different classes and mixins.
|
||||
|
||||
If in doubt, it's often better to back off and base your work on
|
||||
:class:`View` or :class:`TemplateView`, perhaps with
|
||||
:class:`SimpleObjectMixin` and
|
||||
:class:`MultipleObjectMixin`. Although you will probably end up
|
||||
writing more code, it is more likely to be clearly understandable
|
||||
to someone else coming to it later, and with fewer interactions to
|
||||
worry about you will save yourself some thinking. (Of course, you
|
||||
can always dip into Django's implementation of the generic class
|
||||
based views for inspiration on how to tackle problems.)
|
||||
|
||||
.. _method resolution order: http://www.python.org/download/releases/2.3/mro/
|
||||
|
||||
|
||||
Using SingleObjectMixin with View
|
||||
---------------------------------
|
||||
|
||||
If we want to write a simple class-based view that responds only to
|
||||
``POST``, we'll subclass :class:`~django.views.generic.base.View` and
|
||||
write a ``post()`` method in the subclass. However if we want our
|
||||
processing to work on a particular object, identified from the URL,
|
||||
we'll want the functionality provided by
|
||||
:class:`~django.views.generic.detail.SingleObjectMixin`.
|
||||
|
||||
We'll demonstrate this with the publisher modelling we used in the
|
||||
:doc:`generic class-based views
|
||||
introduction<generic-display>`.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# views.py
|
||||
from django.http import HttpResponseForbidden, HttpResponseRedirect
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.views.generic import View
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
from books.models import Author
|
||||
|
||||
class RecordInterest(View, SingleObjectMixin):
|
||||
"""Records the current user's interest in an author."""
|
||||
model = Author
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
if not request.user.is_authenticated():
|
||||
return HttpResponseForbidden()
|
||||
|
||||
# Look up the author we're interested in.
|
||||
self.object = self.get_object()
|
||||
# Actually record interest somehow here!
|
||||
|
||||
return HttpResponseRedirect(reverse('author-detail', kwargs={'pk': self.object.pk}))
|
||||
|
||||
In practice you'd probably want to record the interest in a key-value
|
||||
store rather than in a relational database, so we've left that bit
|
||||
out. The only bit of the view that needs to worry about using
|
||||
:class:`SingleObjectMixin` is where we want to look up the author
|
||||
we're interested in, which it just does with a simple call to
|
||||
``self.get_object()``. Everything else is taken care of for us by the
|
||||
mixin.
|
||||
|
||||
We can hook this into our URLs easily enough:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# urls.py
|
||||
from books.views import RecordInterest
|
||||
|
||||
urlpatterns = patterns('',
|
||||
#...
|
||||
url(r'^author/(?P<pk>\d+)/interest/$', RecordInterest.as_view(), name='author-interest'),
|
||||
)
|
||||
|
||||
Note the ``pk`` named group, which
|
||||
:meth:`~django.views.generic.detail.SingleObjectMixin.get_object` uses
|
||||
to look up the :class:`Author` instance. You could also use a slug, or
|
||||
any of the other features of :class:`SingleObjectMixin`.
|
||||
|
||||
Using SingleObjectMixin with ListView
|
||||
-------------------------------------
|
||||
|
||||
:class:`~django.views.generic.list.ListView` provides built-in
|
||||
pagination, but you might want to paginate a list of objects that are
|
||||
all linked (by a foreign key) to another object. In our publishing
|
||||
example, you might want to paginate through all the books by a
|
||||
particular publisher.
|
||||
|
||||
One way to do this is to combine :class:`ListView` with
|
||||
:class:`SingleObjectMixin`, so that the queryset for the paginated
|
||||
list of books can hang off the publisher found as the single
|
||||
object. In order to do this, we need to have two different querysets:
|
||||
|
||||
**Publisher queryset for use in get_object**
|
||||
We'll set that up directly when we call :meth:`get_object()`.
|
||||
|
||||
**Book queryset for use by ListView**
|
||||
We'll figure that out ourselves in :meth:`get_queryset()` so we
|
||||
can take into account the Publisher we're looking at.
|
||||
|
||||
.. highlightlang:: python
|
||||
|
||||
.. note::
|
||||
|
||||
We have to think carefully about :meth:`get_context_data()`.
|
||||
Since both :class:`SingleObjectMixin` and :class:`ListView` will
|
||||
put things in the context data under the value of
|
||||
:attr:`context_object_name` if it's set, we'll instead explictly
|
||||
ensure the Publisher is in the context data. :class:`ListView`
|
||||
will add in the suitable ``page_obj`` and ``paginator`` for us
|
||||
providing we remember to call ``super()``.
|
||||
|
||||
Now we can write a new :class:`PublisherDetail`::
|
||||
|
||||
from django.views.generic import ListView
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
from books.models import Publisher
|
||||
|
||||
class PublisherDetail(SingleObjectMixin, ListView):
|
||||
paginate_by = 2
|
||||
template_name = "books/publisher_detail.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs['publisher'] = self.object
|
||||
return super(PublisherDetail, self).get_context_data(**kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
self.object = self.get_object(Publisher.objects.all())
|
||||
return self.object.book_set.all()
|
||||
|
||||
Notice how we set ``self.object`` within :meth:`get_queryset` so we
|
||||
can use it again later in :meth:`get_context_data`. If you don't set
|
||||
:attr:`template_name`, the template will default to the normal
|
||||
:class:`ListView` choice, which in this case would be
|
||||
``"books/book_list.html"`` because it's a list of books;
|
||||
:class:`ListView` knows nothing about :class:`SingleObjectMixin`, so
|
||||
it doesn't have any clue this view is anything to do with a Publisher.
|
||||
|
||||
.. highlightlang:: html+django
|
||||
|
||||
The ``paginate_by`` is deliberately small in the example so you don't
|
||||
have to create lots of books to see the pagination working! Here's the
|
||||
template you'd want to use::
|
||||
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Publisher {{ publisher.name }}</h2>
|
||||
|
||||
<ol>
|
||||
{% for book in page_obj %}
|
||||
<li>{{ book.title }}</li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
|
||||
<div class="pagination">
|
||||
<span class="step-links">
|
||||
{% if page_obj.has_previous %}
|
||||
<a href="?page={{ page_obj.previous_page_number }}">previous</a>
|
||||
{% endif %}
|
||||
|
||||
<span class="current">
|
||||
Page {{ page_obj.number }} of {{ paginator.num_pages }}.
|
||||
</span>
|
||||
|
||||
{% if page_obj.has_next %}
|
||||
<a href="?page={{ page_obj.next_page_number }}">next</a>
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
Avoid anything more complex
|
||||
===========================
|
||||
|
||||
Generally you can use
|
||||
:class:`~django.views.generic.base.TemplateResponseMixin` and
|
||||
:class:`~django.views.generic.detail.SingleObjectMixin` when you need
|
||||
their functionality. As shown above, with a bit of care you can even
|
||||
combine :class:`SingleObjectMixin` with
|
||||
:class:`~django.views.generic.list.ListView`. However things get
|
||||
increasingly complex as you try to do so, and a good rule of thumb is:
|
||||
|
||||
.. hint::
|
||||
|
||||
Each of your views should use only mixins or views from one of the
|
||||
groups of generic class-based views: :doc:`detail,
|
||||
list<generic-display>`, :doc:`editing<generic-editing>` and
|
||||
date. For example it's fine to combine
|
||||
:class:`TemplateView` (built in view) with
|
||||
:class:`MultipleObjectMixin` (generic list), but you're likely to
|
||||
have problems combining :class:`SingleObjectMixin` (generic
|
||||
detail) with :class:`MultipleObjectMixin` (generic list).
|
||||
|
||||
To show what happens when you try to get more sophisticated, we show
|
||||
an example that sacrifices readability and maintainability when there
|
||||
is a simpler solution. First, let's look at a naive attempt to combine
|
||||
:class:`~django.views.generic.detail.DetailView` with
|
||||
:class:`~django.views.generic.edit.FormMixin` to enable use to
|
||||
``POST`` a Django :class:`Form` to the same URL as we're displaying an
|
||||
object using :class:`DetailView`.
|
||||
|
||||
Using FormMixin with DetailView
|
||||
-------------------------------
|
||||
|
||||
Think back to our earlier example of using :class:`View` and
|
||||
:class:`SingleObjectMixin` together. We were recording a user's
|
||||
interest in a particular author; say now that we want to let them
|
||||
leave a message saying why they like them. Again, let's assume we're
|
||||
not going to store this in a relational database but instead in
|
||||
something more esoteric that we won't worry about here.
|
||||
|
||||
At this point it's natural to reach for a :class:`Form` to encapsulate
|
||||
the information sent from the user's browser to Django. Say also that
|
||||
we're heavily invested in `REST`_, so we want to use the same URL for
|
||||
displaying the author as for capturing the message from the
|
||||
user. Let's rewrite our :class:`AuthorDetailView` to do that.
|
||||
|
||||
.. _REST: http://en.wikipedia.org/wiki/Representational_state_transfer
|
||||
|
||||
We'll keep the ``GET`` handling from :class:`DetailView`, although
|
||||
we'll have to add a :class:`Form` into the context data so we can
|
||||
render it in the template. We'll also want to pull in form processing
|
||||
from :class:`~django.views.generic.edit.FormMixin`, and write a bit of
|
||||
code so that on ``POST`` the form gets called appropriately.
|
||||
|
||||
.. note::
|
||||
|
||||
We use :class:`FormMixin` and implement :meth:`post()` ourselves
|
||||
rather than try to mix :class:`DetailView` with :class:`FormView`
|
||||
(which provides a suitable :meth:`post()` already) because both of
|
||||
the views implement :meth:`get()`, and things would get much more
|
||||
confusing.
|
||||
|
||||
Our new :class:`AuthorDetail` looks like this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# CAUTION: you almost certainly do not want to do this.
|
||||
# It is provided as part of a discussion of problems you can
|
||||
# run into when combining different generic class-based view
|
||||
# functionality that is not designed to be used together.
|
||||
|
||||
from django import forms
|
||||
from django.http import HttpResponseForbidden
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.views.generic import DetailView
|
||||
from django.views.generic.edit import FormMixin
|
||||
|
||||
class AuthorInterestForm(forms.Form):
|
||||
message = forms.CharField()
|
||||
|
||||
class AuthorDetail(DetailView, FormMixin):
|
||||
model = Author
|
||||
form_class = AuthorInterestForm
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse(
|
||||
'author-detail',
|
||||
kwargs = {'pk': self.object.pk},
|
||||
)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
form_class = self.get_form_class()
|
||||
form = self.get_form(form_class)
|
||||
context = {
|
||||
'form': form
|
||||
}
|
||||
context.update(kwargs)
|
||||
return super(AuthorDetail, self).get_context_data(**context)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
form_class = self.get_form_class()
|
||||
form = self.get_form(form_class)
|
||||
if form.is_valid():
|
||||
return self.form_valid(form)
|
||||
else:
|
||||
return self.form_invalid(form)
|
||||
|
||||
def form_valid(self, form):
|
||||
if not self.request.user.is_authenticated():
|
||||
return HttpResponseForbidden()
|
||||
self.object = self.get_object()
|
||||
# record the interest using the message in form.cleaned_data
|
||||
return super(AuthorDetail, self).form_valid(form)
|
||||
|
||||
:meth:`get_success_url()` is just providing somewhere to redirect to,
|
||||
which gets used in the default implementation of
|
||||
:meth:`form_valid()`. We have to provide our own :meth:`post()` as
|
||||
noted earlier, and override :meth:`get_context_data()` to make the
|
||||
:class:`Form` available in the context data.
|
||||
|
||||
A better solution
|
||||
-----------------
|
||||
|
||||
It should be obvious that the number of subtle interactions between
|
||||
:class:`FormMixin` and :class:`DetailView` is already testing our
|
||||
ability to manage things. It's unlikely you'd want to write this kind
|
||||
of class yourself.
|
||||
|
||||
In this case, it would be fairly easy to just write the :meth:`post()`
|
||||
method yourself, keeping :class:`DetailView` as the only generic
|
||||
functionality, although writing :class:`Form` handling code involves a
|
||||
lot of duplication.
|
||||
|
||||
Alternatively, it would still be easier than the above approach to
|
||||
have a separate view for processing the form, which could use
|
||||
:class:`~django.views.generic.edit.FormView` distinct from
|
||||
:class:`DetailView` without concerns.
|
||||
|
||||
An alternative better solution
|
||||
------------------------------
|
||||
|
||||
What we're really trying to do here is to use two different class
|
||||
based views from the same URL. So why not do just that? We have a very
|
||||
clear division here: ``GET`` requests should get the
|
||||
:class:`DetailView` (with the :class:`Form` added to the context
|
||||
data), and ``POST`` requests should get the :class:`FormView`. Let's
|
||||
set up those views first.
|
||||
|
||||
The :class:`AuthorDisplay` view is almost the same as :ref:`when we
|
||||
first introduced AuthorDetail<generic-views-extra-work>`; we have to
|
||||
write our own :meth:`get_context_data()` to make the
|
||||
:class:`AuthorInterestForm` available to the template. We'll skip the
|
||||
:meth:`get_object()` override from before for clarity.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from django.views.generic import DetailView
|
||||
from django import forms
|
||||
from books.models import Author
|
||||
|
||||
class AuthorInterestForm(forms.Form):
|
||||
message = forms.CharField()
|
||||
|
||||
class AuthorDisplay(DetailView):
|
||||
|
||||
queryset = Author.objects.all()
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'form': AuthorInterestForm(),
|
||||
}
|
||||
context.update(kwargs)
|
||||
return super(AuthorDisplay, self).get_context_data(**context)
|
||||
|
||||
Then the :class:`AuthorInterest` is a simple :class:`FormView`, but we
|
||||
have to bring in :class:`SingleObjectMixin` so we can find the author
|
||||
we're talking about, and we have to remember to set
|
||||
:attr:`template_name` to ensure that form errors will render the same
|
||||
template as :class:`AuthorDisplay` is using on ``GET``.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from django.views.generic import FormView
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
|
||||
class AuthorInterest(FormView, SingleObjectMixin):
|
||||
template_name = 'books/author_detail.html'
|
||||
form_class = AuthorInterestForm
|
||||
model = Author
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'object': self.get_object(),
|
||||
}
|
||||
return super(AuthorInterest, self).get_context_data(**context)
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse(
|
||||
'author-detail',
|
||||
kwargs = {'pk': self.object.pk},
|
||||
)
|
||||
|
||||
def form_valid(self, form):
|
||||
if not self.request.user.is_authenticated():
|
||||
return HttpResponseForbidden()
|
||||
self.object = self.get_object()
|
||||
# record the interest using the message in form.cleaned_data
|
||||
return super(AuthorInterest, self).form_valid(form)
|
||||
|
||||
Finally we bring this together in a new :class:`AuthorDetail` view. We
|
||||
already know that calling :meth:`as_view()` on a class-based view
|
||||
gives us something that behaves exactly like a function based view, so
|
||||
we can do that at the point we choose between the two subviews.
|
||||
|
||||
You can of course pass through keyword arguments to :meth:`as_view()`
|
||||
in the same way you would in your URLconf, such as if you wanted the
|
||||
:class:`AuthorInterest` behaviour to also appear at another URL but
|
||||
using a different template.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from django.views.generic import View
|
||||
|
||||
class AuthorDetail(View):
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
view = AuthorDisplay.as_view()
|
||||
return view(request, *args, **kwargs)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
view = AuthorInterest.as_view()
|
||||
return view(request, *args, **kwargs)
|
||||
|
||||
This approach can also be used with any other generic class-based
|
||||
views or your own class-based views inheriting directly from
|
||||
:class:`View` or :class:`TemplateView`, as it keeps the different
|
||||
views as separate as possible.
|
|
@ -82,6 +82,8 @@ If your form is going to be used to directly add or edit a Django model, you can
|
|||
use a :doc:`ModelForm </topics/forms/modelforms>` to avoid duplicating your model
|
||||
description.
|
||||
|
||||
.. _using-a-form-in-a-view:
|
||||
|
||||
Using a form in a view
|
||||
----------------------
|
||||
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
Generic views
|
||||
=============
|
||||
|
||||
See :doc:`/ref/class-based-views`.
|
||||
See :doc:`/ref/class-based-views/index`.
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue