mirror of
https://github.com/django/django.git
synced 2025-10-31 09:41:08 +00:00
Merged recent changes from trunk.
This commit is contained in:
@@ -64,11 +64,8 @@ Fields
|
||||
|
||||
.. attribute:: models.User.username
|
||||
|
||||
Required. 30 characters or fewer. Alphanumeric characters only
|
||||
(letters, digits and underscores).
|
||||
|
||||
.. versionchanged:: 1.2
|
||||
Usernames may now contain ``@``, ``+``, ``.`` and ``-`` characters.
|
||||
Required. 30 characters or fewer. Usernames may contain alphanumeric,
|
||||
``_``, ``@``, ``+``, ``.`` and ``-`` characters.
|
||||
|
||||
.. attribute:: models.User.first_name
|
||||
|
||||
@@ -101,12 +98,13 @@ Fields
|
||||
|
||||
This doesn't necessarily control whether or not the user can log in.
|
||||
Authentication backends aren't required to check for the ``is_active``
|
||||
flag, so if you want to reject a login based on ``is_active`` being
|
||||
``False``, it's up to you to check that in your own login view.
|
||||
However, the :class:`~django.contrib.auth.forms.AuthenticationForm`
|
||||
used by the :func:`~django.contrib.auth.views.login` view *does*
|
||||
perform this check, as do the permission-checking methods such as
|
||||
:meth:`~models.User.has_perm` and the authentication in the Django
|
||||
flag, and the default backends do not. If you want to reject a login
|
||||
based on ``is_active`` being ``False``, it's up to you to check that in
|
||||
your own login view or a custom authentication backend. However, the
|
||||
:class:`~django.contrib.auth.forms.AuthenticationForm` used by the
|
||||
:func:`~django.contrib.auth.views.login` view (which is the default)
|
||||
*does* perform this check, as do the permission-checking methods such
|
||||
as :meth:`~models.User.has_perm` and the authentication in the Django
|
||||
admin. All of those functions/methods will return ``False`` for
|
||||
inactive users.
|
||||
|
||||
@@ -207,8 +205,6 @@ Methods
|
||||
Returns a set of permission strings that the user has, through his/her
|
||||
groups.
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
If ``obj`` is passed in, only returns the group permissions for
|
||||
this specific object.
|
||||
|
||||
@@ -217,8 +213,6 @@ Methods
|
||||
Returns a set of permission strings that the user has, both through
|
||||
group and user permissions.
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
If ``obj`` is passed in, only returns the permissions for this
|
||||
specific object.
|
||||
|
||||
@@ -229,8 +223,6 @@ Methods
|
||||
`permissions`_ section below). If the user is inactive, this method will
|
||||
always return ``False``.
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
If ``obj`` is passed in, this method won't check for a permission for
|
||||
the model, but for this specific object.
|
||||
|
||||
@@ -241,8 +233,6 @@ Methods
|
||||
``"<app label>.<permission codename>"``. If the user is inactive,
|
||||
this method will always return ``False``.
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
If ``obj`` is passed in, this method won't check for permissions for
|
||||
the model, but for the specific object.
|
||||
|
||||
@@ -349,9 +339,6 @@ Django requires add *and* change permissions as a slight security measure.
|
||||
Changing passwords
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionadded:: 1.2
|
||||
The ``manage.py changepassword`` command was added.
|
||||
|
||||
:djadmin:`manage.py changepassword *username* <changepassword>` offers a method
|
||||
of changing a User's password from the command line. It prompts you to
|
||||
change the password of a given user which you must enter twice. If
|
||||
@@ -1046,8 +1033,6 @@ The login_required decorator
|
||||
|
||||
{% endblock %}
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
If you are using alternate authentication (see
|
||||
:ref:`authentication-backends`) you can pass a custom authentication form
|
||||
to the login view via the ``authentication_form`` parameter. This form must
|
||||
@@ -1140,8 +1125,6 @@ includes a few other useful built-in views located in
|
||||
* ``post_change_redirect``: The URL to redirect to after a successful
|
||||
password change.
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
* ``password_change_form``: A custom "change password" form which must
|
||||
accept a ``user`` keyword argument. The form is responsible for
|
||||
actually changing the user's password. Defaults to
|
||||
@@ -1465,12 +1448,13 @@ The permission_required decorator
|
||||
|
||||
.. currentmodule:: django.contrib.auth
|
||||
|
||||
Limiting access to generic views
|
||||
--------------------------------
|
||||
Applying permissions 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 apply a permission 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:
|
||||
|
||||
@@ -1493,12 +1477,13 @@ The Django admin site uses permissions as follows:
|
||||
* Access to delete an object is limited to users with the "delete"
|
||||
permission for that type of object.
|
||||
|
||||
Permissions are set globally per type of object, not per specific object
|
||||
instance. For example, it's possible to say "Mary may change news stories," but
|
||||
it's not currently possible to say "Mary may change news stories, but only the
|
||||
ones she created herself" or "Mary may only change news stories that have a
|
||||
certain status, publication date or ID." The latter functionality is something
|
||||
Django developers are currently discussing.
|
||||
Permissions can be set not only per type of object, but also per specific
|
||||
object instance. By using the
|
||||
:meth:`~django.contrib.admin.ModelAdmin.has_add_permission`,
|
||||
:meth:`~django.contrib.admin.ModelAdmin.has_change_permission` and
|
||||
:meth:`~django.contrib.admin.ModelAdmin.has_delete_permission` methods provided
|
||||
by the :class:`~django.contrib.admin.ModelAdmin` class, it is possible to
|
||||
customize permissions for different object instances of the same type.
|
||||
|
||||
Default permissions
|
||||
-------------------
|
||||
@@ -1771,7 +1756,11 @@ By default, :setting:`AUTHENTICATION_BACKENDS` is set to::
|
||||
|
||||
('django.contrib.auth.backends.ModelBackend',)
|
||||
|
||||
That's the basic authentication scheme that checks the Django users database.
|
||||
That's the basic authentication backend that checks the Django users database
|
||||
and queries the builtin permissions. It does not provide protection against
|
||||
brute force attacks via any rate limiting mechanism. You may either implement
|
||||
your own rate limiting mechanism in a custom auth backend, or use the
|
||||
mechanisms provided by most Web servers.
|
||||
|
||||
The order of :setting:`AUTHENTICATION_BACKENDS` matters, so if the same
|
||||
username and password is valid in multiple backends, Django will stop
|
||||
@@ -1791,8 +1780,9 @@ processing at the first positive match.
|
||||
Writing an authentication backend
|
||||
---------------------------------
|
||||
|
||||
An authentication backend is a class that implements two methods:
|
||||
``get_user(user_id)`` and ``authenticate(**credentials)``.
|
||||
An authentication backend is a class that implements two required methods:
|
||||
``get_user(user_id)`` and ``authenticate(**credentials)``, as well as a set of
|
||||
optional permission related :ref:`authorization methods <authorization_methods>`.
|
||||
|
||||
The ``get_user`` method takes a ``user_id`` -- which could be a username,
|
||||
database ID or whatever -- and returns a ``User`` object.
|
||||
@@ -1861,6 +1851,8 @@ object the first time a user authenticates::
|
||||
except User.DoesNotExist:
|
||||
return None
|
||||
|
||||
.. _authorization_methods:
|
||||
|
||||
Handling authorization in custom backends
|
||||
-----------------------------------------
|
||||
|
||||
@@ -1891,13 +1883,16 @@ fairly simply::
|
||||
return False
|
||||
|
||||
This gives full permissions to the user granted access in the above example.
|
||||
Notice that the backend auth functions all take the user object as an argument,
|
||||
and they also accept the same arguments given to the associated
|
||||
:class:`django.contrib.auth.models.User` functions.
|
||||
Notice that in addition to the same arguments given to the associated
|
||||
:class:`django.contrib.auth.models.User` functions, the backend auth functions
|
||||
all take the user object, which may be an anonymous user, as an argument.
|
||||
|
||||
A full authorization implementation can be found in
|
||||
`django/contrib/auth/backends.py`_, which is the default backend and queries
|
||||
the ``auth_permission`` table most of the time.
|
||||
A full authorization implementation can be found in the ``ModelBackend`` class
|
||||
in `django/contrib/auth/backends.py`_, which is the default backend and queries
|
||||
the ``auth_permission`` table most of the time. If you wish to provide
|
||||
custom behavior for only part of the backend API, you can take advantage of
|
||||
Python inheritence and subclass ``ModelBackend`` instead of implementing the
|
||||
complete API in a custom backend.
|
||||
|
||||
.. _django/contrib/auth/backends.py: https://github.com/django/django/blob/master/django/contrib/auth/backends.py
|
||||
|
||||
@@ -1906,8 +1901,6 @@ the ``auth_permission`` table most of the time.
|
||||
Authorization for anonymous users
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionchanged:: 1.2
|
||||
|
||||
An anonymous user is one that is not authenticated i.e. they have provided no
|
||||
valid authentication details. However, that does not necessarily mean they are
|
||||
not authorized to do anything. At the most basic level, most Web sites
|
||||
@@ -1915,25 +1908,27 @@ authorize anonymous users to browse most of the site, and many allow anonymous
|
||||
posting of comments etc.
|
||||
|
||||
Django's permission framework does not have a place to store permissions for
|
||||
anonymous users. However, it has a foundation that allows custom authentication
|
||||
backends to specify authorization for anonymous users. This is especially useful
|
||||
for the authors of re-usable apps, who can delegate all questions of authorization
|
||||
to the auth backend, rather than needing settings, for example, to control
|
||||
anonymous access.
|
||||
anonymous users. However, the user object passed to an authentication backend
|
||||
may be an :class:`django.contrib.auth.models.AnonymousUser` object, allowing
|
||||
the backend to specify custom authorization behavior for anonymous users. This
|
||||
is especially useful for the authors of re-usable apps, who can delegate all
|
||||
questions of authorization to the auth backend, rather than needing settings,
|
||||
for example, to control anonymous access.
|
||||
|
||||
.. _inactive_auth:
|
||||
|
||||
Authorization for inactive users
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionadded:: 1.3
|
||||
.. versionchanged:: 1.3
|
||||
|
||||
An inactive user is a one that is authenticated but has its attribute
|
||||
``is_active`` set to ``False``. However this does not mean they are not
|
||||
authorized to do anything. For example they are allowed to activate their
|
||||
account.
|
||||
|
||||
The support for anonymous users in the permission system allows for
|
||||
anonymous users to have permissions to do something while inactive
|
||||
The support for anonymous users in the permission system allows for a scenario
|
||||
where anonymous users have permissions to do something while inactive
|
||||
authenticated users do not.
|
||||
|
||||
Do not forget to test for the ``is_active`` attribute of the user in your own
|
||||
@@ -1941,9 +1936,11 @@ backend permission methods.
|
||||
|
||||
|
||||
Handling object permissions
|
||||
---------------------------
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Django's permission framework has a foundation for object permissions, though
|
||||
there is no implementation for it in the core. That means that checking for
|
||||
object permissions will always return ``False`` or an empty list (depending on
|
||||
the check performed).
|
||||
the check performed). An authentication backend will receive the keyword
|
||||
parameters ``obj`` and ``user_obj`` for each object related authorization
|
||||
method and can return the object level permission as appropriate.
|
||||
|
||||
@@ -83,12 +83,6 @@ two most common are `python-memcached`_ and `pylibmc`_.
|
||||
.. _`python-memcached`: ftp://ftp.tummy.com/pub/python-memcached/
|
||||
.. _`pylibmc`: http://sendapatch.se/projects/pylibmc/
|
||||
|
||||
.. versionchanged:: 1.2
|
||||
In Django 1.0 and 1.1, you could also use ``cmemcache`` as a binding.
|
||||
However, support for this library was deprecated in 1.2 due to
|
||||
a lack of maintenance on the ``cmemcache`` library itself. Support for
|
||||
``cmemcache`` will be removed completely in Django 1.4.
|
||||
|
||||
.. versionchanged:: 1.3
|
||||
Support for ``pylibmc`` was added.
|
||||
|
||||
@@ -493,8 +487,6 @@ more on these decorators.
|
||||
|
||||
.. _i18n-cache-key:
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
If :setting:`USE_I18N` is set to ``True`` then the generated cache key will
|
||||
include the name of the active :term:`language<language code>` -- see also
|
||||
:ref:`how-django-discovers-language-preference`). This allows you to easily
|
||||
@@ -603,7 +595,8 @@ the ``cache`` template tag. To give your template access to this tag, put
|
||||
|
||||
The ``{% cache %}`` template tag caches the contents of the block for a given
|
||||
amount of time. It takes at least two arguments: the cache timeout, in seconds,
|
||||
and the name to give the cache fragment. For example:
|
||||
and the name to give the cache fragment. The name will be taken as is, do not
|
||||
use a variable. For example:
|
||||
|
||||
.. code-block:: html+django
|
||||
|
||||
@@ -737,8 +730,6 @@ actually exist in the cache (and haven't expired)::
|
||||
>>> cache.get_many(['a', 'b', 'c'])
|
||||
{'a': 1, 'b': 2, 'c': 3}
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
To set multiple values more efficiently, use ``set_many()`` to pass a dictionary
|
||||
of key-value pairs::
|
||||
|
||||
@@ -753,15 +744,11 @@ clearing the cache for a particular object::
|
||||
|
||||
>>> cache.delete('a')
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
If you want to clear a bunch of keys at once, ``delete_many()`` can take a list
|
||||
of keys to be cleared::
|
||||
|
||||
>>> cache.delete_many(['a', 'b', 'c'])
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
Finally, if you want to delete all the keys in the cache, use
|
||||
``cache.clear()``. Be careful with this; ``clear()`` will remove *everything*
|
||||
from the cache, not just the keys set by your application. ::
|
||||
@@ -877,7 +864,7 @@ key version to provide a final cache key. By default, the three parts
|
||||
are joined using colons to produce a final string::
|
||||
|
||||
def make_key(key, key_prefix, version):
|
||||
return ':'.join([key_prefix, str(version), smart_str(key)])
|
||||
return ':'.join([key_prefix, str(version), smart_bytes(key)])
|
||||
|
||||
If you want to combine the parts in different ways, or apply other
|
||||
processing to the final key (e.g., taking a hash digest of the key
|
||||
|
||||
@@ -1,624 +0,0 @@
|
||||
=========================
|
||||
Class-based generic views
|
||||
=========================
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
.. note::
|
||||
Prior to Django 1.3, generic views were implemented as functions. The
|
||||
function-based implementation has been removed in favor of the
|
||||
class-based approach described here.
|
||||
|
||||
Writing Web applications can be monotonous, because we repeat certain patterns
|
||||
again and again. Django tries to take away some of that monotony at the model
|
||||
and template layers, but Web developers also experience this boredom at the view
|
||||
level.
|
||||
|
||||
Django's *generic views* were developed to ease that pain. They take certain
|
||||
common idioms and patterns found in view development and abstract them so that
|
||||
you can quickly write common views of data without having to write too much
|
||||
code.
|
||||
|
||||
We can recognize certain common tasks, like displaying a list of objects, and
|
||||
write code that displays a list of *any* object. Then the model in question can
|
||||
be passed as an extra argument to the URLconf.
|
||||
|
||||
Django ships with generic views to do the following:
|
||||
|
||||
* Perform common "simple" tasks: redirect to a different page and
|
||||
render a given template.
|
||||
|
||||
* Display list and detail pages for a single object. If we were creating an
|
||||
application to manage conferences then a ``TalkListView`` and a
|
||||
``RegisteredUserListView`` would be examples of list views. A single
|
||||
talk page is an example of what we call a "detail" view.
|
||||
|
||||
* Present date-based objects in year/month/day archive pages,
|
||||
associated detail, and "latest" pages.
|
||||
|
||||
* Allow users to create, update, and delete objects -- with or
|
||||
without authorization.
|
||||
|
||||
Taken together, these views provide easy interfaces to perform the most common
|
||||
tasks developers encounter.
|
||||
|
||||
|
||||
Simple usage
|
||||
============
|
||||
|
||||
Class-based generic views (and any class-based views that inherit from
|
||||
the base classes Django provides) can be configured in two
|
||||
ways: subclassing, or passing in arguments directly in the URLconf.
|
||||
|
||||
When you subclass a class-based view, you can override attributes
|
||||
(such as the ``template_name``) or methods (such as ``get_context_data``)
|
||||
in your subclass to provide new values or methods. Consider, for example,
|
||||
a view that just displays one template, ``about.html``. Django has a
|
||||
generic view to do this - :class:`~django.views.generic.base.TemplateView` -
|
||||
so we can just subclass it, and override the template name::
|
||||
|
||||
# some_app/views.py
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
class AboutView(TemplateView):
|
||||
template_name = "about.html"
|
||||
|
||||
Then, we just need to add this new view into our URLconf. As the class-based
|
||||
views themselves are classes, we point the URL to the ``as_view`` class method
|
||||
instead, which is the entry point for class-based views::
|
||||
|
||||
# urls.py
|
||||
from django.conf.urls import patterns, url, include
|
||||
from some_app.views import AboutView
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^about/', AboutView.as_view()),
|
||||
)
|
||||
|
||||
Alternatively, if you're only changing a few simple attributes on a
|
||||
class-based view, you can simply pass the new attributes into the ``as_view``
|
||||
method call itself::
|
||||
|
||||
from django.conf.urls import patterns, url, include
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^about/', TemplateView.as_view(template_name="about.html")),
|
||||
)
|
||||
|
||||
A similar overriding pattern can be used for the ``url`` attribute on
|
||||
:class:`~django.views.generic.base.RedirectView`, another simple
|
||||
generic view.
|
||||
|
||||
|
||||
Generic views of objects
|
||||
========================
|
||||
|
||||
:class:`~django.views.generic.base.TemplateView` certainly is useful,
|
||||
but Django's generic views really shine when it comes to presenting
|
||||
views of your database content. Because it's such a common task,
|
||||
Django comes with a handful of built-in generic views that make
|
||||
generating list and detail views of objects incredibly easy.
|
||||
|
||||
Let's take a look at one of these generic views: the "object list" view. We'll
|
||||
be using these models::
|
||||
|
||||
# models.py
|
||||
from django.db import models
|
||||
|
||||
class Publisher(models.Model):
|
||||
name = models.CharField(max_length=30)
|
||||
address = models.CharField(max_length=50)
|
||||
city = models.CharField(max_length=60)
|
||||
state_province = models.CharField(max_length=30)
|
||||
country = models.CharField(max_length=50)
|
||||
website = models.URLField()
|
||||
|
||||
class Meta:
|
||||
ordering = ["-name"]
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
class Book(models.Model):
|
||||
title = models.CharField(max_length=100)
|
||||
authors = models.ManyToManyField('Author')
|
||||
publisher = models.ForeignKey(Publisher)
|
||||
publication_date = models.DateField()
|
||||
|
||||
To build a list page of all publishers, we'd use a URLconf along these lines::
|
||||
|
||||
from django.conf.urls import patterns, url, include
|
||||
from django.views.generic import ListView
|
||||
from books.models import Publisher
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^publishers/$', ListView.as_view(
|
||||
model=Publisher,
|
||||
)),
|
||||
)
|
||||
|
||||
That's all the Python code we need to write. We still need to write a template,
|
||||
however. We could explicitly tell the view which template to use
|
||||
by including a ``template_name`` key in the arguments to as_view, but in
|
||||
the absence of an explicit template Django will infer one from the object's
|
||||
name. In this case, the inferred template will be
|
||||
``"books/publisher_list.html"`` -- the "books" part comes from the name of the
|
||||
app that defines the model, while the "publisher" bit is just the lowercased
|
||||
version of the model's name.
|
||||
|
||||
.. note::
|
||||
Thus, when (for example) the :class:`django.template.loaders.app_directories.Loader`
|
||||
template loader is enabled in :setting:`TEMPLATE_LOADERS`, the template
|
||||
location would be::
|
||||
|
||||
/path/to/project/books/templates/books/publisher_list.html
|
||||
|
||||
.. highlightlang:: html+django
|
||||
|
||||
This template will be rendered against a context containing a variable called
|
||||
``object_list`` that contains all the publisher objects. A very simple template
|
||||
might look like the following::
|
||||
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Publishers</h2>
|
||||
<ul>
|
||||
{% for publisher in object_list %}
|
||||
<li>{{ publisher.name }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
||||
That's really all there is to it. All the cool features of generic views come
|
||||
from changing the "info" dictionary passed to the generic view. The
|
||||
:doc:`generic views reference</ref/class-based-views>` documents all the generic
|
||||
views and their options in detail; the rest of this document will consider
|
||||
some of the common ways you might customize and extend generic views.
|
||||
|
||||
|
||||
Extending generic views
|
||||
=======================
|
||||
|
||||
.. highlightlang:: python
|
||||
|
||||
There's no question that using generic views can speed up development
|
||||
substantially. In most projects, however, there comes a moment when the
|
||||
generic views no longer suffice. Indeed, the most common question asked by new
|
||||
Django developers is how to make generic views handle a wider array of
|
||||
situations.
|
||||
|
||||
This is one of the reasons generic views were redesigned for the 1.3 release -
|
||||
previously, they were just view functions with a bewildering array of options;
|
||||
now, rather than passing in a large amount of configuration in the URLconf,
|
||||
the recommended way to extend generic views is to subclass them, and override
|
||||
their attributes or methods.
|
||||
|
||||
|
||||
Making "friendly" template contexts
|
||||
-----------------------------------
|
||||
|
||||
You might have noticed that our sample publisher list template stores
|
||||
all the publishers in a variable named ``object_list``. While this
|
||||
works just fine, it isn't all that "friendly" to template authors:
|
||||
they have to "just know" that they're dealing with publishers here.
|
||||
|
||||
Well, if you're dealing with a model object, this is already done for
|
||||
you. When you are dealing with an object or queryset, Django is able
|
||||
to populate the context using the verbose name (or the plural verbose
|
||||
name, in the case of a list of objects) of the object being displayed.
|
||||
This is provided in addition to the default ``object_list`` entry, but
|
||||
contains exactly the same data.
|
||||
|
||||
If the verbose name (or plural verbose name) still isn't a good match,
|
||||
you can manually set the name of the context variable. The
|
||||
``context_object_name`` attribute on a generic view specifies the
|
||||
context variable to use. In this example, we'll override it in the
|
||||
URLconf, since it's a simple change:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^publishers/$', ListView.as_view(
|
||||
model=Publisher,
|
||||
**context_object_name="publisher_list",**
|
||||
)),
|
||||
)
|
||||
|
||||
Providing a useful ``context_object_name`` is always a good idea. Your
|
||||
coworkers who design templates will thank you.
|
||||
|
||||
|
||||
Adding extra context
|
||||
--------------------
|
||||
|
||||
Often you simply need to present some extra information beyond that
|
||||
provided by the generic view. For example, think of showing a list of
|
||||
all the books on each publisher detail page. The
|
||||
:class:`~django.views.generic.detail.DetailView` generic view provides
|
||||
the publisher to the context, but it seems there's no way to get
|
||||
additional information in that template.
|
||||
|
||||
However, there is; you can subclass
|
||||
:class:`~django.views.generic.detail.DetailView` and provide your own
|
||||
implementation of the ``get_context_data`` method. The default
|
||||
implementation of this that comes with
|
||||
:class:`~django.views.generic.detail.DetailView` simply adds in the
|
||||
object being displayed to the template, but you can override it to show
|
||||
more::
|
||||
|
||||
from django.views.generic import DetailView
|
||||
from books.models import Publisher, Book
|
||||
|
||||
class PublisherDetailView(DetailView):
|
||||
|
||||
context_object_name = "publisher"
|
||||
model = Publisher
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
# Call the base implementation first to get a context
|
||||
context = super(PublisherDetailView, self).get_context_data(**kwargs)
|
||||
# Add in a QuerySet of all the books
|
||||
context['book_list'] = Book.objects.all()
|
||||
return context
|
||||
|
||||
.. note::
|
||||
|
||||
Generally, get_context_data will merge the context data of all parent classes
|
||||
with those of the current class. To preserve this behavior in your own classes
|
||||
where you want to alter the context, you should be sure to call
|
||||
get_context_data on the super class. When no two classes try to define the same
|
||||
key, this will give the expected results. However if any class attempts to
|
||||
override a key after parent classes have set it (after the call to super), any
|
||||
children of that class will also need to explictly set it after super if they
|
||||
want to be sure to override all parents.
|
||||
|
||||
Viewing subsets of objects
|
||||
--------------------------
|
||||
|
||||
Now let's take a closer look at the ``model`` argument we've been
|
||||
using all along. The ``model`` argument, which specifies the database
|
||||
model that the view will operate upon, is available on all the
|
||||
generic views that operate on a single object or a collection of
|
||||
objects. However, the ``model`` argument is not the only way to
|
||||
specify the objects that the view will operate upon -- you can also
|
||||
specify the list of objects using the ``queryset`` argument::
|
||||
|
||||
from django.views.generic import DetailView
|
||||
from books.models import Publisher, Book
|
||||
|
||||
class PublisherDetailView(DetailView):
|
||||
|
||||
context_object_name = "publisher"
|
||||
queryset = Publisher.objects.all()
|
||||
|
||||
Specifying ``model = Publisher`` is really just shorthand for saying
|
||||
``queryset = Publisher.objects.all()``. However, by using ``queryset``
|
||||
to define a filtered list of objects you can be more specific about the
|
||||
objects that will be visible in the view (see :doc:`/topics/db/queries`
|
||||
for more information about :class:`QuerySet` objects, and see the
|
||||
:doc:`class-based views reference </ref/class-based-views>` for the complete
|
||||
details).
|
||||
|
||||
To pick a simple example, we might want to order a list of books by
|
||||
publication date, with the most recent first::
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^publishers/$', ListView.as_view(
|
||||
queryset=Publisher.objects.all(),
|
||||
context_object_name="publisher_list",
|
||||
)),
|
||||
(r'^books/$', ListView.as_view(
|
||||
queryset=Book.objects.order_by("-publication_date"),
|
||||
context_object_name="book_list",
|
||||
)),
|
||||
)
|
||||
|
||||
|
||||
That's a pretty simple example, but it illustrates the idea nicely. Of course,
|
||||
you'll usually want to do more than just reorder objects. If you want to
|
||||
present a list of books by a particular publisher, you can use the same
|
||||
technique (here, illustrated using subclassing rather than by passing arguments
|
||||
in the URLconf)::
|
||||
|
||||
from django.views.generic import ListView
|
||||
from books.models import Book
|
||||
|
||||
class AcmeBookListView(ListView):
|
||||
|
||||
context_object_name = "book_list"
|
||||
queryset = Book.objects.filter(publisher__name="Acme Publishing")
|
||||
template_name = "books/acme_list.html"
|
||||
|
||||
Notice that along with a filtered ``queryset``, we're also using a custom
|
||||
template name. If we didn't, the generic view would use the same template as the
|
||||
"vanilla" object list, which might not be what we want.
|
||||
|
||||
Also notice that this isn't a very elegant way of doing publisher-specific
|
||||
books. If we want to add another publisher page, we'd need another handful of
|
||||
lines in the URLconf, and more than a few publishers would get unreasonable.
|
||||
We'll deal with this problem in the next section.
|
||||
|
||||
.. note::
|
||||
|
||||
If you get a 404 when requesting ``/books/acme/``, check to ensure you
|
||||
actually have a Publisher with the name 'ACME Publishing'. Generic
|
||||
views have an ``allow_empty`` parameter for this case. See the
|
||||
:doc:`class-based-views reference</ref/class-based-views>` for more details.
|
||||
|
||||
|
||||
Dynamic filtering
|
||||
-----------------
|
||||
|
||||
Another common need is to filter down the objects given in a list page by some
|
||||
key in the URL. Earlier we hard-coded the publisher's name in the URLconf, but
|
||||
what if we wanted to write a view that displayed all the books by some arbitrary
|
||||
publisher?
|
||||
|
||||
Handily, the ``ListView`` has a
|
||||
:meth:`~django.views.generic.detail.ListView.get_queryset` method we can
|
||||
override. Previously, it has just been returning the value of the ``queryset``
|
||||
attribute, but now we can add more logic.
|
||||
|
||||
The key part to making this work is that when class-based views are called,
|
||||
various useful things are stored on ``self``; as well as the request
|
||||
(``self.request``) this includes the positional (``self.args``) and name-based
|
||||
(``self.kwargs``) arguments captured according to the URLconf.
|
||||
|
||||
Here, we have a URLconf with a single captured group::
|
||||
|
||||
from books.views import PublisherBookListView
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^books/(\w+)/$', PublisherBookListView.as_view()),
|
||||
)
|
||||
|
||||
Next, we'll write the ``PublisherBookListView`` view itself::
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.views.generic import ListView
|
||||
from books.models import Book, Publisher
|
||||
|
||||
class PublisherBookListView(ListView):
|
||||
|
||||
context_object_name = "book_list"
|
||||
template_name = "books/books_by_publisher.html"
|
||||
|
||||
def get_queryset(self):
|
||||
publisher = get_object_or_404(Publisher, name__iexact=self.args[0])
|
||||
return Book.objects.filter(publisher=publisher)
|
||||
|
||||
As you can see, it's quite easy to add more logic to the queryset selection;
|
||||
if we wanted, we could use ``self.request.user`` to filter using the current
|
||||
user, or other more complex logic.
|
||||
|
||||
We can also add the publisher into the context at the same time, so we can
|
||||
use it in the template::
|
||||
|
||||
class PublisherBookListView(ListView):
|
||||
|
||||
context_object_name = "book_list"
|
||||
template_name = "books/books_by_publisher.html"
|
||||
|
||||
def get_queryset(self):
|
||||
self.publisher = get_object_or_404(Publisher, name__iexact=self.args[0])
|
||||
return Book.objects.filter(publisher=self.publisher)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
# Call the base implementation first to get a context
|
||||
context = super(PublisherBookListView, self).get_context_data(**kwargs)
|
||||
# Add in the publisher
|
||||
context['publisher'] = self.publisher
|
||||
return context
|
||||
|
||||
Performing extra work
|
||||
---------------------
|
||||
|
||||
The last common pattern we'll look at involves doing some extra work before
|
||||
or after calling the generic view.
|
||||
|
||||
Imagine we had a ``last_accessed`` field on our ``Author`` object that we were
|
||||
using to keep track of the last time anybody looked at that author::
|
||||
|
||||
# models.py
|
||||
|
||||
class Author(models.Model):
|
||||
salutation = models.CharField(max_length=10)
|
||||
first_name = models.CharField(max_length=30)
|
||||
last_name = models.CharField(max_length=40)
|
||||
email = models.EmailField()
|
||||
headshot = models.ImageField(upload_to='/tmp')
|
||||
last_accessed = models.DateTimeField()
|
||||
|
||||
The generic ``DetailView`` class, of course, wouldn't know anything about this
|
||||
field, but once again we could easily write a custom view to keep that field
|
||||
updated.
|
||||
|
||||
First, we'd need to add an author detail bit in the URLconf to point to a
|
||||
custom view:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
from books.views import AuthorDetailView
|
||||
|
||||
urlpatterns = patterns('',
|
||||
#...
|
||||
**(r'^authors/(?P<pk>\\d+)/$', AuthorDetailView.as_view()),**
|
||||
)
|
||||
|
||||
Then we'd write our new view -- ``get_object`` is the method that retrieves the
|
||||
object -- so we simply override it and wrap the call::
|
||||
|
||||
import datetime
|
||||
from books.models import Author
|
||||
from django.views.generic import DetailView
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
class AuthorDetailView(DetailView):
|
||||
|
||||
queryset = Author.objects.all()
|
||||
|
||||
def get_object(self):
|
||||
# Call the superclass
|
||||
object = super(AuthorDetailView, self).get_object()
|
||||
# Record the last accessed date
|
||||
object.last_accessed = datetime.datetime.now()
|
||||
object.save()
|
||||
# Return the object
|
||||
return object
|
||||
|
||||
.. note::
|
||||
|
||||
This code won't actually work unless you create a
|
||||
``books/author_detail.html`` template.
|
||||
|
||||
.. note::
|
||||
|
||||
The URLconf here uses the named group ``pk`` - this name is the default
|
||||
name that ``DetailView`` uses to find the value of the primary key used to
|
||||
filter the queryset.
|
||||
|
||||
If you want to change it, you'll need to do your own ``get()`` call
|
||||
on ``self.queryset`` using the new named parameter from ``self.kwargs``.
|
||||
|
||||
More than just HTML
|
||||
-------------------
|
||||
|
||||
So far, we've been focusing on rendering templates to generate
|
||||
responses. However, that's not all generic views can do.
|
||||
|
||||
Each generic view is composed out of a series of mixins, and each
|
||||
mixin contributes a little piece of the entire view. Some of these
|
||||
mixins -- such as
|
||||
:class:`~django.views.generic.base.TemplateResponseMixin` -- are
|
||||
specifically designed for rendering content to an HTML response using a
|
||||
template. However, you can write your own mixins that perform
|
||||
different rendering behavior.
|
||||
|
||||
For example, a simple JSON mixin might look something like this::
|
||||
|
||||
import json
|
||||
from django import http
|
||||
|
||||
class JSONResponseMixin(object):
|
||||
def render_to_response(self, context):
|
||||
"Returns a JSON response containing 'context' as payload"
|
||||
return self.get_json_response(self.convert_context_to_json(context))
|
||||
|
||||
def get_json_response(self, content, **httpresponse_kwargs):
|
||||
"Construct an `HttpResponse` object."
|
||||
return http.HttpResponse(content,
|
||||
content_type='application/json',
|
||||
**httpresponse_kwargs)
|
||||
|
||||
def convert_context_to_json(self, context):
|
||||
"Convert the context dictionary into a JSON object"
|
||||
# Note: This is *EXTREMELY* naive; in reality, you'll need
|
||||
# to do much more complex handling to ensure that arbitrary
|
||||
# objects -- such as Django model instances or querysets
|
||||
# -- can be serialized as JSON.
|
||||
return json.dumps(context)
|
||||
|
||||
Then, you could build a JSON-returning
|
||||
:class:`~django.views.generic.detail.DetailView` by mixing your
|
||||
:class:`JSONResponseMixin` with the
|
||||
:class:`~django.views.generic.detail.BaseDetailView` -- (the
|
||||
:class:`~django.views.generic.detail.DetailView` before template
|
||||
rendering behavior has been mixed in)::
|
||||
|
||||
class JSONDetailView(JSONResponseMixin, BaseDetailView):
|
||||
pass
|
||||
|
||||
This view can then be deployed in the same way as any other
|
||||
:class:`~django.views.generic.detail.DetailView`, with exactly the
|
||||
same behavior -- except for the format of the response.
|
||||
|
||||
If you want to be really adventurous, you could even mix a
|
||||
:class:`~django.views.generic.detail.DetailView` subclass that is able
|
||||
to return *both* HTML and JSON content, depending on some property of
|
||||
the HTTP request, such as a query argument or a HTTP header. Just mix
|
||||
in both the :class:`JSONResponseMixin` and a
|
||||
:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`,
|
||||
and override the implementation of :func:`render_to_response()` to defer
|
||||
to the appropriate subclass depending on the type of response that the user
|
||||
requested::
|
||||
|
||||
class HybridDetailView(JSONResponseMixin, SingleObjectTemplateResponseMixin, BaseDetailView):
|
||||
def render_to_response(self, context):
|
||||
# Look for a 'format=json' GET argument
|
||||
if self.request.GET.get('format','html') == 'json':
|
||||
return JSONResponseMixin.render_to_response(self, context)
|
||||
else:
|
||||
return SingleObjectTemplateResponseMixin.render_to_response(self, context)
|
||||
|
||||
Because of the way that Python resolves method overloading, the local
|
||||
``render_to_response()`` implementation will override the versions provided by
|
||||
:class:`JSONResponseMixin` and
|
||||
:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`.
|
||||
|
||||
Decorating class-based views
|
||||
============================
|
||||
|
||||
.. highlightlang:: python
|
||||
|
||||
The extension of class-based views isn't limited to using mixins. You
|
||||
can use also use decorators.
|
||||
|
||||
Decorating in URLconf
|
||||
---------------------
|
||||
|
||||
The simplest way of decorating class-based views is to decorate the
|
||||
result of the :meth:`~django.views.generic.base.View.as_view` method.
|
||||
The easiest place to do this is in the URLconf where you deploy your
|
||||
view::
|
||||
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
from .views import VoteView
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^about/', login_required(TemplateView.as_view(template_name="secret.html"))),
|
||||
(r'^vote/', permission_required('polls.can_vote')(VoteView.as_view())),
|
||||
)
|
||||
|
||||
This approach applies the decorator on a per-instance basis. If you
|
||||
want every instance of a view to be decorated, you need to take a
|
||||
different approach.
|
||||
|
||||
.. _decorating-class-based-views:
|
||||
|
||||
Decorating the class
|
||||
--------------------
|
||||
|
||||
To decorate every instance of a class-based view, you need to decorate
|
||||
the class definition itself. To do this you apply the decorator to the
|
||||
:meth:`~django.views.generic.base.View.dispatch` method of the class.
|
||||
|
||||
A method on a class isn't quite the same as a standalone function, so
|
||||
you can't just apply a function decorator to the method -- you need to
|
||||
transform it into a method decorator first. The ``method_decorator``
|
||||
decorator transforms a function decorator into a method decorator so
|
||||
that it can be used on an instance method. For example::
|
||||
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
class ProtectedView(TemplateView):
|
||||
template_name = 'secret.html'
|
||||
|
||||
@method_decorator(login_required)
|
||||
def dispatch(self, *args, **kwargs):
|
||||
return super(ProtectedView, self).dispatch(*args, **kwargs)
|
||||
|
||||
In this example, every instance of ``ProtectedView`` will have
|
||||
login protection.
|
||||
|
||||
.. note::
|
||||
|
||||
``method_decorator`` passes ``*args`` and ``**kwargs``
|
||||
as parameters to the decorated method on the class. If your method
|
||||
does not accept a compatible set of parameters it will raise a
|
||||
``TypeError`` exception.
|
||||
432
docs/topics/class-based-views/generic-display.txt
Normal file
432
docs/topics/class-based-views/generic-display.txt
Normal file
@@ -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(PublisherDetail, 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 AcmeBookList(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(PublisherBookList, self).get_context_data(**kwargs)
|
||||
# Add in the publisher
|
||||
context['publisher'] = self.publisher
|
||||
return context
|
||||
|
||||
.. _generic-views-extra-work:
|
||||
|
||||
Performing extra work
|
||||
---------------------
|
||||
|
||||
The last common pattern we'll look at involves doing some extra work before
|
||||
or after calling the generic view.
|
||||
|
||||
Imagine we had a ``last_accessed`` field on our ``Author`` object that we were
|
||||
using to keep track of the last time anybody looked at that author::
|
||||
|
||||
# models.py
|
||||
|
||||
class Author(models.Model):
|
||||
salutation = models.CharField(max_length=10)
|
||||
name = models.CharField(max_length=200)
|
||||
email = models.EmailField()
|
||||
headshot = models.ImageField(upload_to='/tmp')
|
||||
last_accessed = models.DateTimeField()
|
||||
|
||||
The generic ``DetailView`` class, of course, wouldn't know anything about this
|
||||
field, but once again we could easily write a custom view to keep that field
|
||||
updated.
|
||||
|
||||
First, we'd need to add an author detail bit in the URLconf to point to a
|
||||
custom view::
|
||||
|
||||
from books.views import AuthorDetailView
|
||||
|
||||
urlpatterns = patterns('',
|
||||
#...
|
||||
url(r'^authors/(?P<pk>\d+)/$', AuthorDetailView.as_view(), name='author-detail'),
|
||||
)
|
||||
|
||||
Then we'd write our new view -- ``get_object`` is the method that retrieves the
|
||||
object -- so we simply override it and wrap the call::
|
||||
|
||||
from django.views.generic import DetailView
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.utils import timezone
|
||||
from books.models import Author
|
||||
|
||||
class AuthorDetailView(DetailView):
|
||||
|
||||
queryset = Author.objects.all()
|
||||
|
||||
def get_object(self):
|
||||
# Call the superclass
|
||||
object = super(AuthorDetailView, self).get_object()
|
||||
# Record the last accessed date
|
||||
object.last_accessed = timezone.now()
|
||||
object.save()
|
||||
# Return the object
|
||||
return object
|
||||
|
||||
.. note::
|
||||
|
||||
The URLconf here uses the named group ``pk`` - this name is the default
|
||||
name that ``DetailView`` uses to find the value of the primary key used to
|
||||
filter the queryset.
|
||||
|
||||
If you want to call the group something else, you can set ``pk_url_kwarg``
|
||||
on the view. More details can be found in the reference for
|
||||
:class:`~django.views.generic.detail.DetailView`
|
||||
205
docs/topics/class-based-views/generic-editing.txt
Normal file
205
docs/topics/class-based-views/generic-editing.txt
Normal file
@@ -0,0 +1,205 @@
|
||||
Form handling with class-based views
|
||||
====================================
|
||||
|
||||
Form processing generally has 3 paths:
|
||||
|
||||
* Initial GET (blank or prepopulated form)
|
||||
* POST with invalid data (typically redisplay form with errors)
|
||||
* POST with valid data (process the data and typically redirect)
|
||||
|
||||
Implementing this yourself often results in a lot of repeated
|
||||
boilerplate code (see :ref:`Using a form in a
|
||||
view<using-a-form-in-a-view>`). To help avoid this, Django provides a
|
||||
collection of generic class-based views for form processing.
|
||||
|
||||
Basic Forms
|
||||
-----------
|
||||
|
||||
Given a simple contact form::
|
||||
|
||||
# forms.py
|
||||
from django import forms
|
||||
|
||||
class ContactForm(forms.Form):
|
||||
name = forms.CharField()
|
||||
message = forms.CharField(widget=forms.Textarea)
|
||||
|
||||
def send_email(self):
|
||||
# send email using the self.cleaned_data dictionary
|
||||
pass
|
||||
|
||||
The view can be constructed using a FormView::
|
||||
|
||||
# views.py
|
||||
from myapp.forms import ContactForm
|
||||
from django.views.generic.edit import FormView
|
||||
|
||||
class ContactView(FormView):
|
||||
template_name = 'contact.html'
|
||||
form_class = ContactForm
|
||||
success_url = '/thanks/'
|
||||
|
||||
def form_valid(self, form):
|
||||
# This method is called when valid form data has been POSTed.
|
||||
# It should return an HttpResponse.
|
||||
form.send_email()
|
||||
return super(ContactView, self).form_valid(form)
|
||||
|
||||
Notes:
|
||||
|
||||
* FormView inherits
|
||||
:class:`~django.views.generic.base.TemplateResponseMixin` so
|
||||
:attr:`~django.views.generic.base.TemplateResponseMixin.template_name`
|
||||
can be used here
|
||||
* The default implementation for
|
||||
:meth:`~django.views.generic.edit.FormView.form_valid` simply
|
||||
redirects to the :attr:`success_url`
|
||||
|
||||
Model Forms
|
||||
-----------
|
||||
|
||||
Generic views really shine when working with models. These generic
|
||||
views will automatically create a :class:`ModelForm`, so long as they
|
||||
can work out which model class to use:
|
||||
|
||||
* If the :attr:`model` attribute is given, that model class will be used
|
||||
* If :meth:`get_object()` returns an object, the class of that object
|
||||
will be used
|
||||
* If a :attr:`queryset` is given, the model for that queryset will be used
|
||||
|
||||
Model form views provide a :meth:`form_valid()` implementation that
|
||||
saves the model automatically. You can override this if you have any
|
||||
special requirements; see below for examples.
|
||||
|
||||
You don't even need to provide a attr:`success_url` for
|
||||
:class:`~django.views.generic.edit.CreateView` or
|
||||
:class:`~django.views.generic.edit.UpdateView` - they will use
|
||||
:meth:`get_absolute_url()` on the model object if available.
|
||||
|
||||
If you want to use a custom :class:`ModelForm` (for instance to add
|
||||
extra validation) simply set
|
||||
:attr:`~django.views.generic.edit.FormMixin.form_class` on your view.
|
||||
|
||||
.. note::
|
||||
When specifying a custom form class, you must still specify the model,
|
||||
even though the :attr:`form_class` may be a :class:`ModelForm`.
|
||||
|
||||
First we need to add :meth:`get_absolute_url()` to our :class:`Author`
|
||||
class:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# models.py
|
||||
from django import models
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
class Author(models.Model):
|
||||
name = models.CharField(max_length=200)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('author-detail', kwargs={'pk': self.pk})
|
||||
|
||||
Then we can use :class:`CreateView` and friends to do the actual
|
||||
work. Notice how we're just configuring the generic class-based views
|
||||
here; we don't have to write any logic ourselves::
|
||||
|
||||
# views.py
|
||||
from django.views.generic.edit import CreateView, UpdateView, DeleteView
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from myapp.models import Author
|
||||
|
||||
class AuthorCreate(CreateView):
|
||||
model = Author
|
||||
|
||||
class AuthorUpdate(UpdateView):
|
||||
model = Author
|
||||
|
||||
class AuthorDelete(DeleteView):
|
||||
model = Author
|
||||
success_url = reverse_lazy('author-list')
|
||||
|
||||
.. note::
|
||||
We have to use :func:`~django.core.urlresolvers.reverse_lazy` here, not
|
||||
just ``reverse`` as the urls are not loaded when the file is imported.
|
||||
|
||||
Finally, we hook these new views into the URLconf::
|
||||
|
||||
# urls.py
|
||||
from django.conf.urls import patterns, url
|
||||
from myapp.views import AuthorCreate, AuthorUpdate, AuthorDelete
|
||||
|
||||
urlpatterns = patterns('',
|
||||
# ...
|
||||
url(r'author/add/$', AuthorCreate.as_view(), name='author_add'),
|
||||
url(r'author/(?P<pk>\d+)/$', AuthorUpdate.as_view(), name='author_update'),
|
||||
url(r'author/(?P<pk>\d+)/delete/$', AuthorDelete.as_view(), name='author_delete'),
|
||||
)
|
||||
|
||||
.. note::
|
||||
|
||||
These views inherit :class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`
|
||||
which uses :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_prefix`
|
||||
to construct the
|
||||
:attr:`~django.views.generic.base.TemplateResponseMixin.template_name`
|
||||
based on the model.
|
||||
|
||||
In this example:
|
||||
|
||||
* :class:`CreateView` and :class:`UpdateView` use ``myapp/author_form.html``
|
||||
* :class:`DeleteView` uses ``myapp/author_confirm_delete.html``
|
||||
|
||||
If you wish to have separate templates for :class:`CreateView` and
|
||||
:class:1UpdateView`, you can set either :attr:`template_name` or
|
||||
:attr:`template_name_suffix` on your view class.
|
||||
|
||||
Models and request.user
|
||||
-----------------------
|
||||
|
||||
To track the user that created an object using a :class:`CreateView`,
|
||||
you can use a custom :class:`ModelForm` to do this. First, add the
|
||||
foreign key relation to the model::
|
||||
|
||||
# models.py
|
||||
from django import models
|
||||
from django.contrib.auth import User
|
||||
|
||||
class Author(models.Model):
|
||||
name = models.CharField(max_length=200)
|
||||
created_by = models.ForeignKey(User)
|
||||
|
||||
# ...
|
||||
|
||||
Create a custom :class:`ModelForm` in order to exclude the
|
||||
``created_by`` field and prevent the user from editing it:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# forms.py
|
||||
from django import forms
|
||||
from myapp.models import Author
|
||||
|
||||
class AuthorForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Author
|
||||
exclude = ('created_by',)
|
||||
|
||||
In the view, use the custom :attr:`form_class` and override
|
||||
:meth:`form_valid()` to add the user::
|
||||
|
||||
# views.py
|
||||
from django.views.generic.edit import CreateView
|
||||
from myapp.models import Author
|
||||
from myapp.forms import AuthorForm
|
||||
|
||||
class AuthorCreate(CreateView):
|
||||
form_class = AuthorForm
|
||||
model = Author
|
||||
|
||||
def form_valid(self, form):
|
||||
form.instance.created_by = self.request.user
|
||||
return super(AuthorCreate, self).form_valid(form)
|
||||
|
||||
Note that you'll need to :ref:`decorate this
|
||||
view<decorating-class-based-views>` using
|
||||
:func:`~django.contrib.auth.decorators.login_required`, or
|
||||
alternatively handle unauthorised users in the :meth:`form_valid()`.
|
||||
233
docs/topics/class-based-views/index.txt
Normal file
233
docs/topics/class-based-views/index.txt
Normal file
@@ -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 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.http import HttpResponse
|
||||
|
||||
class JSONResponseMixin(object):
|
||||
"""
|
||||
A mixin that can be used to render a JSON response.
|
||||
"""
|
||||
response_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 TemplateView::
|
||||
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
class JSONView(JSONResponseMixin, TemplateView):
|
||||
pass
|
||||
|
||||
Equally we could use our mixin with one of the generic views. We can make our
|
||||
own version of :class:`~django.views.generic.detail.DetailView` by mixing
|
||||
:class:`JSONResponseMixin` with the
|
||||
:class:`~django.views.generic.detail.BaseDetailView` -- (the
|
||||
:class:`~django.views.generic.detail.DetailView` before template
|
||||
rendering behavior has been mixed in)::
|
||||
|
||||
class JSONDetailView(JSONResponseMixin, BaseDetailView):
|
||||
pass
|
||||
|
||||
This view can then be deployed in the same way as any other
|
||||
:class:`~django.views.generic.detail.DetailView`, with exactly the
|
||||
same behavior -- except for the format of the response.
|
||||
|
||||
If you want to be really adventurous, you could even mix a
|
||||
:class:`~django.views.generic.detail.DetailView` subclass that is able
|
||||
to return *both* HTML and JSON content, depending on some property of
|
||||
the HTTP request, such as a query argument or a HTTP header. Just mix
|
||||
in both the :class:`JSONResponseMixin` and a
|
||||
:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`,
|
||||
and override the implementation of :func:`render_to_response()` to defer
|
||||
to the appropriate subclass depending on the type of response that the user
|
||||
requested::
|
||||
|
||||
class HybridDetailView(JSONResponseMixin, SingleObjectTemplateResponseMixin, BaseDetailView):
|
||||
def render_to_response(self, context):
|
||||
# Look for a 'format=json' GET argument
|
||||
if self.request.GET.get('format','html') == 'json':
|
||||
return JSONResponseMixin.render_to_response(self, context)
|
||||
else:
|
||||
return SingleObjectTemplateResponseMixin.render_to_response(self, context)
|
||||
|
||||
Because of the way that Python resolves method overloading, the local
|
||||
``render_to_response()`` implementation will override the versions provided by
|
||||
:class:`JSONResponseMixin` and
|
||||
:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`.
|
||||
|
||||
For more information on how to use the built in generic views, consult the next
|
||||
topic on :doc:`generic class based views</topics/class-based-views/generic-display>`.
|
||||
|
||||
Decorating class-based views
|
||||
============================
|
||||
|
||||
.. highlightlang:: python
|
||||
|
||||
The extension of class-based views isn't limited to using mixins. You
|
||||
can use also use decorators.
|
||||
|
||||
Decorating in URLconf
|
||||
---------------------
|
||||
|
||||
The simplest way of decorating class-based views is to decorate the
|
||||
result of the :meth:`~django.views.generic.base.View.as_view` method.
|
||||
The easiest place to do this is in the URLconf where you deploy your
|
||||
view::
|
||||
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
from .views import VoteView
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^about/', login_required(TemplateView.as_view(template_name="secret.html"))),
|
||||
(r'^vote/', permission_required('polls.can_vote')(VoteView.as_view())),
|
||||
)
|
||||
|
||||
This approach applies the decorator on a per-instance basis. If you
|
||||
want every instance of a view to be decorated, you need to take a
|
||||
different approach.
|
||||
|
||||
.. _decorating-class-based-views:
|
||||
|
||||
Decorating the class
|
||||
--------------------
|
||||
|
||||
To decorate every instance of a class-based view, you need to decorate
|
||||
the class definition itself. To do this you apply the decorator to the
|
||||
:meth:`~django.views.generic.base.View.dispatch` method of the class.
|
||||
|
||||
A method on a class isn't quite the same as a standalone function, so
|
||||
you can't just apply a function decorator to the method -- you need to
|
||||
transform it into a method decorator first. The ``method_decorator``
|
||||
decorator transforms a function decorator into a method decorator so
|
||||
that it can be used on an instance method. For example::
|
||||
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
class ProtectedView(TemplateView):
|
||||
template_name = 'secret.html'
|
||||
|
||||
@method_decorator(login_required)
|
||||
def dispatch(self, *args, **kwargs):
|
||||
return super(ProtectedView, self).dispatch(*args, **kwargs)
|
||||
|
||||
In this example, every instance of ``ProtectedView`` will have
|
||||
login protection.
|
||||
|
||||
.. note::
|
||||
|
||||
``method_decorator`` passes ``*args`` and ``**kwargs``
|
||||
as parameters to the decorated method on the class. If your method
|
||||
does not accept a compatible set of parameters it will raise a
|
||||
``TypeError`` exception.
|
||||
605
docs/topics/class-based-views/mixins.txt
Normal file
605
docs/topics/class-based-views/mixins.txt
Normal file
@@ -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.
|
||||
@@ -156,11 +156,11 @@ ones:
|
||||
A choices list looks like this::
|
||||
|
||||
YEAR_IN_SCHOOL_CHOICES = (
|
||||
(u'FR', u'Freshman'),
|
||||
(u'SO', u'Sophomore'),
|
||||
(u'JR', u'Junior'),
|
||||
(u'SR', u'Senior'),
|
||||
(u'GR', u'Graduate'),
|
||||
('FR', 'Freshman'),
|
||||
('SO', 'Sophomore'),
|
||||
('JR', 'Junior'),
|
||||
('SR', 'Senior'),
|
||||
('GR', 'Graduate'),
|
||||
)
|
||||
|
||||
The first element in each tuple is the value that will be stored in the
|
||||
@@ -172,21 +172,22 @@ ones:
|
||||
from django.db import models
|
||||
|
||||
class Person(models.Model):
|
||||
GENDER_CHOICES = (
|
||||
(u'M', u'Male'),
|
||||
(u'F', u'Female'),
|
||||
SHIRT_SIZES = (
|
||||
('S', 'Small'),
|
||||
('M', 'Medium'),
|
||||
('L', 'Large'),
|
||||
)
|
||||
name = models.CharField(max_length=60)
|
||||
gender = models.CharField(max_length=2, choices=GENDER_CHOICES)
|
||||
shirt_size = models.CharField(max_length=2, choices=SHIRT_SIZES)
|
||||
|
||||
::
|
||||
|
||||
>>> p = Person(name="Fred Flintstone", gender="M")
|
||||
>>> p = Person(name="Fred Flintstone", shirt_size="L")
|
||||
>>> p.save()
|
||||
>>> p.gender
|
||||
u'M'
|
||||
>>> p.get_gender_display()
|
||||
u'Male'
|
||||
>>> p.shirt_size
|
||||
u'L'
|
||||
>>> p.get_shirt_size_display()
|
||||
u'Large'
|
||||
|
||||
:attr:`~Field.default`
|
||||
The default value for the field. This can be a value or a callable
|
||||
@@ -383,7 +384,8 @@ Extra fields on many-to-many relationships
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When you're only dealing with simple many-to-many relationships such as
|
||||
mixing and matching pizzas and toppings, a standard :class:`~django.db.models.ManyToManyField` is all you need. However, sometimes
|
||||
mixing and matching pizzas and toppings, a standard
|
||||
:class:`~django.db.models.ManyToManyField` is all you need. However, sometimes
|
||||
you may need to associate data with the relationship between two models.
|
||||
|
||||
For example, consider the case of an application tracking the musical groups
|
||||
@@ -880,8 +882,6 @@ field. This would normally cause a problem in abstract base classes, since the
|
||||
fields on this class are included into each of the child classes, with exactly
|
||||
the same values for the attributes (including :attr:`~django.db.models.ForeignKey.related_name`) each time.
|
||||
|
||||
.. versionchanged:: 1.2
|
||||
|
||||
To work around this problem, when you are using :attr:`~django.db.models.ForeignKey.related_name` in an
|
||||
abstract base class (only), part of the name should contain
|
||||
``'%(app_label)s'`` and ``'%(class)s'``.
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
Multiple databases
|
||||
==================
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
This topic guide describes Django's support for interacting with
|
||||
multiple databases. Most of the rest of Django's documentation assumes
|
||||
you are interacting with a single database. If you want to interact
|
||||
|
||||
@@ -230,7 +230,7 @@ It is optimal because:
|
||||
#. Use of :ttag:`with` means that we store ``user.emails.all`` in a variable
|
||||
for later use, allowing its cache to be re-used.
|
||||
|
||||
#. The line ``{% if emails %}`` causes ``QuerySet.__nonzero__()`` to be called,
|
||||
#. The line ``{% if emails %}`` causes ``QuerySet.__bool__()`` to be called,
|
||||
which causes the ``user.emails.all()`` query to be run on the database, and
|
||||
at the least the first line to be turned into an ORM object. If there aren't
|
||||
any results, it will return False, otherwise True.
|
||||
|
||||
@@ -18,8 +18,6 @@ __ `executing custom SQL directly`_
|
||||
Performing raw queries
|
||||
======================
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
The ``raw()`` manager method can be used to perform raw SQL queries that
|
||||
return model instances:
|
||||
|
||||
|
||||
@@ -54,6 +54,13 @@ The various cache middlewares are an exception:
|
||||
Even when using database caching, Django's cache backend uses its own
|
||||
database cursor (which is mapped to its own database connection internally).
|
||||
|
||||
.. note::
|
||||
|
||||
The ``TransactionMiddleware`` only affects the database aliased
|
||||
as "default" within your :setting:`DATABASES` setting. If you are using
|
||||
multiple databases and want transaction control over databases other than
|
||||
"default", you will need to write your own transaction middleware.
|
||||
|
||||
.. _transaction-management-functions:
|
||||
|
||||
Controlling transaction management in views
|
||||
|
||||
@@ -192,7 +192,7 @@ from the request's POST data, sends that to admin@example.com and redirects to
|
||||
# to get proper validation errors.
|
||||
return HttpResponse('Make sure all fields are entered and valid.')
|
||||
|
||||
.. _Header injection: http://www.nyphp.org/phundamentals/email_header_injection.php
|
||||
.. _Header injection: http://www.nyphp.org/phundamentals/8_Preventing-Email-Header-Injection
|
||||
|
||||
.. _emailmessage-and-smtpconnection:
|
||||
|
||||
@@ -365,8 +365,6 @@ subtype. For example::
|
||||
Email backends
|
||||
==============
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
The actual sending of an email is handled by the email backend.
|
||||
|
||||
The email backend class has the following methods:
|
||||
|
||||
@@ -75,6 +75,29 @@ using a Python built-in ``file`` object::
|
||||
Now you can use any of the documented attributes and methods
|
||||
of the :class:`~django.core.files.File` class.
|
||||
|
||||
Be aware that files created in this way are not automatically closed.
|
||||
The following approach may be used to close files automatically::
|
||||
|
||||
>>> from django.core.files import File
|
||||
|
||||
# Create a Python file object using open() and the with statement
|
||||
>>> with open('/tmp/hello.world', 'w') as f:
|
||||
>>> myfile = File(f)
|
||||
>>> for line in myfile:
|
||||
>>> print line
|
||||
>>> myfile.closed
|
||||
True
|
||||
>>> f.closed
|
||||
True
|
||||
|
||||
Closing files is especially important when accessing file fields in a loop
|
||||
over a large number of objects:: If files are not manually closed after
|
||||
accessing them, the risk of running out of file descriptors may arise. This
|
||||
may lead to the following error:
|
||||
|
||||
IOError: [Errno 24] Too many open files
|
||||
|
||||
|
||||
File storage
|
||||
============
|
||||
|
||||
|
||||
@@ -102,15 +102,12 @@ limit the maximum number of empty forms the formset will display::
|
||||
<tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" id="id_form-0-title" /></td></tr>
|
||||
<tr><th><label for="id_form-0-pub_date">Pub date:</label></th><td><input type="text" name="form-0-pub_date" id="id_form-0-pub_date" /></td></tr>
|
||||
|
||||
.. versionchanged:: 1.2
|
||||
|
||||
If the value of ``max_num`` is greater than the number of existing
|
||||
objects, up to ``extra`` additional blank forms will be added to the formset,
|
||||
so long as the total number of forms does not exceed ``max_num``.
|
||||
|
||||
A ``max_num`` value of ``None`` (the default) puts no limit on the number of
|
||||
forms displayed. Please note that the default value of ``max_num`` was changed
|
||||
from ``0`` to ``None`` in version 1.2 to allow ``0`` as a valid value.
|
||||
forms displayed.
|
||||
|
||||
Formset validation
|
||||
------------------
|
||||
@@ -210,8 +207,6 @@ pre-filled, and is also used to determine how many forms are required. You
|
||||
will probably never need to override either of these methods, so please be
|
||||
sure you understand what they do before doing so.
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
``empty_form``
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -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
|
||||
----------------------
|
||||
|
||||
@@ -89,6 +91,9 @@ The standard pattern for processing a form in a view looks like this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from django.shortcuts import render
|
||||
from django.http import HttpResponseRedirect
|
||||
|
||||
def contact(request):
|
||||
if request.method == 'POST': # If the form has been submitted...
|
||||
form = ContactForm(request.POST) # A form bound to the POST data
|
||||
@@ -99,53 +104,63 @@ The standard pattern for processing a form in a view looks like this:
|
||||
else:
|
||||
form = ContactForm() # An unbound form
|
||||
|
||||
return render_to_response('contact.html', {
|
||||
return render(request, 'contact.html', {
|
||||
'form': form,
|
||||
})
|
||||
|
||||
|
||||
There are three code paths here:
|
||||
There are three possible code paths here:
|
||||
|
||||
1. If the form has not been submitted, an unbound instance of ContactForm is
|
||||
created and passed to the template.
|
||||
2. If the form has been submitted, a bound instance of the form is created
|
||||
using ``request.POST``. If the submitted data is valid, it is processed
|
||||
and the user is re-directed to a "thanks" page.
|
||||
3. If the form has been submitted but is invalid, the bound form instance is
|
||||
passed on to the template.
|
||||
+------------------+---------------+-----------------------------------------+
|
||||
| Form submitted? | Data? | What occurs |
|
||||
+==================+===============+=========================================+
|
||||
| Unsubmitted | None yet | Template gets passed unbound instance |
|
||||
| | | of ContactForm. |
|
||||
+------------------+---------------+-----------------------------------------+
|
||||
| Submitted | Invalid data | Template gets passed bound instance |
|
||||
| | | of ContactForm. |
|
||||
+------------------+---------------+-----------------------------------------+
|
||||
| Submitted | Valid data | Valid data is processed. Redirect to a |
|
||||
| | | "thanks" page. |
|
||||
+------------------+---------------+-----------------------------------------+
|
||||
|
||||
The distinction between **bound** and **unbound** forms is important. An unbound
|
||||
form does not have any data associated with it; when rendered to the user, it
|
||||
will be empty or will contain default values. A bound form does have submitted
|
||||
data, and hence can be used to tell if that data is valid. If an invalid bound
|
||||
form is rendered it can include inline error messages telling the user where
|
||||
they went wrong.
|
||||
The distinction between :ref:`ref-forms-api-bound-unbound` is important:
|
||||
|
||||
See :ref:`ref-forms-api-bound-unbound` for further information on the
|
||||
differences between bound and unbound forms.
|
||||
* An unbound form has no data associated with it. When rendered to the user,
|
||||
it will be empty or will contain default values.
|
||||
|
||||
* A bound form has submitted data, and hence can be used to tell if that data
|
||||
is valid. If an invalid bound form is rendered, it can include inline error
|
||||
messages telling the user what data to correct.
|
||||
|
||||
Handling file uploads with a form
|
||||
---------------------------------
|
||||
|
||||
To see how to handle file uploads with your form see
|
||||
:ref:`binding-uploaded-files` for more information.
|
||||
To see how to handle file uploads with your form, see
|
||||
:ref:`binding-uploaded-files`.
|
||||
|
||||
Processing the data from a form
|
||||
-------------------------------
|
||||
|
||||
Once ``is_valid()`` returns ``True``, you can process the form submission safe
|
||||
in the knowledge that it conforms to the validation rules defined by your form.
|
||||
While you could access ``request.POST`` directly at this point, it is better to
|
||||
access ``form.cleaned_data``. This data has not only been validated but will
|
||||
also be converted in to the relevant Python types for you. In the above example,
|
||||
``cc_myself`` will be a boolean value. Likewise, fields such as ``IntegerField``
|
||||
and ``FloatField`` convert values to a Python int and float respectively. Note
|
||||
that read-only fields are not available in ``form.cleaned_data`` (and setting
|
||||
a value in a custom ``clean()`` method won't have any effect) because these
|
||||
Once ``is_valid()`` returns ``True``, the successfully validated form data
|
||||
will be in the ``form.cleaned_data`` dictionary. This data will have been
|
||||
converted nicely into Python types for you.
|
||||
|
||||
.. note::
|
||||
|
||||
You can still access the unvalidated data directly from ``request.POST`` at
|
||||
this point, but the validated data is better.
|
||||
|
||||
In the above example, ``cc_myself`` will be a boolean value. Likewise, fields
|
||||
such as ``IntegerField`` and ``FloatField`` convert values to a Python int and
|
||||
float respectively.
|
||||
|
||||
Read-only fields are not available in ``form.cleaned_data`` (and setting
|
||||
a value in a custom ``clean()`` method won't have any effect). These
|
||||
fields are displayed as text rather than as input elements, and thus are not
|
||||
posted back to the server.
|
||||
|
||||
Extending the above example, here's how the form data could be processed:
|
||||
Extending the earlier example, here's how the form data could be processed:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@@ -163,7 +178,9 @@ Extending the above example, here's how the form data could be processed:
|
||||
send_mail(subject, message, sender, recipients)
|
||||
return HttpResponseRedirect('/thanks/') # Redirect after POST
|
||||
|
||||
For more on sending email from Django, see :doc:`/topics/email`.
|
||||
.. tip::
|
||||
|
||||
For more on sending email from Django, see :doc:`/topics/email`.
|
||||
|
||||
Displaying a form using a template
|
||||
----------------------------------
|
||||
@@ -294,7 +311,7 @@ templates:
|
||||
The field's label wrapped in the appropriate HTML ``<label>`` tag,
|
||||
e.g. ``<label for="id_email">Email address</label>``
|
||||
|
||||
``{{ field.value }}``
|
||||
``{{ field.value }}``
|
||||
The value of the field. e.g ``someone@example.com``
|
||||
|
||||
``{{ field.html_name }}``
|
||||
|
||||
@@ -109,9 +109,6 @@ Model field Form field
|
||||
``URLField`` ``URLField``
|
||||
=============================== ========================================
|
||||
|
||||
.. versionadded:: 1.2
|
||||
The ``BigIntegerField`` is new in Django 1.2.
|
||||
|
||||
|
||||
As you might expect, the ``ForeignKey`` and ``ManyToManyField`` model field
|
||||
types are special cases:
|
||||
@@ -199,10 +196,11 @@ The ``is_valid()`` method and ``errors``
|
||||
----------------------------------------
|
||||
|
||||
The first time you call ``is_valid()`` or access the ``errors`` attribute of a
|
||||
``ModelForm`` triggers form validation as well as :ref:`model validation
|
||||
<validating-objects>`. This has the side-effect of cleaning the model you pass
|
||||
to the ``ModelForm`` constructor. For instance, calling ``is_valid()`` on your
|
||||
form will convert any date fields on your model to actual date objects.
|
||||
``ModelForm`` triggers :ref:`form validation <form-and-field-validation>` as
|
||||
well as :ref:`model validation <validating-objects>`. This has the side-effect
|
||||
of cleaning the model you pass to the ``ModelForm`` constructor. For instance,
|
||||
calling ``is_valid()`` on your form will convert any date fields on your model
|
||||
to actual date objects.
|
||||
|
||||
|
||||
The ``save()`` method
|
||||
@@ -362,9 +360,6 @@ Since the Author model has only 3 fields, 'name', 'title', and
|
||||
Overriding the default field types or widgets
|
||||
---------------------------------------------
|
||||
|
||||
.. versionadded:: 1.2
|
||||
The ``widgets`` attribute is new in Django 1.2.
|
||||
|
||||
The default field types, as described in the `Field types`_ table above, are
|
||||
sensible defaults. If you have a ``DateField`` in your model, chances are you'd
|
||||
want that to be represented as a ``DateField`` in your form. But
|
||||
@@ -442,6 +437,10 @@ parameter when declaring the form field::
|
||||
class Meta:
|
||||
model = Article
|
||||
|
||||
You must ensure that the type of the form field can be used to set the
|
||||
contents of the corresponding model field. When they are not compatible,
|
||||
you will get a ``ValueError`` as no implicit conversion takes place.
|
||||
|
||||
See the :doc:`form field documentation </ref/forms/fields>` for more information
|
||||
on fields and their arguments.
|
||||
|
||||
@@ -670,8 +669,6 @@ are saved properly.
|
||||
Limiting the number of editable objects
|
||||
---------------------------------------
|
||||
|
||||
.. versionchanged:: 1.2
|
||||
|
||||
As with regular formsets, you can use the ``max_num`` and ``extra`` parameters
|
||||
to ``modelformset_factory`` to limit the number of extra forms displayed.
|
||||
|
||||
@@ -698,8 +695,6 @@ so long as the total number of forms does not exceed ``max_num``::
|
||||
<tr><th><label for="id_form-2-name">Name:</label></th><td><input id="id_form-2-name" type="text" name="form-2-name" value="Walt Whitman" maxlength="100" /><input type="hidden" name="form-2-id" value="2" id="id_form-2-id" /></td></tr>
|
||||
<tr><th><label for="id_form-3-name">Name:</label></th><td><input id="id_form-3-name" type="text" name="form-3-name" maxlength="100" /><input type="hidden" name="form-3-id" id="id_form-3-id" /></td></tr>
|
||||
|
||||
.. versionchanged:: 1.2
|
||||
|
||||
A ``max_num`` value of ``None`` (the default) puts no limit on the number of
|
||||
forms displayed.
|
||||
|
||||
@@ -886,7 +881,8 @@ of a model. Here's how you can do that::
|
||||
formset = BookInlineFormSet(request.POST, request.FILES, instance=author)
|
||||
if formset.is_valid():
|
||||
formset.save()
|
||||
# Do something.
|
||||
# Do something. Should generally end with a redirect. For example:
|
||||
return HttpResponseRedirect(author.get_absolute_url())
|
||||
else:
|
||||
formset = BookInlineFormSet(instance=author)
|
||||
return render_to_response("manage_books.html", {
|
||||
|
||||
@@ -178,6 +178,50 @@ Three settings control Django's file upload behavior:
|
||||
Which means "try to upload to memory first, then fall back to temporary
|
||||
files."
|
||||
|
||||
Handling uploaded files with a model
|
||||
------------------------------------
|
||||
|
||||
If you're saving a file on a :class:`~django.db.models.Model` with a
|
||||
:class:`~django.db.models.FileField`, using a :class:`~django.forms.ModelForm`
|
||||
makes this process much easier. The file object will be saved when calling
|
||||
``form.save()``::
|
||||
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import render
|
||||
from .forms import ModelFormWithFileField
|
||||
|
||||
def upload_file(request):
|
||||
if request.method == 'POST':
|
||||
form = ModelFormWithFileField(request.POST, request.FILES)
|
||||
if form.is_valid():
|
||||
# file is saved
|
||||
form.save()
|
||||
return HttpResponseRedirect('/success/url/')
|
||||
else:
|
||||
form = ModelFormWithFileField()
|
||||
return render('upload.html', {'form': form})
|
||||
|
||||
If you are constructing an object manually, you can simply assign the file
|
||||
object from :attr:`request.FILES <django.http.HttpRequest.FILES>` to the file
|
||||
field in the model::
|
||||
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import render
|
||||
from .forms import UploadFileForm
|
||||
from .models import ModelWithFileField
|
||||
|
||||
def upload_file(request):
|
||||
if request.method == 'POST':
|
||||
form = UploadFileForm(request.POST, request.FILES)
|
||||
if form.is_valid():
|
||||
instance = ModelWithFileField(file_field=request.FILES['file'])
|
||||
instance.save()
|
||||
return HttpResponseRedirect('/success/url/')
|
||||
else:
|
||||
form = UploadFileForm()
|
||||
return render('upload.html', {'form': form})
|
||||
|
||||
|
||||
``UploadedFile`` objects
|
||||
========================
|
||||
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
Generic views
|
||||
=============
|
||||
|
||||
See :doc:`/ref/class-based-views`.
|
||||
See :doc:`/ref/class-based-views/index`.
|
||||
|
||||
@@ -199,7 +199,8 @@ of caveats:
|
||||
define ``__init__`` as requiring any arguments.
|
||||
|
||||
* Unlike the ``process_*`` methods which get called once per request,
|
||||
``__init__`` gets called only *once*, when the Web server starts up.
|
||||
``__init__`` gets called only *once*, when the Web server responds to the
|
||||
first request.
|
||||
|
||||
Marking middleware as unused
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -423,6 +423,9 @@ cookie will be sent on every request.
|
||||
Similarly, the ``expires`` part of a session cookie is updated each time the
|
||||
session cookie is sent.
|
||||
|
||||
.. versionchanged:: 1.5
|
||||
The session is not saved if the response's status code is 500.
|
||||
|
||||
Browser-length sessions vs. persistent sessions
|
||||
===============================================
|
||||
|
||||
@@ -503,7 +506,7 @@ SESSION_COOKIE_DOMAIN
|
||||
Default: ``None``
|
||||
|
||||
The domain to use for session cookies. Set this to a string such as
|
||||
``".lawrence.com"`` (note the leading dot!) for cross-domain cookies, or use
|
||||
``".example.com"`` (note the leading dot!) for cross-domain cookies, or use
|
||||
``None`` for a standard domain cookie.
|
||||
|
||||
SESSION_COOKIE_HTTPONLY
|
||||
@@ -521,7 +524,7 @@ consistently by all browsers. However, when it is honored, it can be a
|
||||
useful way to mitigate the risk of client side script accessing the
|
||||
protected cookie data.
|
||||
|
||||
.. _HTTPOnly: http://www.owasp.org/index.php/HTTPOnly
|
||||
.. _HTTPOnly: https://www.owasp.org/index.php/HTTPOnly
|
||||
|
||||
SESSION_COOKIE_NAME
|
||||
-------------------
|
||||
|
||||
@@ -15,7 +15,7 @@ introduce controlled coupling for convenience's sake.
|
||||
``render``
|
||||
==========
|
||||
|
||||
.. function:: render(request, template[, dictionary][, context_instance][, content_type][, status][, current_app])
|
||||
.. function:: render(request, template_name[, dictionary][, context_instance][, content_type][, status][, current_app])
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
@@ -32,7 +32,7 @@ Required arguments
|
||||
``request``
|
||||
The request object used to generate this response.
|
||||
|
||||
``template``
|
||||
``template_name``
|
||||
The full name of a template to use or sequence of template names.
|
||||
|
||||
Optional arguments
|
||||
|
||||
@@ -314,9 +314,6 @@ that should be called if none of the URL patterns match.
|
||||
By default, this is ``'django.views.defaults.page_not_found'``. That default
|
||||
value should suffice.
|
||||
|
||||
.. versionchanged:: 1.2
|
||||
Previous versions of Django only accepted strings representing import paths.
|
||||
|
||||
handler500
|
||||
----------
|
||||
|
||||
@@ -329,9 +326,6 @@ have runtime errors in view code.
|
||||
By default, this is ``'django.views.defaults.server_error'``. That default
|
||||
value should suffice.
|
||||
|
||||
.. versionchanged:: 1.2
|
||||
Previous versions of Django only accepted strings representing import paths.
|
||||
|
||||
Notes on capturing text in URLs
|
||||
===============================
|
||||
|
||||
@@ -710,8 +704,8 @@ target each pattern individually by using its name:
|
||||
|
||||
.. code-block:: html+django
|
||||
|
||||
{% url arch-summary 1945 %}
|
||||
{% url full-archive 2007 %}
|
||||
{% url 'arch-summary' 1945 %}
|
||||
{% url 'full-archive' 2007 %}
|
||||
|
||||
Even though both URL patterns refer to the ``archive`` view here, using the
|
||||
``name`` parameter to ``url()`` allows you to tell them apart in templates.
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
Format localization
|
||||
===================
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
@@ -23,7 +21,10 @@ necessary to set :setting:`USE_L10N = True <USE_L10N>` in your settings file.
|
||||
|
||||
The default :file:`settings.py` file created by :djadmin:`django-admin.py
|
||||
startproject <startproject>` includes :setting:`USE_L10N = True <USE_L10N>`
|
||||
for convenience.
|
||||
for convenience. Note, however, that to enable number formatting with
|
||||
thousand separators it is necessary to set :setting:`USE_THOUSAND_SEPARATOR
|
||||
= True <USE_THOUSAND_SEPARATOR>` in your settings file. Alternatively, you
|
||||
could use :tfilter:`intcomma` to format numbers in your template.
|
||||
|
||||
.. note::
|
||||
|
||||
|
||||
@@ -509,7 +509,7 @@ Setup
|
||||
Finally, our calendar system contains interesting traps for computers::
|
||||
|
||||
>>> import datetime
|
||||
>>> def substract_one_year(value): # DON'T DO THAT!
|
||||
>>> def one_year_before(value): # DON'T DO THAT!
|
||||
... return value.replace(year=value.year - 1)
|
||||
>>> one_year_before(datetime.datetime(2012, 3, 1, 10, 0))
|
||||
datetime.datetime(2011, 3, 1, 10, 0)
|
||||
|
||||
@@ -37,6 +37,13 @@ from your :setting:`TEMPLATE_CONTEXT_PROCESSORS` setting.
|
||||
controls if Django should implement format localization. See
|
||||
:doc:`/topics/i18n/formatting` for more details.
|
||||
|
||||
.. note::
|
||||
|
||||
Make sure you've activated translation for your project (the fastest way is
|
||||
to check if :setting:`MIDDLEWARE_CLASSES` includes
|
||||
:mod:`django.middleware.locale.LocaleMiddleware`). If you haven't yet,
|
||||
see :ref:`how-django-discovers-language-preference`.
|
||||
|
||||
Internationalization: in Python code
|
||||
====================================
|
||||
|
||||
@@ -317,10 +324,10 @@ Model fields and relationships ``verbose_name`` and ``help_text`` option values
|
||||
For example, to translate the help text of the *name* field in the following
|
||||
model, do the following::
|
||||
|
||||
from django.utils.translation import ugettext_lazy
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
class MyThing(models.Model):
|
||||
name = models.CharField(help_text=ugettext_lazy('This is the help text'))
|
||||
name = models.CharField(help_text=_('This is the help text'))
|
||||
|
||||
You can mark names of ``ForeignKey``, ``ManyTomanyField`` or ``OneToOneField``
|
||||
relationship as translatable by using their ``verbose_name`` options::
|
||||
@@ -344,14 +351,14 @@ It is recommended to always provide explicit
|
||||
relying on the fallback English-centric and somewhat naïve determination of
|
||||
verbose names Django performs bu looking at the model's class name::
|
||||
|
||||
from django.utils.translation import ugettext_lazy
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
class MyThing(models.Model):
|
||||
name = models.CharField(_('name'), help_text=ugettext_lazy('This is the help text'))
|
||||
name = models.CharField(_('name'), help_text=_('This is the help text'))
|
||||
|
||||
class Meta:
|
||||
verbose_name = ugettext_lazy('my thing')
|
||||
verbose_name_plural = ugettext_lazy('my things')
|
||||
verbose_name = _('my thing')
|
||||
verbose_name_plural = _('my things')
|
||||
|
||||
Model methods ``short_description`` attribute values
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -416,8 +423,8 @@ result is included in a string. For example::
|
||||
|
||||
from django.utils.translation import string_concat
|
||||
...
|
||||
name = ugettext_lazy(u'John Lennon')
|
||||
instrument = ugettext_lazy(u'guitar')
|
||||
name = ugettext_lazy('John Lennon')
|
||||
instrument = ugettext_lazy('guitar')
|
||||
result = string_concat(name, ': ', instrument)
|
||||
|
||||
In this case, the lazy translations in ``result`` will only be converted to
|
||||
@@ -456,6 +463,9 @@ Internationalization: in template code
|
||||
Translations in :doc:`Django templates </topics/templates>` uses two template
|
||||
tags and a slightly different syntax than in Python code. To give your template
|
||||
access to these tags, put ``{% load i18n %}`` toward the top of your template.
|
||||
As with all template tags, this tag needs to be loaded in all templates which
|
||||
use translations, even those templates that extend from other templates which
|
||||
have already loaded the ``i18n`` tag.
|
||||
|
||||
.. templatetag:: trans
|
||||
|
||||
@@ -596,7 +606,7 @@ apply.
|
||||
Reverse URL lookups cannot be carried out within the ``blocktrans`` and should
|
||||
be retrieved (and stored) beforehand::
|
||||
|
||||
{% url path.to.view arg arg2 as the_url %}
|
||||
{% url 'path.to.view' arg arg2 as the_url %}
|
||||
{% blocktrans %}
|
||||
This is a URL: {{ the_url }}
|
||||
{% endblocktrans %}
|
||||
@@ -790,7 +800,7 @@ To use the catalog, just pull in the dynamically generated script like this:
|
||||
|
||||
.. code-block:: html+django
|
||||
|
||||
<script type="text/javascript" src="{% url django.views.i18n.javascript_catalog %}"></script>
|
||||
<script type="text/javascript" src="{% url 'django.views.i18n.javascript_catalog' %}"></script>
|
||||
|
||||
This uses reverse URL lookup to find the URL of the JavaScript catalog view.
|
||||
When the catalog is loaded, your JavaScript code can use the standard
|
||||
@@ -890,7 +900,7 @@ prepend the current active language code to all url patterns defined within
|
||||
|
||||
urlpatterns += i18n_patterns('',
|
||||
url(r'^about/$', 'about.view', name='about'),
|
||||
url(r'^news/$', include(news_patterns, namespace='news')),
|
||||
url(r'^news/', include(news_patterns, namespace='news')),
|
||||
)
|
||||
|
||||
|
||||
@@ -945,7 +955,7 @@ URL patterns can also be marked translatable using the
|
||||
|
||||
urlpatterns += i18n_patterns('',
|
||||
url(_(r'^about/$'), 'about.view', name='about'),
|
||||
url(_(r'^news/$'), include(news_patterns, namespace='news')),
|
||||
url(_(r'^news/'), include(news_patterns, namespace='news')),
|
||||
)
|
||||
|
||||
|
||||
@@ -992,7 +1002,7 @@ template tag. It enables the given language in the enclosed template section:
|
||||
{% trans "View this category in:" %}
|
||||
{% for lang_code, lang_name in languages %}
|
||||
{% language lang_code %}
|
||||
<a href="{% url category slug=category.slug %}">{{ lang_name }}</a>
|
||||
<a href="{% url 'category' slug=category.slug %}">{{ lang_name }}</a>
|
||||
{% endlanguage %}
|
||||
{% endfor %}
|
||||
|
||||
@@ -1039,8 +1049,6 @@ Django comes with a tool, :djadmin:`django-admin.py makemessages
|
||||
commands from the GNU gettext toolset: ``xgettext``, ``msgfmt``,
|
||||
``msgmerge`` and ``msguniq``.
|
||||
|
||||
.. versionchanged:: 1.2
|
||||
|
||||
The minimum version of the ``gettext`` utilities supported is 0.15.
|
||||
|
||||
To create or update a message file, run this command::
|
||||
@@ -1249,6 +1257,12 @@ Activate this view by adding the following line to your URLconf::
|
||||
|
||||
(Note that this example makes the view available at ``/i18n/setlang/``.)
|
||||
|
||||
.. warning::
|
||||
|
||||
Make sure that you don't include the above URL within
|
||||
:func:`~django.conf.urls.i18n.i18n_patterns` - it needs to be
|
||||
language-independent itself to work correctly.
|
||||
|
||||
The view expects to be called via the ``POST`` method, with a ``language``
|
||||
parameter set in request. If session support is enabled, the view
|
||||
saves the language choice in the user's session. Otherwise, it saves the
|
||||
@@ -1268,7 +1282,7 @@ Here's example HTML template code:
|
||||
|
||||
.. code-block:: html+django
|
||||
|
||||
<form action="/i18n/setlang/" method="post">
|
||||
<form action="{% url 'set_language' %}" method="post">
|
||||
{% csrf_token %}
|
||||
<input name="next" type="hidden" value="{{ redirect_to }}" />
|
||||
<select name="language">
|
||||
|
||||
@@ -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
|
||||
@@ -22,6 +22,7 @@ Introductions to all the key parts of Django you'll need to know:
|
||||
i18n/index
|
||||
logging
|
||||
pagination
|
||||
python3
|
||||
security
|
||||
serialization
|
||||
settings
|
||||
|
||||
@@ -9,7 +9,7 @@ Install Python
|
||||
|
||||
Being a Python Web framework, Django requires Python.
|
||||
|
||||
It works with any Python version from 2.6 to 2.7 (due to backwards
|
||||
It works with any Python version from 2.6.5 to 2.7 (due to backwards
|
||||
incompatibilities in Python 3.0, Django does not currently work with
|
||||
Python 3.0; see :doc:`the Django FAQ </faq/install>` for more
|
||||
information on supported Python versions and the 3.0 transition).
|
||||
@@ -62,7 +62,7 @@ for information on how to configure mod_wsgi once you have it
|
||||
installed.
|
||||
|
||||
If you can't use mod_wsgi for some reason, fear not: Django supports many other
|
||||
deployment options. One is :doc:`uWSGI </howto/deployment/fastcgi>`; it works
|
||||
deployment options. One is :doc:`uWSGI </howto/deployment/wsgi/uwsgi>`; it works
|
||||
very well with `nginx`_. Another is :doc:`FastCGI </howto/deployment/fastcgi>`,
|
||||
perfect for using Django with servers other than Apache. Additionally, Django
|
||||
follows the WSGI spec (:pep:`3333`), which allows it to run on a variety of
|
||||
@@ -70,7 +70,7 @@ server platforms. See the `server-arrangements wiki page`_ for specific
|
||||
installation instructions for each platform.
|
||||
|
||||
.. _Apache: http://httpd.apache.org/
|
||||
.. _nginx: http://nginx.net/
|
||||
.. _nginx: http://nginx.org/
|
||||
.. _mod_wsgi: http://code.google.com/p/modwsgi/
|
||||
.. _server-arrangements wiki page: https://code.djangoproject.com/wiki/ServerArrangements
|
||||
|
||||
@@ -107,7 +107,7 @@ database bindings are installed.
|
||||
|
||||
If you're on Windows, check out the unofficial `compiled Windows version`_.
|
||||
|
||||
* If you're using MySQL, you'll need MySQLdb_, version 1.2.1p2 or higher. You
|
||||
* If you're using MySQL, you'll need the ``MySQL-python`` package, version 1.2.1p2 or higher. You
|
||||
will also want to read the database-specific :ref:`notes for the MySQL
|
||||
backend <mysql-notes>`.
|
||||
|
||||
@@ -135,7 +135,6 @@ Django will need permission to create a test database.
|
||||
.. _MySQL: http://www.mysql.com/
|
||||
.. _psycopg: http://initd.org/pub/software/psycopg/
|
||||
.. _compiled Windows version: http://stickpeople.com/projects/python/win-psycopg/
|
||||
.. _MySQLdb: http://sourceforge.net/projects/mysql-python
|
||||
.. _SQLite: http://www.sqlite.org/
|
||||
.. _pysqlite: http://trac.edgewall.org/wiki/PySqlite
|
||||
.. _cx_Oracle: http://cx-oracle.sourceforge.net/
|
||||
|
||||
@@ -209,8 +209,8 @@ By default, Django uses the `dictConfig format`_.
|
||||
``logging.dictConfig`` is a builtin library in Python 2.7. In
|
||||
order to make this library available for users of earlier Python
|
||||
versions, Django includes a copy as part of ``django.utils.log``.
|
||||
If you have Python 2.7, the system native library will be used; if
|
||||
you have Python 2.6 or earlier, Django's copy will be used.
|
||||
If you have Python 2.7 or later, the system native library will be used; if
|
||||
you have Python 2.6, Django's copy will be used.
|
||||
|
||||
In order to configure logging, you use :setting:`LOGGING` to define a
|
||||
dictionary of logging settings. These settings describes the loggers,
|
||||
@@ -218,10 +218,11 @@ handlers, filters and formatters that you want in your logging setup,
|
||||
and the log levels and other properties that you want those components
|
||||
to have.
|
||||
|
||||
Logging is configured immediately after settings have been loaded.
|
||||
Since the loading of settings is one of the first things that Django
|
||||
does, you can be certain that loggers are always ready for use in your
|
||||
project code.
|
||||
Logging is configured as soon as settings have been loaded
|
||||
(either manually using :func:`~django.conf.settings.configure` or when at least
|
||||
one setting is accessed). Since the loading of settings is one of the first
|
||||
things that Django does, you can be certain that loggers are always ready for
|
||||
use in your project code.
|
||||
|
||||
.. _dictConfig format: http://docs.python.org/library/logging.config.html#configuration-dictionary-schema
|
||||
|
||||
|
||||
@@ -43,7 +43,9 @@ page::
|
||||
>>> page2.has_other_pages()
|
||||
True
|
||||
>>> page2.next_page_number()
|
||||
3
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
EmptyPage: That page contains no results
|
||||
>>> page2.previous_page_number()
|
||||
1
|
||||
>>> page2.start_index() # The 1-based index of the first item on this page
|
||||
@@ -253,13 +255,19 @@ Methods
|
||||
|
||||
.. method:: Page.next_page_number()
|
||||
|
||||
Returns the next page number. Note that this is "dumb" and will return the
|
||||
next page number regardless of whether a subsequent page exists.
|
||||
Returns the next page number.
|
||||
|
||||
.. versionchanged:: 1.5
|
||||
|
||||
Raises :exc:`InvalidPage` if next page doesn't exist.
|
||||
|
||||
.. method:: Page.previous_page_number()
|
||||
|
||||
Returns the previous page number. Note that this is "dumb" and will return
|
||||
the previous page number regardless of whether a previous page exists.
|
||||
Returns the previous page number.
|
||||
|
||||
.. versionchanged:: 1.5
|
||||
|
||||
Raises :exc:`InvalidPage` if previous page doesn't exist.
|
||||
|
||||
.. method:: Page.start_index()
|
||||
|
||||
|
||||
396
docs/topics/python3.txt
Normal file
396
docs/topics/python3.txt
Normal file
@@ -0,0 +1,396 @@
|
||||
===================
|
||||
Porting to Python 3
|
||||
===================
|
||||
|
||||
Django 1.5 is the first version of Django to support Python 3. The same code
|
||||
runs both on Python 2 (≥ 2.6.5) and Python 3 (≥ 3.2), thanks to the six_
|
||||
compatibility layer.
|
||||
|
||||
.. _six: http://packages.python.org/six/
|
||||
|
||||
This document is primarily targeted at authors of pluggable application
|
||||
who want to support both Python 2 and 3. It also describes guidelines that
|
||||
apply to Django's code.
|
||||
|
||||
Philosophy
|
||||
==========
|
||||
|
||||
This document assumes that you are familiar with the changes between Python 2
|
||||
and Python 3. If you aren't, read `Python's official porting guide`_ first.
|
||||
Refreshing your knowledge of unicode handling on Python 2 and 3 will help; the
|
||||
`Pragmatic Unicode`_ presentation is a good resource.
|
||||
|
||||
Django uses the *Python 2/3 Compatible Source* strategy. Of course, you're
|
||||
free to chose another strategy for your own code, especially if you don't need
|
||||
to stay compatible with Python 2. But authors of pluggable applications are
|
||||
encouraged to use the same porting strategy as Django itself.
|
||||
|
||||
Writing compatible code is much easier if you target Python ≥ 2.6. You will
|
||||
most likely take advantage of the compatibility functions introduced in Django
|
||||
1.5, like :mod:`django.utils.six`, so your application will also require
|
||||
Django ≥ 1.5.
|
||||
|
||||
Obviously, writing compatible source code adds some overhead, and that can
|
||||
cause frustration. Django's developers have found that attempting to write
|
||||
Python 3 code that's compatible with Python 2 is much more rewarding than the
|
||||
opposite. Not only does that make your code more future-proof, but Python 3's
|
||||
advantages (like the saner string handling) start shining quickly. Dealing
|
||||
with Python 2 becomes a backwards compatibility requirement, and we as
|
||||
developers are used to dealing with such constraints.
|
||||
|
||||
Porting tools provided by Django are inspired by this philosophy, and it's
|
||||
reflected throughout this guide.
|
||||
|
||||
.. _Python's official porting guide: http://docs.python.org/py3k/howto/pyporting.html
|
||||
.. _Pragmatic Unicode: http://nedbatchelder.com/text/unipain.html
|
||||
|
||||
Porting tips
|
||||
============
|
||||
|
||||
Unicode literals
|
||||
----------------
|
||||
|
||||
This step consists in:
|
||||
|
||||
- Adding ``from __future__ import unicode_literals`` at the top of your Python
|
||||
modules -- it's best to put it in each and every module, otherwise you'll
|
||||
keep checking the top of your files to see which mode is in effect;
|
||||
- Removing the ``u`` prefix before unicode strings;
|
||||
- Adding a ``b`` prefix before bytestrings.
|
||||
|
||||
Performing these changes systematically guarantees backwards compatibility.
|
||||
|
||||
However, Django applications generally don't need bytestrings, since Django
|
||||
only exposes unicode interfaces to the programmer. Python 3 discourages using
|
||||
bytestrings, except for binary data or byte-oriented interfaces. Python 2
|
||||
makes bytestrings and unicode strings effectively interchangeable, as long as
|
||||
they only contain ASCII data. Take advantage of this to use unicode strings
|
||||
wherever possible and avoid the ``b`` prefixes.
|
||||
|
||||
.. note::
|
||||
|
||||
Python 2's ``u`` prefix is a syntax error in Python 3.2 but it will be
|
||||
allowed again in Python 3.3 thanks to :pep:`414`. Thus, this
|
||||
transformation is optional if you target Python ≥ 3.3. It's still
|
||||
recommended, per the "write Python 3 code" philosophy.
|
||||
|
||||
String handling
|
||||
---------------
|
||||
|
||||
Python 2's :class:`unicode` type was renamed :class:`str` in Python 3,
|
||||
:class:`str` was renamed :class:`bytes`, and :class:`basestring` disappeared.
|
||||
six_ provides :ref:`tools <string-handling-with-six>` to deal with these
|
||||
changes.
|
||||
|
||||
Django also contains several string related classes and functions in the
|
||||
:mod:`django.utils.encoding` and :mod:`django.utils.safestring` modules. Their
|
||||
names used the words ``str``, which doesn't mean the same thing in Python 2
|
||||
and Python 3, and ``unicode``, which doesn't exist in Python 3. In order to
|
||||
avoid ambiguity and confusion these concepts were renamed ``bytes`` and
|
||||
``text``.
|
||||
|
||||
Here are the name changes in :mod:`django.utils.encoding`:
|
||||
|
||||
================== ==================
|
||||
Old name New name
|
||||
================== ==================
|
||||
``smart_str`` ``smart_bytes``
|
||||
``smart_unicode`` ``smart_text``
|
||||
``force_unicode`` ``force_text``
|
||||
================== ==================
|
||||
|
||||
For backwards compatibility, the old names still work on Python 2. Under
|
||||
Python 3, ``smart_str`` is an alias for ``smart_text``.
|
||||
|
||||
.. note::
|
||||
|
||||
:mod:`django.utils.encoding` was deeply refactored in Django 1.5 to
|
||||
provide a more consistent API. Check its documentation for more
|
||||
information.
|
||||
|
||||
:mod:`django.utils.safestring` is mostly used via the
|
||||
:func:`~django.utils.safestring.mark_safe` and
|
||||
:func:`~django.utils.safestring.mark_for_escaping` functions, which didn't
|
||||
change. In case you're using the internals, here are the name changes:
|
||||
|
||||
================== ==================
|
||||
Old name New name
|
||||
================== ==================
|
||||
``EscapeString`` ``EscapeBytes``
|
||||
``EscapeUnicode`` ``EscapeText``
|
||||
``SafeString`` ``SafeBytes``
|
||||
``SafeUnicode`` ``SafeText``
|
||||
================== ==================
|
||||
|
||||
For backwards compatibility, the old names still work on Python 2. Under
|
||||
Python 3, ``EscapeString`` and ``SafeString`` are aliases for ``EscapeText``
|
||||
and ``SafeText`` respectively.
|
||||
|
||||
:meth:`__str__` and :meth:`__unicode__` methods
|
||||
-----------------------------------------------
|
||||
|
||||
In Python 2, the object model specifies :meth:`__str__` and
|
||||
:meth:`__unicode__` methods. If these methods exist, they must return
|
||||
:class:`str` (bytes) and :class:`unicode` (text) respectively.
|
||||
|
||||
The ``print`` statement and the :func:`str` built-in call :meth:`__str__` to
|
||||
determine the human-readable representation of an object. The :func:`unicode`
|
||||
built-in calls :meth:`__unicode__` if it exists, and otherwise falls back to
|
||||
:meth:`__str__` and decodes the result with the system encoding. Conversely,
|
||||
the :class:`~django.db.models.Model` base class automatically derives
|
||||
:meth:`__str__` from :meth:`__unicode__` by encoding to UTF-8.
|
||||
|
||||
In Python 3, there's simply :meth:`__str__`, which must return :class:`str`
|
||||
(text).
|
||||
|
||||
(It is also possible to define :meth:`__bytes__`, but Django application have
|
||||
little use for that method, because they hardly ever deal with
|
||||
:class:`bytes`.)
|
||||
|
||||
Django provides a simple way to define :meth:`__str__` and :meth:`__unicode__`
|
||||
methods that work on Python 2 and 3: you must define a :meth:`__str__` method
|
||||
returning text and to apply the
|
||||
:func:`~django.utils.encoding.python_2_unicode_compatible` decorator.
|
||||
|
||||
On Python 3, the decorator is a no-op. On Python 2, it defines appropriate
|
||||
:meth:`__unicode__` and :meth:`__str__` methods (replacing the original
|
||||
:meth:`__str__` method in the process). Here's an example::
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class MyClass(object):
|
||||
def __str__(self):
|
||||
return "Instance of my class"
|
||||
|
||||
This technique is the best match for Django's porting philosophy.
|
||||
|
||||
Finally, note that :meth:`__repr__` must return a :class:`str` on all versions
|
||||
of Python.
|
||||
|
||||
:class:`dict` and :class:`dict`-like classes
|
||||
--------------------------------------------
|
||||
|
||||
:meth:`dict.keys`, :meth:`dict.items` and :meth:`dict.values` return lists in
|
||||
Python 2 and iterators in Python 3. :class:`~django.http.QueryDict` and the
|
||||
:class:`dict`-like classes defined in :mod:`django.utils.datastructures`
|
||||
behave likewise in Python 3.
|
||||
|
||||
six_ provides compatibility functions to work around this change:
|
||||
:func:`~six.iterkeys`, :func:`~six.iteritems`, and :func:`~six.itervalues`.
|
||||
Django's bundled version adds :func:`~django.utils.six.iterlists` for
|
||||
:class:`~django.utils.datastructures.MultiValueDict` and its subclasses.
|
||||
|
||||
:class:`~django.http.HttpRequest` and :class:`~django.http.HttpResponse` objects
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
According to :pep:`3333`:
|
||||
|
||||
- headers are always :class:`str` objects,
|
||||
- input and output streams are always :class:`bytes` objects.
|
||||
|
||||
Specifically, :attr:`HttpResponse.content <django.http.HttpResponse.content>`
|
||||
contains :class:`bytes`, which may become an issue if you compare it with a
|
||||
:class:`str` in your tests. The preferred solution is to rely on
|
||||
:meth:`~django.test.TestCase.assertContains` and
|
||||
:meth:`~django.test.TestCase.assertNotContains`. These methods accept a
|
||||
response and a unicode string as arguments.
|
||||
|
||||
Coding guidelines
|
||||
=================
|
||||
|
||||
The following guidelines are enforced in Django's source code. They're also
|
||||
recommended for third-party application who follow the same porting strategy.
|
||||
|
||||
Syntax requirements
|
||||
-------------------
|
||||
|
||||
Unicode
|
||||
~~~~~~~
|
||||
|
||||
In Python 3, all strings are considered Unicode by default. The ``unicode``
|
||||
type from Python 2 is called ``str`` in Python 3, and ``str`` becomes
|
||||
``bytes``.
|
||||
|
||||
You mustn't use the ``u`` prefix before a unicode string literal because it's
|
||||
a syntax error in Python 3.2. You must prefix byte strings with ``b``.
|
||||
|
||||
In order to enable the same behavior in Python 2, every module must import
|
||||
``unicode_literals`` from ``__future__``::
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
my_string = "This is an unicode literal"
|
||||
my_bytestring = b"This is a bytestring"
|
||||
|
||||
If you need a byte string literal under Python 2 and a unicode string literal
|
||||
under Python 3, use the :func:`str` builtin::
|
||||
|
||||
str('my string')
|
||||
|
||||
In Python 3, there aren't any automatic conversions between :class:`str` and
|
||||
:class:`bytes`, and the :mod:`codecs` module became more strict.
|
||||
:meth:`str.decode` always returns :class:`bytes`, and :meth:`bytes.decode`
|
||||
always returns :class:`str`. As a consequence, the following pattern is
|
||||
sometimes necessary::
|
||||
|
||||
value = value.encode('ascii', 'ignore').decode('ascii')
|
||||
|
||||
Be cautious if you have to `slice bytestrings`_.
|
||||
|
||||
.. _slice bytestrings: http://docs.python.org/py3k/howto/pyporting.html#bytes-literals
|
||||
|
||||
Exceptions
|
||||
~~~~~~~~~~
|
||||
|
||||
When you capture exceptions, use the ``as`` keyword::
|
||||
|
||||
try:
|
||||
...
|
||||
except MyException as exc:
|
||||
...
|
||||
|
||||
This older syntax was removed in Python 3::
|
||||
|
||||
try:
|
||||
...
|
||||
except MyException, exc: # Don't do that!
|
||||
...
|
||||
|
||||
The syntax to reraise an exception with a different traceback also changed.
|
||||
Use :func:`six.reraise`.
|
||||
|
||||
Magic methods
|
||||
-------------
|
||||
|
||||
Use the patterns below to handle magic methods renamed in Python 3.
|
||||
|
||||
Iterators
|
||||
~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
class MyIterator(object):
|
||||
def __iter__(self):
|
||||
return self # implement some logic here
|
||||
|
||||
def __next__(self):
|
||||
raise StopIteration # implement some logic here
|
||||
|
||||
next = __next__ # Python 2 compatibility
|
||||
|
||||
Boolean evaluation
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
class MyBoolean(object):
|
||||
|
||||
def __bool__(self):
|
||||
return True # implement some logic here
|
||||
|
||||
__nonzero__ = __bool__ # Python 2 compatibility
|
||||
|
||||
Division
|
||||
~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
class MyDivisible(object):
|
||||
|
||||
def __truediv__(self, other):
|
||||
return self / other # implement some logic here
|
||||
|
||||
__div__ = __truediv__ # Python 2 compatibility
|
||||
|
||||
def __itruediv__(self, other):
|
||||
return self // other # implement some logic here
|
||||
|
||||
__idiv__ = __itruediv__ # Python 2 compatibility
|
||||
|
||||
.. module: django.utils.six
|
||||
|
||||
Writing compatible code with six
|
||||
--------------------------------
|
||||
|
||||
six_ is the canonical compatibility library for supporting Python 2 and 3 in
|
||||
a single codebase. Read its documentation!
|
||||
|
||||
:mod:`six` is bundled with Django: you can import it as :mod:`django.utils.six`.
|
||||
|
||||
Here are the most common changes required to write compatible code.
|
||||
|
||||
.. _string-handling-with-six:
|
||||
|
||||
String handling
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
The ``basestring`` and ``unicode`` types were removed in Python 3, and the
|
||||
meaning of ``str`` changed. To test these types, use the following idioms::
|
||||
|
||||
isinstance(myvalue, six.string_types) # replacement for basestring
|
||||
isinstance(myvalue, six.text_type) # replacement for unicode
|
||||
isinstance(myvalue, bytes) # replacement for str
|
||||
|
||||
Python ≥ 2.6 provides ``bytes`` as an alias for ``str``, so you don't need
|
||||
:attr:`six.binary_type`.
|
||||
|
||||
``long``
|
||||
~~~~~~~~
|
||||
|
||||
The ``long`` type no longer exists in Python 3. ``1L`` is a syntax error. Use
|
||||
:data:`six.integer_types` check if a value is an integer or a long::
|
||||
|
||||
isinstance(myvalue, six.integer_types) # replacement for (int, long)
|
||||
|
||||
``xrange``
|
||||
~~~~~~~~~~
|
||||
|
||||
Import :func:`six.moves.xrange` wherever you use ``xrange``.
|
||||
|
||||
Moved modules
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Some modules were renamed in Python 3. The :mod:`django.utils.six.moves
|
||||
<six.moves>` module provides a compatible location to import them.
|
||||
|
||||
The ``urllib``, ``urllib2`` and ``urlparse`` modules were reworked in depth
|
||||
and :mod:`django.utils.six.moves <six.moves>` doesn't handle them. Django
|
||||
explicitly tries both locations, as follows::
|
||||
|
||||
try:
|
||||
from urllib.parse import urlparse, urlunparse
|
||||
except ImportError: # Python 2
|
||||
from urlparse import urlparse, urlunparse
|
||||
|
||||
PY3
|
||||
~~~
|
||||
|
||||
If you need different code in Python 2 and Python 3, check :data:`six.PY3`::
|
||||
|
||||
if six.PY3:
|
||||
# do stuff Python 3-wise
|
||||
else:
|
||||
# do stuff Python 2-wise
|
||||
|
||||
This is a last resort solution when :mod:`six` doesn't provide an appropriate
|
||||
function.
|
||||
|
||||
.. module:: django.utils.six
|
||||
|
||||
Customizations of six
|
||||
---------------------
|
||||
|
||||
The version of six bundled with Django includes one extra function:
|
||||
|
||||
.. function:: iterlists(MultiValueDict)
|
||||
|
||||
Returns an iterator over the lists of values of a
|
||||
:class:`~django.utils.datastructures.MultiValueDict`. This replaces
|
||||
:meth:`~django.utils.datastructures.MultiValueDict.iterlists()` on Python
|
||||
2 and :meth:`~django.utils.datastructures.MultiValueDict.lists()` on
|
||||
Python 3.
|
||||
|
||||
In addition to six' defaults moves, Django's version provides ``thread`` as
|
||||
``_thread`` and ``dummy_thread`` as ``_dummy_thread``.
|
||||
@@ -122,22 +122,19 @@ transferred between client and server, and in some cases -- **active** network
|
||||
attackers -- to alter data that is sent in either direction.
|
||||
|
||||
If you want the protection that HTTPS provides, and have enabled it on your
|
||||
server, there are some additional steps to consider to ensure that sensitive
|
||||
information is not leaked:
|
||||
server, there are some additional steps you may need:
|
||||
|
||||
* If necessary, set :setting:`SECURE_PROXY_SSL_HEADER`, ensuring that you have
|
||||
understood the warnings there thoroughly. Failure to do this can result
|
||||
in CSRF vulnerabilities, and failure to do it correctly can also be
|
||||
dangerous!
|
||||
|
||||
* Set up redirection so that requests over HTTP are redirected to HTTPS.
|
||||
|
||||
It is possible to do this with a piece of Django middleware. However, this has
|
||||
problems for the common case of a Django app running behind a reverse
|
||||
proxy. Often, reverse proxies are configured to set the ``X-Forwarded-SSL``
|
||||
header (or equivalent) if the incoming connection was HTTPS, and the absence
|
||||
of this header could be used to detect a request that was not HTTPS. However,
|
||||
this method usually cannot be relied on, as a client, or a malicious active
|
||||
network attacker, could also set this header.
|
||||
|
||||
So, for the case of a reverse proxy, it is recommended that the main Web
|
||||
server should be configured to do the redirect to HTTPS, or configured to send
|
||||
HTTP requests to an app that unconditionally redirects to HTTPS.
|
||||
This could be done using a custom middleware. Please note the caveats under
|
||||
:setting:`SECURE_PROXY_SSL_HEADER`. For the case of a reverse proxy, it may be
|
||||
easier or more secure to configure the main Web server to do the redirect to
|
||||
HTTPS.
|
||||
|
||||
* Use 'secure' cookies.
|
||||
|
||||
|
||||
@@ -173,12 +173,12 @@ In particular, :ref:`lazy translation objects <lazy-translations>` need a
|
||||
|
||||
import json
|
||||
from django.utils.functional import Promise
|
||||
from django.utils.encoding import force_unicode
|
||||
from django.utils.encoding import force_text
|
||||
|
||||
class LazyEncoder(json.JSONEncoder):
|
||||
def default(self, obj):
|
||||
if isinstance(obj, Promise):
|
||||
return force_unicode(obj)
|
||||
return force_text(obj)
|
||||
return super(LazyEncoder, self).default(obj)
|
||||
|
||||
.. _special encoder: http://docs.python.org/library/json.html#encoders-and-decoders
|
||||
@@ -188,11 +188,6 @@ In particular, :ref:`lazy translation objects <lazy-translations>` need a
|
||||
Natural keys
|
||||
------------
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
The ability to use natural keys when serializing/deserializing data was
|
||||
added in the 1.2 release.
|
||||
|
||||
The default serialization strategy for foreign keys and many-to-many relations
|
||||
is to serialize the value of the primary key(s) of the objects in the relation.
|
||||
This strategy works well for most objects, but it can cause difficulty in some
|
||||
|
||||
@@ -52,10 +52,10 @@ called when the signal is sent by using the
|
||||
:meth:`.Signal.connect` method:
|
||||
|
||||
.. method:: Signal.connect(receiver, [sender=None, weak=True, dispatch_uid=None])
|
||||
|
||||
|
||||
:param receiver: The callback function which will be connected to this
|
||||
signal. See :ref:`receiver-functions` for more information.
|
||||
|
||||
|
||||
:param sender: Specifies a particular sender to receive signals from. See
|
||||
:ref:`connecting-to-specific-signals` for more information.
|
||||
|
||||
@@ -129,10 +129,17 @@ receiver:
|
||||
|
||||
Now, our ``my_callback`` function will be called each time a request finishes.
|
||||
|
||||
Note that ``receiver`` can also take a list of signals to connect a function
|
||||
to.
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
The ``receiver`` decorator was added in Django 1.3.
|
||||
|
||||
.. versionchanged:: 1.5
|
||||
|
||||
The ability to pass a list of signals was added.
|
||||
|
||||
.. admonition:: Where should this code live?
|
||||
|
||||
You can put signal handling and registration code anywhere you like.
|
||||
@@ -182,7 +189,7 @@ Preventing duplicate signals
|
||||
In some circumstances, the module in which you are connecting signals may be
|
||||
imported multiple times. This can cause your receiver function to be
|
||||
registered more than once, and thus called multiples times for a single signal
|
||||
event.
|
||||
event.
|
||||
|
||||
If this behavior is problematic (such as when using signals to
|
||||
send an email whenever a model is saved), pass a unique identifier as
|
||||
@@ -228,7 +235,7 @@ Remember that you're allowed to change this list of arguments at any time, so ge
|
||||
Sending signals
|
||||
---------------
|
||||
|
||||
There are two ways to send send signals in Django.
|
||||
There are two ways to send signals in Django.
|
||||
|
||||
.. method:: Signal.send(sender, **kwargs)
|
||||
.. method:: Signal.send_robust(sender, **kwargs)
|
||||
|
||||
@@ -226,6 +226,10 @@ tags:
|
||||
Athlete: {{ athlete_list.0.name }}
|
||||
{% endif %}
|
||||
|
||||
While the above example works, be aware that most template filters return
|
||||
strings, so mathematical comparisons using filters will generally not work
|
||||
as you expect. :tfilter:`length` is an exception.
|
||||
|
||||
:ttag:`block` and :ttag:`extends`
|
||||
Set up `template inheritance`_ (see below), a powerful way
|
||||
of cutting down on "boilerplate" in templates.
|
||||
|
||||
@@ -291,9 +291,6 @@ label::
|
||||
|
||||
$ ./manage.py test animals.AnimalTestCase.test_animals_can_speak
|
||||
|
||||
.. versionadded:: 1.2
|
||||
The ability to select individual doctests was added.
|
||||
|
||||
You can use the same rules if you're using doctests. Django will use the
|
||||
test label as a path to the test method or class that you want to run.
|
||||
If your ``models.py`` or ``tests.py`` has a function with a doctest, or
|
||||
@@ -311,9 +308,6 @@ If you're using a ``__test__`` dictionary to specify doctests for a
|
||||
module, Django will use the label as a key in the ``__test__`` dictionary
|
||||
for defined in ``models.py`` and ``tests.py``.
|
||||
|
||||
.. versionadded:: 1.2
|
||||
You can now trigger a graceful exit from a test run by pressing ``Ctrl-C``.
|
||||
|
||||
If you press ``Ctrl-C`` while the tests are running, the test runner will
|
||||
wait for the currently running test to complete and then exit gracefully.
|
||||
During a graceful exit the test runner will output details of any test
|
||||
@@ -392,8 +386,6 @@ advanced settings.
|
||||
Testing master/slave configurations
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
If you're testing a multiple database configuration with master/slave
|
||||
replication, this strategy of creating test databases poses a problem.
|
||||
When the test databases are created, there won't be any replication,
|
||||
@@ -486,6 +478,32 @@ If there are any circular dependencies in the
|
||||
:setting:`TEST_DEPENDENCIES` definition, an ``ImproperlyConfigured``
|
||||
exception will be raised.
|
||||
|
||||
Order in which tests are executed
|
||||
---------------------------------
|
||||
|
||||
In order to guarantee that all ``TestCase`` code starts with a clean database,
|
||||
the Django test runner reorders tests in the following way:
|
||||
|
||||
* First, all unittests (including :class:`unittest.TestCase`,
|
||||
:class:`~django.test.SimpleTestCase`, :class:`~django.test.TestCase` and
|
||||
:class:`~django.test.TransactionTestCase`) are run with no particular ordering
|
||||
guaranteed nor enforced among them.
|
||||
|
||||
* Then any other tests (e.g. doctests) that may alter the database without
|
||||
restoring it to its original state are run.
|
||||
|
||||
.. versionchanged:: 1.5
|
||||
Before Django 1.5, the only guarantee was that
|
||||
:class:`~django.test.TestCase` tests were always ran first, before any other
|
||||
tests.
|
||||
|
||||
.. note::
|
||||
|
||||
The new ordering of tests may reveal unexpected dependencies on test case
|
||||
ordering. This is the case with doctests that relied on state left in the
|
||||
database by a given :class:`~django.test.TransactionTestCase` test, they
|
||||
must be updated to be able to run independently.
|
||||
|
||||
Other test conditions
|
||||
---------------------
|
||||
|
||||
@@ -553,6 +571,21 @@ failed and erroneous tests. If all the tests pass, the return code is 0. This
|
||||
feature is useful if you're using the test-runner script in a shell script and
|
||||
need to test for success or failure at that level.
|
||||
|
||||
Speeding up the tests
|
||||
---------------------
|
||||
|
||||
In recent versions of Django, the default password hasher is rather slow by
|
||||
design. If during your tests you are authenticating many users, you may want
|
||||
to use a custom settings file and set the :setting:`PASSWORD_HASHERS` setting
|
||||
to a faster hashing algorithm::
|
||||
|
||||
PASSWORD_HASHERS = (
|
||||
'django.contrib.auth.hashers.MD5PasswordHasher',
|
||||
)
|
||||
|
||||
Don't forget to also include in :setting:`PASSWORD_HASHERS` any hashing
|
||||
algorithm used in fixtures, if any.
|
||||
|
||||
Testing tools
|
||||
=============
|
||||
|
||||
@@ -650,8 +683,6 @@ Note a few important things about how the test client works:
|
||||
* By default, the test client will disable any CSRF checks
|
||||
performed by your site.
|
||||
|
||||
.. versionadded:: 1.2.2
|
||||
|
||||
If, for some reason, you *want* the test client to perform CSRF
|
||||
checks, you can create an instance of the test client that
|
||||
enforces CSRF checks. To do this, pass in the
|
||||
@@ -664,10 +695,23 @@ Note a few important things about how the test client works:
|
||||
Making requests
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Use the ``django.test.client.Client`` class to make requests. It requires no
|
||||
arguments at time of construction:
|
||||
Use the ``django.test.client.Client`` class to make requests.
|
||||
|
||||
.. class:: Client()
|
||||
.. class:: Client(enforce_csrf_checks=False, **defaults)
|
||||
|
||||
It requires no arguments at time of construction. However, you can use
|
||||
keywords arguments to specify some default headers. For example, this will
|
||||
send a ``User-Agent`` HTTP header in each request::
|
||||
|
||||
>>> c = Client(HTTP_USER_AGENT='Mozilla/5.0')
|
||||
|
||||
The values from the ``extra`` keywords arguments passed to
|
||||
:meth:`~django.test.client.Client.get()`,
|
||||
:meth:`~django.test.client.Client.post()`, etc. have precedence over
|
||||
the defaults passed to the class constructor.
|
||||
|
||||
The ``enforce_csrf_checks`` argument can be used to test CSRF
|
||||
protection (see above).
|
||||
|
||||
Once you have a ``Client`` instance, you can call any of the following
|
||||
methods:
|
||||
@@ -1091,8 +1135,11 @@ The following is a simple unit test using the request factory::
|
||||
response = my_view(request)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
TestCase
|
||||
--------
|
||||
Test cases
|
||||
----------
|
||||
|
||||
Provided test case classes
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. currentmodule:: django.test
|
||||
|
||||
@@ -1106,16 +1153,19 @@ Normal Python unit test classes extend a base class of
|
||||
|
||||
Hierarchy of Django unit testing classes
|
||||
|
||||
TestCase
|
||||
^^^^^^^^
|
||||
|
||||
.. class:: TestCase()
|
||||
|
||||
This class provides some additional capabilities that can be useful for testing
|
||||
Web sites.
|
||||
|
||||
Converting a normal :class:`unittest.TestCase` to a Django :class:`TestCase` is
|
||||
easy: just change the base class of your test from :class:`unittest.TestCase` to
|
||||
:class:`django.test.TestCase`. All of the standard Python unit test
|
||||
functionality will continue to be available, but it will be augmented with some
|
||||
useful additions, including:
|
||||
easy: Just change the base class of your test from `'unittest.TestCase'` to
|
||||
`'django.test.TestCase'`. All of the standard Python unit test functionality
|
||||
will continue to be available, but it will be augmented with some useful
|
||||
additions, including:
|
||||
|
||||
* Automatic loading of fixtures.
|
||||
|
||||
@@ -1123,11 +1173,18 @@ useful additions, including:
|
||||
|
||||
* Creates a TestClient instance.
|
||||
|
||||
* Django-specific assertions for testing for things
|
||||
like redirection and form errors.
|
||||
* Django-specific assertions for testing for things like redirection and form
|
||||
errors.
|
||||
|
||||
.. versionchanged:: 1.5
|
||||
The order in which tests are run has changed. See `Order in which tests are
|
||||
executed`_.
|
||||
|
||||
``TestCase`` inherits from :class:`~django.test.TransactionTestCase`.
|
||||
|
||||
TransactionTestCase
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. class:: TransactionTestCase()
|
||||
|
||||
Django ``TestCase`` classes make use of database transaction facilities, if
|
||||
@@ -1139,38 +1196,66 @@ behavior, you should use a Django ``TransactionTestCase``.
|
||||
|
||||
``TransactionTestCase`` and ``TestCase`` are identical except for the manner
|
||||
in which the database is reset to a known state and the ability for test code
|
||||
to test the effects of commit and rollback. A ``TransactionTestCase`` resets
|
||||
the database before the test runs by truncating all tables and reloading
|
||||
initial data. A ``TransactionTestCase`` may call commit and rollback and
|
||||
observe the effects of these calls on the database.
|
||||
to test the effects of commit and rollback:
|
||||
|
||||
A ``TestCase``, on the other hand, does not truncate tables and reload initial
|
||||
data at the beginning of a test. Instead, it encloses the test code in a
|
||||
database transaction that is rolled back at the end of the test. It also
|
||||
prevents the code under test from issuing any commit or rollback operations
|
||||
on the database, to ensure that the rollback at the end of the test restores
|
||||
the database to its initial state. In order to guarantee that all ``TestCase``
|
||||
code starts with a clean database, the Django test runner runs all ``TestCase``
|
||||
tests first, before any other tests (e.g. doctests) that may alter the
|
||||
database without restoring it to its original state.
|
||||
* A ``TransactionTestCase`` resets the database after the test runs by
|
||||
truncating all tables. A ``TransactionTestCase`` may call commit and rollback
|
||||
and observe the effects of these calls on the database.
|
||||
|
||||
When running on a database that does not support rollback (e.g. MySQL with the
|
||||
MyISAM storage engine), ``TestCase`` falls back to initializing the database
|
||||
by truncating tables and reloading initial data.
|
||||
* A ``TestCase``, on the other hand, does not truncate tables after a test.
|
||||
Instead, it encloses the test code in a database transaction that is rolled
|
||||
back at the end of the test. It also prevents the code under test from
|
||||
issuing any commit or rollback operations on the database, to ensure that the
|
||||
rollback at the end of the test restores the database to its initial state.
|
||||
|
||||
When running on a database that does not support rollback (e.g. MySQL with the
|
||||
MyISAM storage engine), ``TestCase`` falls back to initializing the database
|
||||
by truncating tables and reloading initial data.
|
||||
|
||||
.. note::
|
||||
|
||||
.. versionchanged:: 1.5
|
||||
|
||||
Prior to 1.5, ``TransactionTestCase`` flushed the database tables *before*
|
||||
each test. In Django 1.5, this is instead done *after* the test has been run.
|
||||
|
||||
When the flush took place before the test, it was guaranteed that primary
|
||||
key values started at one in :class:`~django.test.TransactionTestCase`
|
||||
tests.
|
||||
|
||||
Tests should not depend on this behaviour, but for legacy tests that do, the
|
||||
:attr:`~TransactionTestCase.reset_sequences` attribute can be used until
|
||||
the test has been properly updated.
|
||||
|
||||
.. versionchanged:: 1.5
|
||||
The order in which tests are run has changed. See `Order in which tests are
|
||||
executed`_.
|
||||
|
||||
``TransactionTestCase`` inherits from :class:`~django.test.SimpleTestCase`.
|
||||
|
||||
.. note::
|
||||
The ``TestCase`` use of rollback to un-do the effects of the test code
|
||||
may reveal previously-undetected errors in test code. For example,
|
||||
test code that assumes primary keys values will be assigned starting at
|
||||
one may find that assumption no longer holds true when rollbacks instead
|
||||
of table truncation are being used to reset the database. Similarly,
|
||||
the reordering of tests so that all ``TestCase`` classes run first may
|
||||
reveal unexpected dependencies on test case ordering. In such cases a
|
||||
quick fix is to switch the ``TestCase`` to a ``TransactionTestCase``.
|
||||
A better long-term fix, that allows the test to take advantage of the
|
||||
speed benefit of ``TestCase``, is to fix the underlying test problem.
|
||||
.. attribute:: TransactionTestCase.reset_sequences
|
||||
|
||||
.. versionadded:: 1.5
|
||||
|
||||
Setting ``reset_sequences = True`` on a ``TransactionTestCase`` will make
|
||||
sure sequences are always reset before the test run::
|
||||
|
||||
class TestsThatDependsOnPrimaryKeySequences(TransactionTestCase):
|
||||
reset_sequences = True
|
||||
|
||||
def test_animal_pk(self):
|
||||
lion = Animal.objects.create(name="lion", sound="roar")
|
||||
# lion.pk is guaranteed to always be 1
|
||||
self.assertEqual(lion.pk, 1)
|
||||
|
||||
Unless you are explicitly testing primary keys sequence numbers, it is
|
||||
recommended that you do not hard code primary key values in tests.
|
||||
|
||||
Using ``reset_sequences = True`` will slow down the test, since the primary
|
||||
key reset is an relatively expensive database operation.
|
||||
|
||||
SimpleTestCase
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
.. class:: SimpleTestCase()
|
||||
|
||||
@@ -1376,8 +1461,6 @@ Multi-database support
|
||||
|
||||
.. attribute:: TestCase.multi_db
|
||||
|
||||
.. versionadded:: 1.2
|
||||
|
||||
Django sets up a test database corresponding to every database that is
|
||||
defined in the :setting:`DATABASES` definition in your settings
|
||||
file. However, a big part of the time taken to run a Django TestCase
|
||||
@@ -1501,9 +1584,6 @@ Assertions
|
||||
|
||||
.. currentmodule:: django.test
|
||||
|
||||
.. versionchanged:: 1.2
|
||||
Added ``msg_prefix`` argument.
|
||||
|
||||
As Python's normal :class:`unittest.TestCase` class implements assertion methods
|
||||
such as :meth:`~unittest.TestCase.assertTrue` and
|
||||
:meth:`~unittest.TestCase.assertEqual`, Django's custom :class:`TestCase` class
|
||||
@@ -1927,7 +2007,7 @@ out the `full reference`_ for more details.
|
||||
|
||||
.. _Selenium: http://seleniumhq.org/
|
||||
.. _selenium package: http://pypi.python.org/pypi/selenium
|
||||
.. _full reference: http://readthedocs.org/docs/selenium-python/en/latest/api.html
|
||||
.. _full reference: http://selenium-python.readthedocs.org/en/latest/api.html
|
||||
.. _Firefox: http://www.mozilla.com/firefox/
|
||||
|
||||
.. note::
|
||||
@@ -2011,9 +2091,6 @@ process to satisfy whatever testing requirements you may have.
|
||||
Defining a test runner
|
||||
----------------------
|
||||
|
||||
.. versionchanged:: 1.2
|
||||
Prior to 1.2, test runners were a single function, not a class.
|
||||
|
||||
.. currentmodule:: django.test.simple
|
||||
|
||||
A test runner is a class defining a ``run_tests()`` method. Django ships
|
||||
|
||||
Reference in New Issue
Block a user