mirror of
https://github.com/django/django.git
synced 2025-08-03 18:38:50 +00:00
Fixed #19516 - Fixed remaining broken links.
Added -n to sphinx builds to catch issues going forward.
This commit is contained in:
parent
3f890f8dc7
commit
9b5f64cc6e
83 changed files with 727 additions and 611 deletions
|
@ -257,9 +257,9 @@ 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).
|
||||
for more information about :class:`~django.db.models.query.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::
|
||||
|
@ -312,9 +312,9 @@ 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.
|
||||
:meth:`~django.views.generic.list.MultipleObjectMixin.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
|
||||
|
|
|
@ -7,10 +7,10 @@ Form processing generally has 3 paths:
|
|||
* 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.
|
||||
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
|
||||
-----------
|
||||
|
@ -28,7 +28,7 @@ Given a simple contact form::
|
|||
# send email using the self.cleaned_data dictionary
|
||||
pass
|
||||
|
||||
The view can be constructed using a FormView::
|
||||
The view can be constructed using a ``FormView``::
|
||||
|
||||
# views.py
|
||||
from myapp.forms import ContactForm
|
||||
|
@ -50,42 +50,46 @@ Notes:
|
|||
* FormView inherits
|
||||
:class:`~django.views.generic.base.TemplateResponseMixin` so
|
||||
:attr:`~django.views.generic.base.TemplateResponseMixin.template_name`
|
||||
can be used here
|
||||
can be used here.
|
||||
* The default implementation for
|
||||
:meth:`~django.views.generic.edit.FormView.form_valid` simply
|
||||
redirects to the :attr:`success_url`
|
||||
:meth:`~django.views.generic.edit.FormMixin.form_valid` simply
|
||||
redirects to the :attr:`~django.views.generic.edit.FormMixin.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:
|
||||
views will automatically create a :class:`~django.forms.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
|
||||
* If the :attr:`~django.views.generic.edit.ModelFormMixin.model` attribute is
|
||||
given, that model class will be used.
|
||||
* If :meth:`~django.views.generic.detail.SingleObjectMixin.get_object()`
|
||||
returns an object, the class of that object will be used.
|
||||
* If a :attr:`~django.views.generic.detail.SingleObjectMixin.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
|
||||
Model form views provide a
|
||||
:meth:`~django.views.generic.edit.ModelFormMixin.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
|
||||
You don't even need to provide a ``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.
|
||||
:meth:`~django.db.models.Model.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
|
||||
If you want to use a custom :class:`~django.forms.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`.
|
||||
even though the :attr:`~django.views.generic.edit.FormMixin.form_class` may
|
||||
be a :class:`~django.forms.ModelForm`.
|
||||
|
||||
First we need to add :meth:`get_absolute_url()` to our :class:`Author`
|
||||
class:
|
||||
First we need to add :meth:`~django.db.models.Model.get_absolute_url()` to our
|
||||
``Author`` class:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
|
@ -137,8 +141,10 @@ Finally, we hook these new views into the URLconf::
|
|||
|
||||
.. note::
|
||||
|
||||
These views inherit :class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`
|
||||
which uses :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_prefix`
|
||||
These views inherit
|
||||
:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`
|
||||
which uses
|
||||
:attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_suffix`
|
||||
to construct the
|
||||
:attr:`~django.views.generic.base.TemplateResponseMixin.template_name`
|
||||
based on the model.
|
||||
|
@ -149,15 +155,17 @@ Finally, we hook these new views into the URLconf::
|
|||
* :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.
|
||||
:class:`UpdateView`, you can set either
|
||||
:attr:`~django.views.generic.base.TemplateResponseMixin.template_name` or
|
||||
:attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.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::
|
||||
you can use a custom :class:`~django.forms.ModelForm` to do this. First, add
|
||||
the foreign key relation to the model::
|
||||
|
||||
# models.py
|
||||
from django.contrib.auth import User
|
||||
|
@ -169,7 +177,7 @@ foreign key relation to the model::
|
|||
|
||||
# ...
|
||||
|
||||
Create a custom :class:`ModelForm` in order to exclude the
|
||||
Create a custom :class:`~django.forms.ModelForm` in order to exclude the
|
||||
``created_by`` field and prevent the user from editing it:
|
||||
|
||||
.. code-block:: python
|
||||
|
@ -183,8 +191,10 @@ Create a custom :class:`ModelForm` in order to exclude the
|
|||
model = Author
|
||||
exclude = ('created_by',)
|
||||
|
||||
In the view, use the custom :attr:`form_class` and override
|
||||
:meth:`form_valid()` to add the user::
|
||||
In the view, use the custom
|
||||
:attr:`~django.views.generic.edit.FormMixin.form_class` and override
|
||||
:meth:`~django.views.generic.edit.ModelFormMixin.form_valid()` to add the
|
||||
user::
|
||||
|
||||
# views.py
|
||||
from django.views.generic.edit import CreateView
|
||||
|
@ -202,7 +212,8 @@ In the view, use the custom :attr:`form_class` and override
|
|||
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()`.
|
||||
alternatively handle unauthorized users in the
|
||||
:meth:`~django.views.generic.edit.ModelFormMixin.form_valid()`.
|
||||
|
||||
AJAX example
|
||||
------------
|
||||
|
|
|
@ -32,13 +32,14 @@ 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
|
||||
method that ``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
|
||||
:class:`~django.views.generic.detail.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
|
||||
|
@ -59,10 +60,10 @@ interface to working with templates in class-based views.
|
|||
|
||||
: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
|
||||
template (including ``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
|
||||
``get_context_data`` returns a dictionary; in ``ContextMixin`` it
|
||||
simply returns its keyword arguments, but it is common to override this to
|
||||
add more members to the dictionary.
|
||||
|
||||
|
@ -106,7 +107,7 @@ 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
|
||||
attribute if that's provided). ``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.
|
||||
|
@ -115,10 +116,12 @@ To then make a :class:`~django.template.response.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
|
||||
overriding
|
||||
:meth:`~django.views.generic.base.TemplateResponseMixin.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
|
||||
|
@ -128,9 +131,10 @@ 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.
|
||||
paginated) list of objects, typically a
|
||||
:class:`~django.db.models.query.QuerySet`, and then we need to make a
|
||||
:class:`~django.template.response.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
|
||||
|
@ -138,9 +142,9 @@ 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
|
||||
with :class:`~django.views.generic.detail.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
|
||||
|
@ -148,19 +152,19 @@ on the view class. A common reason to override
|
|||
here would be to dynamically vary the objects, such as depending on
|
||||
the current user or to exclude posts in the future for a blog.
|
||||
|
||||
:class:`MultipleObjectMixin` also overrides
|
||||
:class:`~django.views.generic.list.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
|
||||
To make a :class:`~django.template.response.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>`,
|
||||
as with :class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`
|
||||
above, this overrides ``get_template_names()`` to provide :meth:`a range of
|
||||
options <django.views.generic.list.MultipleObjectTemplateResponseMixin>`,
|
||||
with the most commonly-used being
|
||||
``<app_label>/<object_name>_list.html``, with the ``_list`` part again
|
||||
being taken from the
|
||||
|
@ -197,13 +201,13 @@ the box.
|
|||
|
||||
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.)
|
||||
:class:`~django.views.generic.detail.SingleObjectMixin` and
|
||||
:class:`~django.views.generic.list.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/
|
||||
|
||||
|
@ -247,9 +251,9 @@ We'll demonstrate this with the publisher modelling we used in the
|
|||
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
|
||||
:class:`~django.views.generic.detail.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::
|
||||
|
@ -265,7 +269,8 @@ We can hook this into our URLs easily enough::
|
|||
Note the ``pk`` named group, which
|
||||
:meth:`~django.views.generic.detail.SingleObjectMixin.get_object` uses
|
||||
to look up the ``Author`` instance. You could also use a slug, or
|
||||
any of the other features of :class:`SingleObjectMixin`.
|
||||
any of the other features of
|
||||
:class:`~django.views.generic.detail.SingleObjectMixin`.
|
||||
|
||||
Using SingleObjectMixin with ListView
|
||||
-------------------------------------
|
||||
|
@ -277,23 +282,24 @@ 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
|
||||
:class:`~django.views.generic.detail.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()`.
|
||||
We'll set that up directly when we call ``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.
|
||||
We'll figure that out ourselves in ``get_queryset()`` so we
|
||||
can take into account the ``Publisher`` we're looking at.
|
||||
|
||||
.. note::
|
||||
|
||||
We have to think carefully about :meth:`get_context_data()`.
|
||||
Since both :class:`SingleObjectMixin` and :class:`ListView` will
|
||||
We have to think carefully about ``get_context_data()``.
|
||||
Since both :class:`~django.views.generic.detail.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
|
||||
``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()``.
|
||||
|
@ -316,13 +322,14 @@ Now we can write a new ``PublisherDetail``::
|
|||
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
|
||||
Notice how we set ``self.object`` within ``get_queryset()`` so we
|
||||
can use it again later in ``get_context_data()``. If you don't set
|
||||
``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.
|
||||
:class:`ListView` knows nothing about
|
||||
:class:`~django.views.generic.detail.SingleObjectMixin`, so it doesn't have
|
||||
any clue this view is anything to do with a Publisher.
|
||||
|
||||
.. highlightlang:: html+django
|
||||
|
||||
|
@ -365,7 +372,7 @@ 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
|
||||
combine ``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:
|
||||
|
||||
|
@ -376,48 +383,48 @@ increasingly complex as you try to do so, and a good rule of thumb is:
|
|||
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).
|
||||
:class:`~django.views.generic.list.MultipleObjectMixin` (generic list), but
|
||||
you're likely to have problems combining ``SingleObjectMixin`` (generic
|
||||
detail) with ``MultipleObjectMixin`` (generic list).
|
||||
|
||||
To show what happens when you try to get more sophisticated, we show
|
||||
an example that sacrifices readability and maintainability when there
|
||||
is a simpler solution. First, let's look at a naive attempt to combine
|
||||
: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`.
|
||||
``POST`` a Django :class:`~django.forms.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
|
||||
:class:`~django.views.generic.detail.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
|
||||
At this point it's natural to reach for a :class:`~django.forms.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 ``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
|
||||
we'll have to add a :class:`~django.forms.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
|
||||
We use :class:`~django.views.generic.edit.FormMixin` and implement
|
||||
``post()`` ourselves rather than try to mix :class:`DetailView` with
|
||||
:class:`FormView` (which provides a suitable ``post()`` already) because
|
||||
both of the views implement ``get()``, and things would get much more
|
||||
confusing.
|
||||
|
||||
.. highlightlang:: python
|
||||
|
@ -472,24 +479,24 @@ Our new ``AuthorDetail`` looks like this::
|
|||
# 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,
|
||||
``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.
|
||||
``form_valid()``. We have to provide our own ``post()`` as
|
||||
noted earlier, and override ``get_context_data()`` to make the
|
||||
:class:`~django.forms.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.
|
||||
:class:`~django.views.generic.edit.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()`
|
||||
In this case, it would be fairly easy to just write the ``post()``
|
||||
method yourself, keeping :class:`DetailView` as the only generic
|
||||
functionality, although writing :class:`Form` handling code involves a
|
||||
lot of duplication.
|
||||
functionality, although writing :class:`~django.forms.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
|
||||
|
@ -502,15 +509,15 @@ 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
|
||||
:class:`DetailView` (with the :class:`~django.forms.Form` added to the context
|
||||
data), and ``POST`` requests should get the :class:`FormView`. Let's
|
||||
set up those views first.
|
||||
|
||||
The ``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
|
||||
write our own ``get_context_data()`` to make the
|
||||
``AuthorInterestForm`` available to the template. We'll skip the
|
||||
:meth:`get_object()` override from before for clarity.
|
||||
``get_object()`` override from before for clarity.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
|
@ -533,9 +540,9 @@ write our own :meth:`get_context_data()` to make the
|
|||
return super(AuthorDisplay, self).get_context_data(**context)
|
||||
|
||||
Then the ``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
|
||||
have to bring in :class:`~django.views.generic.detail.SingleObjectMixin` so we
|
||||
can find the author we're talking about, and we have to remember to set
|
||||
``template_name`` to ensure that form errors will render the same
|
||||
template as ``AuthorDisplay`` is using on ``GET``.
|
||||
|
||||
.. code-block:: python
|
||||
|
@ -568,14 +575,14 @@ template as ``AuthorDisplay`` is using on ``GET``.
|
|||
return super(AuthorInterest, self).form_valid(form)
|
||||
|
||||
Finally we bring this together in a new ``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.
|
||||
already know that calling :meth:`~django.views.generic.base.View.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
|
||||
``AuthorInterest`` behaviour to also appear at another URL but
|
||||
using a different template.
|
||||
You can of course pass through keyword arguments to
|
||||
:meth:`~django.views.generic.base.View.as_view()` in the same way you
|
||||
would in your URLconf, such as if you wanted the ``AuthorInterest`` behavior
|
||||
to also appear at another URL but using a different template.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
|
@ -646,8 +653,8 @@ Now we mix this into the base TemplateView::
|
|||
|
||||
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
|
||||
``JSONResponseMixin`` with the
|
||||
``django.views.generic.detail.BaseDetailView`` -- (the
|
||||
:class:`~django.views.generic.detail.DetailView` before template
|
||||
rendering behavior has been mixed in)::
|
||||
|
||||
|
@ -662,11 +669,12 @@ 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
|
||||
in both the ``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::
|
||||
and override the implementation of
|
||||
:func:`~django.views.generic.base.TemplateResponseMixin.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):
|
||||
|
@ -678,5 +686,5 @@ requested::
|
|||
|
||||
Because of the way that Python resolves method overloading, the local
|
||||
``render_to_response()`` implementation will override the versions provided by
|
||||
:class:`JSONResponseMixin` and
|
||||
``JSONResponseMixin`` and
|
||||
:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue