summaryrefslogtreecommitdiff
path: root/docs/topics
diff options
context:
space:
mode:
authorAndrew Godwin <andrew@aeracode.org>2013-06-07 11:15:34 +0100
committerAndrew Godwin <andrew@aeracode.org>2013-06-07 11:15:34 +0100
commit3c296382b8dea5de7f4e1e11b66bd7cecaf2ee51 (patch)
tree0ca12593be82971691ffca01a836d00d3fcb3bd4 /docs/topics
parent7609e0b42e0014a6ad0adf9dafc7018cb268070e (diff)
parent357d62d9f2972bf1bc21e5835c12c849143e06af (diff)
Merge remote-tracking branch 'core/master' into schema-alteration
Conflicts: django/db/models/fields/related.py
Diffstat (limited to 'docs/topics')
-rw-r--r--docs/topics/auth/customizing.txt8
-rw-r--r--docs/topics/cache.txt41
-rw-r--r--docs/topics/class-based-views/generic-display.txt6
-rw-r--r--docs/topics/class-based-views/generic-editing.txt1
-rw-r--r--docs/topics/class-based-views/mixins.txt8
-rw-r--r--docs/topics/db/aggregation.txt40
-rw-r--r--docs/topics/db/managers.txt22
-rw-r--r--docs/topics/db/models.txt52
-rw-r--r--docs/topics/db/queries.txt48
-rw-r--r--docs/topics/db/transactions.txt62
-rw-r--r--docs/topics/files.txt2
-rw-r--r--docs/topics/forms/formsets.txt29
-rw-r--r--docs/topics/forms/media.txt5
-rw-r--r--docs/topics/forms/modelforms.txt69
-rw-r--r--docs/topics/http/file-uploads.txt2
-rw-r--r--docs/topics/http/sessions.txt18
-rw-r--r--docs/topics/http/urls.txt31
-rw-r--r--docs/topics/http/views.txt28
-rw-r--r--docs/topics/i18n/timezones.txt2
-rw-r--r--docs/topics/i18n/translation.txt48
-rw-r--r--docs/topics/logging.txt31
-rw-r--r--docs/topics/python3.txt4
-rw-r--r--docs/topics/testing/advanced.txt4
-rw-r--r--docs/topics/testing/overview.txt275
24 files changed, 639 insertions, 197 deletions
diff --git a/docs/topics/auth/customizing.txt b/docs/topics/auth/customizing.txt
index 56f3e60350..bc021b14ad 100644
--- a/docs/topics/auth/customizing.txt
+++ b/docs/topics/auth/customizing.txt
@@ -939,7 +939,7 @@ authentication app::
raise ValueError('Users must have an email address')
user = self.model(
- email=MyUserManager.normalize_email(email),
+ email=self.normalize_email(email),
date_of_birth=date_of_birth,
)
@@ -1075,7 +1075,6 @@ code would be required in the app's ``admin.py`` file::
(None, {'fields': ('email', 'password')}),
('Personal info', {'fields': ('date_of_birth',)}),
('Permissions', {'fields': ('is_admin',)}),
- ('Important dates', {'fields': ('last_login',)}),
)
add_fieldsets = (
(None, {
@@ -1092,3 +1091,8 @@ code would be required in the app's ``admin.py`` file::
# ... and, since we're not using Django's builtin permissions,
# unregister the Group model from admin.
admin.site.unregister(Group)
+
+Finally, specify the custom model as the default user model for your project
+using the :setting:`AUTH_USER_MODEL` setting in your ``settings.py``::
+
+ AUTH_USER_MODEL = 'customauth.MyUser'
diff --git a/docs/topics/cache.txt b/docs/topics/cache.txt
index 6b6d57511a..2352770bad 100644
--- a/docs/topics/cache.txt
+++ b/docs/topics/cache.txt
@@ -443,15 +443,9 @@ Then, add the following required settings to your Django settings file:
The cache middleware caches GET and HEAD responses with status 200, where the request
and response headers allow. Responses to requests for the same URL with different
query parameters are considered to be unique pages and are cached separately.
-Optionally, if the :setting:`CACHE_MIDDLEWARE_ANONYMOUS_ONLY`
-setting is ``True``, only anonymous requests (i.e., not those made by a
-logged-in user) will be cached. This is a simple and effective way of disabling
-caching for any user-specific pages (including Django's admin interface). Note
-that if you use :setting:`CACHE_MIDDLEWARE_ANONYMOUS_ONLY`, you should make
-sure you've activated ``AuthenticationMiddleware``. The cache middleware
-expects that a HEAD request is answered with the same response headers as
-the corresponding GET request; in which case it can return a cached GET
-response for HEAD request.
+The cache middleware expects that a HEAD request is answered with the same
+response headers as the corresponding GET request; in which case it can return
+a cached GET response for HEAD request.
Additionally, the cache middleware automatically sets a few headers in each
:class:`~django.http.HttpResponse`:
@@ -707,10 +701,15 @@ The basic interface is ``set(key, value, timeout)`` and ``get(key)``::
>>> cache.get('my_key')
'hello, world!'
-The ``timeout`` argument is optional and defaults to the ``timeout``
-argument of the appropriate backend in the :setting:`CACHES` setting
-(explained above). It's the number of seconds the value should be stored
-in the cache.
+The ``timeout`` argument is optional and defaults to the ``timeout`` argument
+of the appropriate backend in the :setting:`CACHES` setting (explained above).
+It's the number of seconds the value should be stored in the cache. Passing in
+``None`` for ``timeout`` will cache the value forever.
+
+.. versionchanged:: 1.6
+
+ Previously, passing ``None`` explicitly would use the default timeout
+ value.
If the object doesn't exist in the cache, ``cache.get()`` returns ``None``::
@@ -998,8 +997,8 @@ produces different content based on some difference in request headers -- such
as a cookie, or a language, or a user-agent -- you'll need to use the ``Vary``
header to tell caching mechanisms that the page output depends on those things.
-To do this in Django, use the convenient ``vary_on_headers`` view decorator,
-like so::
+To do this in Django, use the convenient
+:func:`django.views.decorators.vary.vary_on_headers` view decorator, like so::
from django.views.decorators.vary import vary_on_headers
@@ -1028,8 +1027,9 @@ the user-agent ``Mozilla`` and the cookie value ``foo=bar`` will be considered
different from a request with the user-agent ``Mozilla`` and the cookie value
``foo=ham``.
-Because varying on cookie is so common, there's a ``vary_on_cookie``
-decorator. These two views are equivalent::
+Because varying on cookie is so common, there's a
+:func:`django.views.decorators.vary.vary_on_cookie` decorator. These two views
+are equivalent::
@vary_on_cookie
def my_view(request):
@@ -1042,7 +1042,7 @@ decorator. These two views are equivalent::
The headers you pass to ``vary_on_headers`` are not case sensitive;
``"User-Agent"`` is the same thing as ``"user-agent"``.
-You can also use a helper function, ``django.utils.cache.patch_vary_headers``,
+You can also use a helper function, :func:`django.utils.cache.patch_vary_headers`,
directly. This function sets, or adds to, the ``Vary header``. For example::
from django.utils.cache import patch_vary_headers
@@ -1091,8 +1091,9 @@ exclusive. The decorator ensures that the "public" directive is removed if
"private" should be set (and vice versa). An example use of the two directives
would be a blog site that offers both private and public entries. Public
entries may be cached on any shared cache. The following code uses
-``patch_cache_control``, the manual way to modify the cache control header
-(it is internally called by the ``cache_control`` decorator)::
+:func:`django.utils.cache.patch_cache_control`, the manual way to modify the
+cache control header (it is internally called by the ``cache_control``
+decorator)::
from django.views.decorators.cache import patch_cache_control
from django.views.decorators.vary import vary_on_cookie
diff --git a/docs/topics/class-based-views/generic-display.txt b/docs/topics/class-based-views/generic-display.txt
index 64b998770f..7ffa471e79 100644
--- a/docs/topics/class-based-views/generic-display.txt
+++ b/docs/topics/class-based-views/generic-display.txt
@@ -248,7 +248,7 @@ 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
+ from books.models import Publisher
class PublisherDetail(DetailView):
@@ -326,6 +326,7 @@ various useful things are stored on ``self``; as well as the request
Here, we have a URLconf with a single captured group::
# urls.py
+ from django.conf.urls import patterns
from books.views import PublisherBookList
urlpatterns = patterns('',
@@ -375,6 +376,7 @@ 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
+ from django.db import models
class Author(models.Model):
salutation = models.CharField(max_length=10)
@@ -390,6 +392,7 @@ updated.
First, we'd need to add an author detail bit in the URLconf to point to a
custom view::
+ from django.conf.urls import patterns, url
from books.views import AuthorDetailView
urlpatterns = patterns('',
@@ -401,7 +404,6 @@ 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
diff --git a/docs/topics/class-based-views/generic-editing.txt b/docs/topics/class-based-views/generic-editing.txt
index 86c5280159..7c4e02cc4e 100644
--- a/docs/topics/class-based-views/generic-editing.txt
+++ b/docs/topics/class-based-views/generic-editing.txt
@@ -222,6 +222,7 @@ works for AJAX requests as well as 'normal' form POSTs::
from django.http import HttpResponse
from django.views.generic.edit import CreateView
+ from myapp.models import Author
class AjaxableResponseMixin(object):
"""
diff --git a/docs/topics/class-based-views/mixins.txt b/docs/topics/class-based-views/mixins.txt
index 9550d2fb86..980e571c85 100644
--- a/docs/topics/class-based-views/mixins.txt
+++ b/docs/topics/class-based-views/mixins.txt
@@ -258,6 +258,7 @@ mixin.
We can hook this into our URLs easily enough::
# urls.py
+ from django.conf.urls import patterns, url
from books.views import RecordInterest
urlpatterns = patterns('',
@@ -440,6 +441,7 @@ Our new ``AuthorDetail`` looks like this::
from django.core.urlresolvers import reverse
from django.views.generic import DetailView
from django.views.generic.edit import FormMixin
+ from books.models import Author
class AuthorInterestForm(forms.Form):
message = forms.CharField()
@@ -546,6 +548,8 @@ template as ``AuthorDisplay`` is using on ``GET``.
.. code-block:: python
+ from django.core.urlresolvers import reverse
+ from django.http import HttpResponseForbidden
from django.views.generic import FormView
from django.views.generic.detail import SingleObjectMixin
@@ -657,6 +661,8 @@ own version of :class:`~django.views.generic.detail.DetailView` by mixing
:class:`~django.views.generic.detail.DetailView` before template
rendering behavior has been mixed in)::
+ from django.views.generic.detail import BaseDetailView
+
class JSONDetailView(JSONResponseMixin, BaseDetailView):
pass
@@ -675,6 +681,8 @@ and override the implementation of
to defer to the appropriate subclass depending on the type of response that the
user requested::
+ from django.views.generic.detail import SingleObjectTemplateResponseMixin
+
class HybridDetailView(JSONResponseMixin, SingleObjectTemplateResponseMixin, BaseDetailView):
def render_to_response(self, context):
# Look for a 'format=json' GET argument
diff --git a/docs/topics/db/aggregation.txt b/docs/topics/db/aggregation.txt
index 125cd0bdee..1024d6b0c2 100644
--- a/docs/topics/db/aggregation.txt
+++ b/docs/topics/db/aggregation.txt
@@ -18,27 +18,29 @@ used to track the inventory for a series of online bookstores:
.. code-block:: python
+ from django.db import models
+
class Author(models.Model):
- name = models.CharField(max_length=100)
- age = models.IntegerField()
+ name = models.CharField(max_length=100)
+ age = models.IntegerField()
class Publisher(models.Model):
- name = models.CharField(max_length=300)
- num_awards = models.IntegerField()
+ name = models.CharField(max_length=300)
+ num_awards = models.IntegerField()
class Book(models.Model):
- name = models.CharField(max_length=300)
- pages = models.IntegerField()
- price = models.DecimalField(max_digits=10, decimal_places=2)
- rating = models.FloatField()
- authors = models.ManyToManyField(Author)
- publisher = models.ForeignKey(Publisher)
- pubdate = models.DateField()
+ name = models.CharField(max_length=300)
+ pages = models.IntegerField()
+ price = models.DecimalField(max_digits=10, decimal_places=2)
+ rating = models.FloatField()
+ authors = models.ManyToManyField(Author)
+ publisher = models.ForeignKey(Publisher)
+ pubdate = models.DateField()
class Store(models.Model):
- name = models.CharField(max_length=300)
- books = models.ManyToManyField(Book)
- registered_users = models.PositiveIntegerField()
+ name = models.CharField(max_length=300)
+ books = models.ManyToManyField(Book)
+ registered_users = models.PositiveIntegerField()
Cheat sheet
===========
@@ -123,7 +125,7 @@ If you want to generate more than one aggregate, you just add another
argument to the ``aggregate()`` clause. So, if we also wanted to know
the maximum and minimum price of all books, we would issue the query::
- >>> from django.db.models import Avg, Max, Min, Count
+ >>> from django.db.models import Avg, Max, Min
>>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
{'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}
@@ -148,6 +150,7 @@ the number of authors:
.. code-block:: python
# Build an annotated queryset
+ >>> from django.db.models import Count
>>> q = Book.objects.annotate(Count('authors'))
# Interrogate the first object in the queryset
>>> q[0]
@@ -192,6 +195,7 @@ and aggregate the related value.
For example, to find the price range of books offered in each store,
you could use the annotation::
+ >>> from django.db.models import Max, Min
>>> Store.objects.annotate(min_price=Min('books__price'), max_price=Max('books__price'))
This tells Django to retrieve the ``Store`` model, join (through the
@@ -222,7 +226,7 @@ For example, we can ask for all publishers, annotated with their respective
total book stock counters (note how we use ``'book'`` to specify the
``Publisher`` -> ``Book`` reverse foreign key hop)::
- >>> from django.db.models import Count, Min, Sum, Max, Avg
+ >>> from django.db.models import Count, Min, Sum, Avg
>>> Publisher.objects.annotate(Count('book'))
(Every ``Publisher`` in the resulting ``QuerySet`` will have an extra attribute
@@ -269,6 +273,7 @@ constraining the objects for which an annotation is calculated. For example,
you can generate an annotated list of all books that have a title starting
with "Django" using the query::
+ >>> from django.db.models import Count, Avg
>>> Book.objects.filter(name__startswith="Django").annotate(num_authors=Count('authors'))
When used with an ``aggregate()`` clause, a filter has the effect of
@@ -407,6 +412,8 @@ particularly, when counting things.
By way of example, suppose you have a model like this::
+ from django.db import models
+
class Item(models.Model):
name = models.CharField(max_length=10)
data = models.IntegerField()
@@ -457,5 +464,6 @@ For example, if you wanted to calculate the average number of authors per
book you first annotate the set of books with the author count, then
aggregate that author count, referencing the annotation field::
+ >>> from django.db.models import Count, Avg
>>> Book.objects.annotate(num_authors=Count('authors')).aggregate(Avg('num_authors'))
{'num_authors__avg': 1.66}
diff --git a/docs/topics/db/managers.txt b/docs/topics/db/managers.txt
index 2a0f7e4ce0..b940b09d33 100644
--- a/docs/topics/db/managers.txt
+++ b/docs/topics/db/managers.txt
@@ -62,6 +62,8 @@ For example, this custom ``Manager`` offers a method ``with_counts()``, which
returns a list of all ``OpinionPoll`` objects, each with an extra
``num_responses`` attribute that is the result of an aggregate query::
+ from django.db import models
+
class PollManager(models.Manager):
def with_counts(self):
from django.db import connection
@@ -101,6 +103,8 @@ Modifying initial Manager QuerySets
A ``Manager``'s base ``QuerySet`` returns all objects in the system. For
example, using this model::
+ from django.db import models
+
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=50)
@@ -236,7 +240,7 @@ class, but still customize the default manager. For example, suppose you have
this base class::
class AbstractBase(models.Model):
- ...
+ # ...
objects = CustomManager()
class Meta:
@@ -246,14 +250,15 @@ If you use this directly in a subclass, ``objects`` will be the default
manager if you declare no managers in the base class::
class ChildA(AbstractBase):
- ...
+ # ...
# This class has CustomManager as the default manager.
+ pass
If you want to inherit from ``AbstractBase``, but provide a different default
manager, you can provide the default manager on the child class::
class ChildB(AbstractBase):
- ...
+ # ...
# An explicit default manager.
default_manager = OtherManager()
@@ -274,9 +279,10 @@ it into the inheritance hierarchy *after* the defaults::
abstract = True
class ChildC(AbstractBase, ExtraManager):
- ...
+ # ...
# Default manager is CustomManager, but OtherManager is
# also available via the "extra_manager" attribute.
+ pass
Note that while you can *define* a custom manager on the abstract model, you
can't *invoke* any methods using the abstract model. That is::
@@ -349,8 +355,7 @@ the manager class::
class MyManager(models.Manager):
use_for_related_fields = True
-
- ...
+ # ...
If this attribute is set on the *default* manager for a model (only the
default manager is considered in these situations), Django will use that class
@@ -396,7 +401,8 @@ it, whereas the following will not work::
# BAD: Incorrect code
class MyManager(models.Manager):
- ...
+ # ...
+ pass
# Sets the attribute on an instance of MyManager. Django will
# ignore this setting.
@@ -404,7 +410,7 @@ it, whereas the following will not work::
mgr.use_for_related_fields = True
class MyModel(models.Model):
- ...
+ # ...
objects = mgr
# End of incorrect code.
diff --git a/docs/topics/db/models.txt b/docs/topics/db/models.txt
index dd7714052d..c0ba53ddd7 100644
--- a/docs/topics/db/models.txt
+++ b/docs/topics/db/models.txt
@@ -90,6 +90,8 @@ attributes. Be careful not to choose field names that conflict with the
Example::
+ from django.db import models
+
class Musician(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
@@ -180,7 +182,7 @@ ones:
('L', 'Large'),
)
name = models.CharField(max_length=60)
- shirt_size = models.CharField(max_length=2, choices=SHIRT_SIZES)
+ shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)
::
@@ -290,8 +292,11 @@ For example, if a ``Car`` model has a ``Manufacturer`` -- that is, a
``Manufacturer`` makes multiple cars but each ``Car`` only has one
``Manufacturer`` -- use the following definitions::
+ from django.db import models
+
class Manufacturer(models.Model):
# ...
+ pass
class Car(models.Model):
manufacturer = models.ForeignKey(Manufacturer)
@@ -340,8 +345,11 @@ For example, if a ``Pizza`` has multiple ``Topping`` objects -- that is, a
``Topping`` can be on multiple pizzas and each ``Pizza`` has multiple toppings
-- here's how you'd represent that::
+ from django.db import models
+
class Topping(models.Model):
# ...
+ pass
class Pizza(models.Model):
# ...
@@ -403,6 +411,8 @@ intermediate model. The intermediate model is associated with the
that will act as an intermediary. For our musician example, the code would look
something like this::
+ from django.db import models
+
class Person(models.Model):
name = models.CharField(max_length=128)
@@ -583,6 +593,7 @@ It's perfectly OK to relate a model to one from another app. To do this, import
the related model at the top of the file where your model is defined. Then,
just refer to the other model class wherever needed. For example::
+ from django.db import models
from geography.models import ZipCode
class Restaurant(models.Model):
@@ -630,6 +641,8 @@ Meta options
Give your model metadata by using an inner ``class Meta``, like so::
+ from django.db import models
+
class Ox(models.Model):
horn_length = models.IntegerField()
@@ -660,6 +673,8 @@ model.
For example, this model has a few custom methods::
+ from django.db import models
+
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
@@ -729,6 +744,8 @@ A classic use-case for overriding the built-in methods is if you want something
to happen whenever you save an object. For example (see
:meth:`~Model.save` for documentation of the parameters it accepts)::
+ from django.db import models
+
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
@@ -740,6 +757,8 @@ to happen whenever you save an object. For example (see
You can also prevent saving::
+ from django.db import models
+
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
@@ -826,6 +845,8 @@ the child (and Django will raise an exception).
An example::
+ from django.db import models
+
class CommonInfo(models.Model):
name = models.CharField(max_length=100)
age = models.PositiveIntegerField()
@@ -854,14 +875,16 @@ attribute. If a child class does not declare its own :ref:`Meta <meta-options>`
class, it will inherit the parent's :ref:`Meta <meta-options>`. If the child wants to
extend the parent's :ref:`Meta <meta-options>` class, it can subclass it. For example::
+ from django.db import models
+
class CommonInfo(models.Model):
- ...
+ # ...
class Meta:
abstract = True
ordering = ['name']
class Student(CommonInfo):
- ...
+ # ...
class Meta(CommonInfo.Meta):
db_table = 'student_info'
@@ -901,6 +924,8 @@ abstract base class (only), part of the name should contain
For example, given an app ``common/models.py``::
+ from django.db import models
+
class Base(models.Model):
m2m = models.ManyToManyField(OtherModel, related_name="%(app_label)s_%(class)s_related")
@@ -949,6 +974,8 @@ relationship introduces links between the child model and each of its parents
(via an automatically-created :class:`~django.db.models.OneToOneField`).
For example::
+ from django.db import models
+
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
@@ -998,7 +1025,7 @@ If the parent has an ordering and you don't want the child to have any natural
ordering, you can explicitly disable it::
class ChildModel(ParentModel):
- ...
+ # ...
class Meta:
# Remove parent's ordering effect
ordering = []
@@ -1061,15 +1088,21 @@ Proxy models are declared like normal models. You tell Django that it's a
proxy model by setting the :attr:`~django.db.models.Options.proxy` attribute of
the ``Meta`` class to ``True``.
-For example, suppose you want to add a method to the ``Person`` model described
-above. You can do it like this::
+For example, suppose you want to add a method to the ``Person`` model. You can do it like this::
+
+ from django.db import models
+
+ class Person(models.Model):
+ first_name = models.CharField(max_length=30)
+ last_name = models.CharField(max_length=30)
class MyPerson(Person):
class Meta:
proxy = True
def do_something(self):
- ...
+ # ...
+ pass
The ``MyPerson`` class operates on the same database table as its parent
``Person`` class. In particular, any new instances of ``Person`` will also be
@@ -1125,8 +1158,11 @@ classes will still be available.
Continuing our example from above, you could change the default manager used
when you query the ``Person`` model like this::
+ from django.db import models
+
class NewManager(models.Manager):
- ...
+ # ...
+ pass
class MyPerson(Person):
objects = NewManager()
diff --git a/docs/topics/db/queries.txt b/docs/topics/db/queries.txt
index 2553eac27a..bdbdd3fa2a 100644
--- a/docs/topics/db/queries.txt
+++ b/docs/topics/db/queries.txt
@@ -17,6 +17,8 @@ models, which comprise a Weblog application:
.. code-block:: python
+ from django.db import models
+
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
@@ -100,7 +102,8 @@ Saving ``ForeignKey`` and ``ManyToManyField`` fields
Updating a :class:`~django.db.models.ForeignKey` field works exactly the same
way as saving a normal field -- simply assign an object of the right type to
the field in question. This example updates the ``blog`` attribute of an
-``Entry`` instance ``entry``::
+``Entry`` instance ``entry``, assuming appropriate instances of ``Entry`` and
+``Blog`` are already saved to the database (so we can retrieve them below)::
>>> from blog.models import Entry
>>> entry = Entry.objects.get(pk=1)
@@ -711,9 +714,9 @@ for you transparently.
Caching and QuerySets
---------------------
-Each :class:`~django.db.models.query.QuerySet` contains a cache, to minimize
-database access. It's important to understand how it works, in order to write
-the most efficient code.
+Each :class:`~django.db.models.query.QuerySet` contains a cache to minimize
+database access. Understanding how it works will allow you to write the most
+efficient code.
In a newly created :class:`~django.db.models.query.QuerySet`, the cache is
empty. The first time a :class:`~django.db.models.query.QuerySet` is evaluated
@@ -744,6 +747,43 @@ To avoid this problem, simply save the
>>> print([p.headline for p in queryset]) # Evaluate the query set.
>>> print([p.pub_date for p in queryset]) # Re-use the cache from the evaluation.
+When querysets are not cached
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Querysets do not always cache their results. When evaluating only *part* of
+the queryset, the cache is checked, but if it is not populated then the items
+returned by the subsequent query are not cached. Specifically, this means that
+:ref:`limiting the queryset <limiting-querysets>` using an array slice or an
+index will not populate the cache.
+
+For example, repeatedly getting a certain index in a queryset object will query
+the database each time::
+
+ >>> queryset = Entry.objects.all()
+ >>> print queryset[5] # Queries the database
+ >>> print queryset[5] # Queries the database again
+
+However, if the entire queryset has already been evaluated, the cache will be
+checked instead::
+
+ >>> queryset = Entry.objects.all()
+ >>> [entry for entry in queryset] # Queries the database
+ >>> print queryset[5] # Uses cache
+ >>> print queryset[5] # Uses cache
+
+Here are some examples of other actions that will result in the entire queryset
+being evaluated and therefore populate the cache::
+
+ >>> [entry for entry in queryset]
+ >>> bool(queryset)
+ >>> entry in queryset
+ >>> list(queryset)
+
+.. note::
+
+ Simply printing the queryset will not populate the cache. This is because
+ the call to ``__repr__()`` only returns a slice of the entire queryset.
+
.. _complex-lookups-with-q:
Complex lookups with Q objects
diff --git a/docs/topics/db/transactions.txt b/docs/topics/db/transactions.txt
index 78786996cd..e9a626f56b 100644
--- a/docs/topics/db/transactions.txt
+++ b/docs/topics/db/transactions.txt
@@ -45,14 +45,6 @@ You may perfom partial commits and rollbacks in your view code, typically with
the :func:`atomic` context manager. However, at the end of the view, either
all the changes will be committed, or none of them.
-To disable this behavior for a specific view, you must set the
-``transactions_per_request`` attribute of the view function itself to
-``False``, like this::
-
- def my_view(request):
- do_stuff()
- my_view.transactions_per_request = False
-
.. warning::
While the simplicity of this transaction model is appealing, it also makes it
@@ -78,6 +70,26 @@ Note that only the execution of your view is enclosed in the transactions.
Middleware runs outside of the transaction, and so does the rendering of
template responses.
+When :setting:`ATOMIC_REQUESTS <DATABASE-ATOMIC_REQUESTS>` is enabled, it's
+still possible to prevent views from running in a transaction.
+
+.. function:: non_atomic_requests(using=None)
+
+ This decorator will negate the effect of :setting:`ATOMIC_REQUESTS
+ <DATABASE-ATOMIC_REQUESTS>` for a given view::
+
+ from django.db import transaction
+
+ @transaction.non_atomic_requests
+ def my_view(request):
+ do_stuff()
+
+ @transaction.non_atomic_requests(using='other')
+ def my_other_view(request):
+ do_stuff_on_the_other_database()
+
+ It only works if it's applied to the view itself.
+
.. versionchanged:: 1.6
Django used to provide this feature via ``TransactionMiddleware``, which is
@@ -519,8 +531,8 @@ Transaction states
------------------
The three functions described above relied on a concept called "transaction
-states". This mechanisme was deprecated in Django 1.6, but it's still
-available until Django 1.8.
+states". This mechanism was deprecated in Django 1.6, but it's still available
+until Django 1.8.
At any time, each database connection is in one of these two states:
@@ -552,25 +564,16 @@ API changes
Transaction middleware
~~~~~~~~~~~~~~~~~~~~~~
-In Django 1.6, ``TransactionMiddleware`` is deprecated and replaced
+In Django 1.6, ``TransactionMiddleware`` is deprecated and replaced by
:setting:`ATOMIC_REQUESTS <DATABASE-ATOMIC_REQUESTS>`. While the general
-behavior is the same, there are a few differences.
-
-With the transaction middleware, it was still possible to switch to autocommit
-or to commit explicitly in a view. Since :func:`atomic` guarantees atomicity,
-this isn't allowed any longer.
+behavior is the same, there are two differences.
-To avoid wrapping a particular view in a transaction, instead of::
-
- @transaction.autocommit
- def my_view(request):
- do_stuff()
-
-you must now use this pattern::
-
- def my_view(request):
- do_stuff()
- my_view.transactions_per_request = False
+With the previous API, it was possible to switch to autocommit or to commit
+explicitly anywhere inside a view. Since :setting:`ATOMIC_REQUESTS
+<DATABASE-ATOMIC_REQUESTS>` relies on :func:`atomic` which enforces atomicity,
+this isn't allowed any longer. However, at the toplevel, it's still possible
+to avoid wrapping an entire view in a transaction. To achieve this, decorate
+the view with :func:`non_atomic_requests` instead of :func:`autocommit`.
The transaction middleware applied not only to view functions, but also to
middleware modules that came after it. For instance, if you used the session
@@ -624,6 +627,9 @@ you should now use::
finally:
transaction.set_autocommit(False)
+Unless you're implementing a transaction management framework, you shouldn't
+ever need to do this.
+
Disabling transaction management
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -653,7 +659,7 @@ Sequences of custom SQL queries
If you're executing several :ref:`custom SQL queries <executing-custom-sql>`
in a row, each one now runs in its own transaction, instead of sharing the
same "automatic transaction". If you need to enforce atomicity, you must wrap
-the sequence of queries in :func:`commit_on_success`.
+the sequence of queries in :func:`atomic`.
To check for this problem, look for calls to ``cursor.execute()``. They're
usually followed by a call to ``transaction.commit_unless_managed()``, which
diff --git a/docs/topics/files.txt b/docs/topics/files.txt
index fb3cdd4af9..492e6a20b5 100644
--- a/docs/topics/files.txt
+++ b/docs/topics/files.txt
@@ -27,6 +27,8 @@ to deal with that file.
Consider the following model, using an :class:`~django.db.models.ImageField` to
store a photo::
+ from django.db import models
+
class Car(models.Model):
name = models.CharField(max_length=255)
price = models.DecimalField(max_digits=5, decimal_places=2)
diff --git a/docs/topics/forms/formsets.txt b/docs/topics/forms/formsets.txt
index 9d77cd5274..8745e9761d 100644
--- a/docs/topics/forms/formsets.txt
+++ b/docs/topics/forms/formsets.txt
@@ -56,6 +56,9 @@ telling the formset how many additional forms to show in addition to the
number of forms it generates from the initial data. Lets take a look at an
example::
+ >>> import datetime
+ >>> from django.forms.formsets import formset_factory
+ >>> from myapp.forms import ArticleForm
>>> ArticleFormSet = formset_factory(ArticleForm, extra=2)
>>> formset = ArticleFormSet(initial=[
... {'title': u'Django is now open source',
@@ -88,6 +91,8 @@ The ``max_num`` parameter to :func:`~django.forms.formsets.formset_factory`
gives you the ability to limit the maximum number of empty forms the formset
will display::
+ >>> from django.forms.formsets import formset_factory
+ >>> from myapp.forms import ArticleForm
>>> ArticleFormSet = formset_factory(ArticleForm, extra=2, max_num=1)
>>> formset = ArticleFormSet()
>>> for form in formset:
@@ -124,6 +129,8 @@ Validation with a formset is almost identical to a regular ``Form``. There is
an ``is_valid`` method on the formset to provide a convenient way to validate
all forms in the formset::
+ >>> from django.forms.formsets import formset_factory
+ >>> from myapp.forms import ArticleForm
>>> ArticleFormSet = formset_factory(ArticleForm)
>>> data = {
... 'form-TOTAL_FORMS': u'1',
@@ -230,6 +237,8 @@ A formset has a ``clean`` method similar to the one on a ``Form`` class. This
is where you define your own validation that works at the formset level::
>>> from django.forms.formsets import BaseFormSet
+ >>> from django.forms.formsets import formset_factory
+ >>> from myapp.forms import ArticleForm
>>> class BaseArticleFormSet(BaseFormSet):
... def clean(self):
@@ -274,8 +283,11 @@ Validating the number of forms in a formset
If ``validate_max=True`` is passed to
:func:`~django.forms.formsets.formset_factory`, validation will also check
-that the number of forms in the data set is less than or equal to ``max_num``.
+that the number of forms in the data set, minus those marked for
+deletion, is less than or equal to ``max_num``.
+ >>> from django.forms.formsets import formset_factory
+ >>> from myapp.forms import ArticleForm
>>> ArticleFormSet = formset_factory(ArticleForm, max_num=1, validate_max=True)
>>> data = {
... 'form-TOTAL_FORMS': u'2',
@@ -329,6 +341,8 @@ Default: ``False``
Lets you create a formset with the ability to order::
+ >>> from django.forms.formsets import formset_factory
+ >>> from myapp.forms import ArticleForm
>>> ArticleFormSet = formset_factory(ArticleForm, can_order=True)
>>> formset = ArticleFormSet(initial=[
... {'title': u'Article #1', 'pub_date': datetime.date(2008, 5, 10)},
@@ -385,6 +399,8 @@ Default: ``False``
Lets you create a formset with the ability to delete::
+ >>> from django.forms.formsets import formset_factory
+ >>> from myapp.forms import ArticleForm
>>> ArticleFormSet = formset_factory(ArticleForm, can_delete=True)
>>> formset = ArticleFormSet(initial=[
... {'title': u'Article #1', 'pub_date': datetime.date(2008, 5, 10)},
@@ -437,6 +453,9 @@ accomplished. The formset base class provides an ``add_fields`` method. You
can simply override this method to add your own fields or even redefine the
default fields/attributes of the order and deletion fields::
+ >>> from django.forms.formsets import BaseFormSet
+ >>> from django.forms.formsets import formset_factory
+ >>> from myapp.forms import ArticleForm
>>> class BaseArticleFormSet(BaseFormSet):
... def add_fields(self, form, index):
... super(BaseArticleFormSet, self).add_fields(form, index)
@@ -459,6 +478,10 @@ management form inside the template. Let's look at a sample view:
.. code-block:: python
+ from django.forms.formsets import formset_factory
+ from django.shortcuts import render_to_response
+ from myapp.forms import ArticleForm
+
def manage_articles(request):
ArticleFormSet = formset_factory(ArticleForm)
if request.method == 'POST':
@@ -534,6 +557,10 @@ a look at how this might be accomplished:
.. code-block:: python
+ from django.forms.formsets import formset_factory
+ from django.shortcuts import render_to_response
+ from myapp.forms import ArticleForm, BookForm
+
def manage_articles(request):
ArticleFormSet = formset_factory(ArticleForm)
BookFormSet = formset_factory(BookForm)
diff --git a/docs/topics/forms/media.txt b/docs/topics/forms/media.txt
index c0d63bb8cf..b014e97119 100644
--- a/docs/topics/forms/media.txt
+++ b/docs/topics/forms/media.txt
@@ -49,6 +49,8 @@ define the media requirements.
Here's a simple example::
+ from django import froms
+
class CalendarWidget(forms.TextInput):
class Media:
css = {
@@ -211,6 +213,7 @@ to using :setting:`MEDIA_URL`. For example, if the :setting:`MEDIA_URL` for
your site was ``'http://uploads.example.com/'`` and :setting:`STATIC_URL`
was ``None``::
+ >>> from django import forms
>>> class CalendarWidget(forms.TextInput):
... class Media:
... css = {
@@ -267,6 +270,7 @@ Combining media objects
Media objects can also be added together. When two media objects are added,
the resulting Media object contains the union of the media from both files::
+ >>> from django import forms
>>> class CalendarWidget(forms.TextInput):
... class Media:
... css = {
@@ -298,6 +302,7 @@ Regardless of whether you define a media declaration, *all* Form objects
have a media property. The default value for this property is the result
of adding the media definitions for all widgets that are part of the form::
+ >>> from django import forms
>>> class ContactForm(forms.Form):
... date = DateField(widget=CalendarWidget)
... name = CharField(max_length=40, widget=OtherWidget)
diff --git a/docs/topics/forms/modelforms.txt b/docs/topics/forms/modelforms.txt
index e58dade736..4c46c6c0c0 100644
--- a/docs/topics/forms/modelforms.txt
+++ b/docs/topics/forms/modelforms.txt
@@ -23,6 +23,7 @@ class from a Django model.
For example::
>>> from django.forms import ModelForm
+ >>> from myapp.models import Article
# Create the form class.
>>> class ArticleForm(ModelForm):
@@ -74,7 +75,7 @@ Model field Form field
``FileField`` ``FileField``
-``FilePathField`` ``CharField``
+``FilePathField`` ``FilePathField``
``FloatField`` ``FloatField``
@@ -222,6 +223,9 @@ supplied, ``save()`` will update that instance. If it's not supplied,
.. code-block:: python
+ >>> from myapp.models import Article
+ >>> from myapp.forms import ArticleForm
+
# Create a form instance from POST data.
>>> f = ArticleForm(request.POST)
@@ -316,6 +320,8 @@ these security concerns do not apply to you:
1. Set the ``fields`` attribute to the special value ``'__all__'`` to indicate
that all fields in the model should be used. For example::
+ from django.forms import ModelForm
+
class AuthorForm(ModelForm):
class Meta:
model = Author
@@ -401,6 +407,7 @@ of its default ``<input type="text">``, you can override the field's
widget::
from django.forms import ModelForm, Textarea
+ from myapp.models import Author
class AuthorForm(ModelForm):
class Meta:
@@ -421,6 +428,9 @@ you can do this by declaratively specifying fields like you would in a regular
For example, if you wanted to use ``MyDateFormField`` for the ``pub_date``
field, you could do the following::
+ from django.forms import ModelForm
+ from myapp.models import Article
+
class ArticleForm(ModelForm):
pub_date = MyDateFormField()
@@ -432,6 +442,9 @@ field, you could do the following::
If you want to override a field's default label, then specify the ``label``
parameter when declaring the form field::
+ from django.forms import ModelForm, DateField
+ from myapp.models import Article
+
class ArticleForm(ModelForm):
pub_date = DateField(label='Publication date')
@@ -474,6 +487,26 @@ parameter when declaring the form field::
See the :doc:`form field documentation </ref/forms/fields>` for more information
on fields and their arguments.
+
+Enabling localization of fields
+-------------------------------
+
+.. versionadded:: 1.6
+
+By default, the fields in a ``ModelForm`` will not localize their data. To
+enable localization for fields, you can use the ``localized_fields``
+attribute on the ``Meta`` class.
+
+ >>> from django.forms import ModelForm
+ >>> from myapp.models import Author
+ >>> class AuthorForm(ModelForm):
+ ... class Meta:
+ ... model = Author
+ ... localized_fields = ('birth_date',)
+
+If ``localized_fields`` is set to the special value ``'__all__'``, all fields
+will be localized.
+
.. _overriding-modelform-clean-method:
Overriding the clean() method
@@ -556,6 +589,7 @@ definition. This may be more convenient if you do not have many customizations
to make::
>>> from django.forms.models import modelform_factory
+ >>> from myapp.models import Book
>>> BookForm = modelform_factory(Book, fields=("author", "title"))
This can also be used to make simple modifications to existing forms, for
@@ -570,6 +604,10 @@ keyword arguments, or the corresponding attributes on the ``ModelForm`` inner
``Meta`` class. Please see the ``ModelForm`` :ref:`modelforms-selecting-fields`
documentation.
+... or enable localization for specific fields::
+
+ >>> Form = modelform_factory(Author, form=AuthorForm, localized_fields=("birth_date",))
+
.. _model-formsets:
Model formsets
@@ -582,6 +620,7 @@ of enhanced formset classes that make it easy to work with Django models. Let's
reuse the ``Author`` model from above::
>>> from django.forms.models import modelformset_factory
+ >>> from myapp.models import Author
>>> AuthorFormSet = modelformset_factory(Author)
This will create a formset that is capable of working with the data associated
@@ -620,6 +659,7 @@ Alternatively, you can create a subclass that sets ``self.queryset`` in
``__init__``::
from django.forms.models import BaseModelFormSet
+ from myapp.models import Author
class BaseAuthorFormSet(BaseModelFormSet):
def __init__(self, *args, **kwargs):
@@ -663,6 +703,20 @@ class of a ``ModelForm`` works::
>>> AuthorFormSet = modelformset_factory(
... Author, widgets={'name': Textarea(attrs={'cols': 80, 'rows': 20})
+Enabling localization for fields with ``localized_fields``
+----------------------------------------------------------
+
+.. versionadded:: 1.6
+
+Using the ``localized_fields`` parameter, you can enable localization for
+fields in the form.
+
+ >>> AuthorFormSet = modelformset_factory(
+ ... Author, localized_fields=('value',))
+
+If ``localized_fields`` is set to the special value ``'__all__'``, all fields
+will be localized.
+
Providing initial values
------------------------
@@ -751,6 +805,10 @@ Using a model formset in a view
Model formsets are very similar to formsets. Let's say we want to present a
formset to edit ``Author`` model instances::
+ from django.forms.models import modelformset_factory
+ from django.shortcuts import render_to_response
+ from myapp.models import Author
+
def manage_authors(request):
AuthorFormSet = modelformset_factory(Author)
if request.method == 'POST':
@@ -779,12 +837,15 @@ the unique constraints on your model (either ``unique``, ``unique_together`` or
on a ``model_formset`` and maintain this validation, you must call the parent
class's ``clean`` method::
+ from django.forms.models import BaseModelFormSet
+
class MyModelFormSet(BaseModelFormSet):
def clean(self):
super(MyModelFormSet, self).clean()
# example custom validation across forms in the formset:
for form in self.forms:
# your custom formset validation
+ pass
Using a custom queryset
-----------------------
@@ -792,6 +853,10 @@ Using a custom queryset
As stated earlier, you can override the default queryset used by the model
formset::
+ from django.forms.models import modelformset_factory
+ from django.shortcuts import render_to_response
+ from myapp.models import Author
+
def manage_authors(request):
AuthorFormSet = modelformset_factory(Author)
if request.method == "POST":
@@ -878,6 +943,8 @@ Inline formsets is a small abstraction layer on top of model formsets. These
simplify the case of working with related objects via a foreign key. Suppose
you have these two models::
+ from django.db import models
+
class Author(models.Model):
name = models.CharField(max_length=100)
diff --git a/docs/topics/http/file-uploads.txt b/docs/topics/http/file-uploads.txt
index 80bd5f3c44..54d748d961 100644
--- a/docs/topics/http/file-uploads.txt
+++ b/docs/topics/http/file-uploads.txt
@@ -15,6 +15,7 @@ Basic file uploads
Consider a simple form containing a :class:`~django.forms.FileField`::
+ # In forms.py...
from django import forms
class UploadFileForm(forms.Form):
@@ -39,6 +40,7 @@ something like::
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
+ from .forms import UploadFileForm
# Imaginary function to handle an uploaded file.
from somewhere import handle_uploaded_file
diff --git a/docs/topics/http/sessions.txt b/docs/topics/http/sessions.txt
index acad61eb2a..772ee122d5 100644
--- a/docs/topics/http/sessions.txt
+++ b/docs/topics/http/sessions.txt
@@ -120,11 +120,29 @@ and the :setting:`SECRET_KEY` setting.
.. note::
+ When using cookies-based sessions :mod:`django.contrib.sessions` can be
+ removed from :setting:`INSTALLED_APPS` setting because data is loaded
+ from the key itself and not from the database, so there is no need for the
+ creation and usage of ``django.contrib.sessions.models.Session`` table.
+
+.. note::
+
It's recommended to leave the :setting:`SESSION_COOKIE_HTTPONLY` setting
``True`` to prevent tampering of the stored data from JavaScript.
.. warning::
+ **If the SECRET_KEY is not kept secret, this can lead to arbitrary remote
+ code execution.**
+
+ An attacker in possession of the :setting:`SECRET_KEY` can not only
+ generate falsified session data, which your site will trust, but also
+ remotely execute arbitrary code, as the data is serialized using pickle.
+
+ If you use cookie-based sessions, pay extra care that your secret key is
+ always kept completely secret, for any system which might be remotely
+ accessible.
+
**The session data is signed but not encrypted**
When using the cookies backend the session data can be read by the client.
diff --git a/docs/topics/http/urls.txt b/docs/topics/http/urls.txt
index 9a96199dba..8a3f240307 100644
--- a/docs/topics/http/urls.txt
+++ b/docs/topics/http/urls.txt
@@ -123,6 +123,8 @@ is ``(?P<name>pattern)``, where ``name`` is the name of the group and
Here's the above example URLconf, rewritten to use named groups::
+ from django.conf.urls import patterns, url
+
urlpatterns = patterns('',
url(r'^articles/2003/$', 'news.views.special_case_2003'),
url(r'^articles/(?P<year>\d{4})/$', 'news.views.year_archive'),
@@ -192,6 +194,8 @@ A convenient trick is to specify default parameters for your views' arguments.
Here's an example URLconf and view::
# URLconf
+ from django.conf.urls import patterns, url
+
urlpatterns = patterns('',
url(r'^blog/$', 'blog.views.page'),
url(r'^blog/page(?P<num>\d+)/$', 'blog.views.page'),
@@ -370,11 +374,15 @@ An included URLconf receives any captured parameters from parent URLconfs, so
the following example is valid::
# In settings/urls/main.py
+ from django.conf.urls import include, patterns, url
+
urlpatterns = patterns('',
url(r'^(?P<username>\w+)/blog/', include('foo.urls.blog')),
)
# In foo/urls/blog.py
+ from django.conf.urls import patterns, url
+
urlpatterns = patterns('foo.views',
url(r'^$', 'blog.index'),
url(r'^archive/$', 'blog.archive'),
@@ -397,6 +405,8 @@ function.
For example::
+ from django.conf.urls import patterns, url
+
urlpatterns = patterns('blog.views',
url(r'^blog/(?P<year>\d{4})/$', 'year_archive', {'foo': 'bar'}),
)
@@ -427,11 +437,15 @@ For example, these two URLconf sets are functionally identical:
Set one::
# main.py
+ from django.conf.urls import include, patterns, url
+
urlpatterns = patterns('',
url(r'^blog/', include('inner'), {'blogid': 3}),
)
# inner.py
+ from django.conf.urls import patterns, url
+
urlpatterns = patterns('',
url(r'^archive/$', 'mysite.views.archive'),
url(r'^about/$', 'mysite.views.about'),
@@ -440,11 +454,15 @@ Set one::
Set two::
# main.py
+ from django.conf.urls import include, patterns, url
+
urlpatterns = patterns('',
url(r'^blog/', include('inner')),
)
# inner.py
+ from django.conf.urls import patterns, url
+
urlpatterns = patterns('',
url(r'^archive/$', 'mysite.views.archive', {'blogid': 3}),
url(r'^about/$', 'mysite.views.about', {'blogid': 3}),
@@ -464,6 +482,8 @@ supported -- you can pass any callable object as the view.
For example, given this URLconf in "string" notation::
+ from django.conf.urls import patterns, url
+
urlpatterns = patterns('',
url(r'^archive/$', 'mysite.views.archive'),
url(r'^about/$', 'mysite.views.about'),
@@ -473,6 +493,7 @@ For example, given this URLconf in "string" notation::
You can accomplish the same thing by passing objects rather than strings. Just
be sure to import the objects::
+ from django.conf.urls import patterns, url
from mysite.views import archive, about, contact
urlpatterns = patterns('',
@@ -485,6 +506,7 @@ The following example is functionally identical. It's just a bit more compact
because it imports the module that contains the views, rather than importing
each view individually::
+ from django.conf.urls import patterns, url
from mysite import views
urlpatterns = patterns('',
@@ -501,6 +523,7 @@ the view prefix (as explained in "The view prefix" above) will have no effect.
Note that :doc:`class based views</topics/class-based-views/index>` must be
imported::
+ from django.conf.urls import patterns, url
from mysite.views import ClassBasedView
urlpatterns = patterns('',
@@ -612,6 +635,9 @@ It's fairly common to use the same view function in multiple URL patterns in
your URLconf. For example, these two URL patterns both point to the ``archive``
view::
+ from django.conf.urls import patterns, url
+ from mysite.views import archive
+
urlpatterns = patterns('',
url(r'^archive/(\d{4})/$', archive),
url(r'^archive-summary/(\d{4})/$', archive, {'summary': True}),
@@ -630,6 +656,9 @@ matching.
Here's the above example, rewritten to use named URL patterns::
+ from django.conf.urls import patterns, url
+ from mysite.views import archive
+
urlpatterns = patterns('',
url(r'^archive/(\d{4})/$', archive, name="full-archive"),
url(r'^archive-summary/(\d{4})/$', archive, {'summary': True}, name="arch-summary"),
@@ -803,6 +832,8 @@ However, you can also ``include()`` a 3-tuple containing::
For example::
+ from django.conf.urls import include, patterns, url
+
help_patterns = patterns('',
url(r'^basic/$', 'apps.help.views.views.basic'),
url(r'^advanced/$', 'apps.help.views.views.advanced'),
diff --git a/docs/topics/http/views.txt b/docs/topics/http/views.txt
index f73ec4f5be..5c27c9c958 100644
--- a/docs/topics/http/views.txt
+++ b/docs/topics/http/views.txt
@@ -70,6 +70,8 @@ documentation. Just return an instance of one of those subclasses instead of
a normal :class:`~django.http.HttpResponse` in order to signify an error. For
example::
+ from django.http import HttpResponse, HttpResponseNotFound
+
def my_view(request):
# ...
if foo:
@@ -83,6 +85,8 @@ the :class:`~django.http.HttpResponse` documentation, you can also pass the
HTTP status code into the constructor for :class:`~django.http.HttpResponse`
to create a return class for any status code you like. For example::
+ from django.http import HttpResponse
+
def my_view(request):
# ...
@@ -110,6 +114,8 @@ standard error page for your application, along with an HTTP error code 404.
Example usage::
from django.http import Http404
+ from django.shortcuts import render_to_response
+ from polls.models import Poll
def detail(request, poll_id):
try:
@@ -225,3 +231,25 @@ same way you can for the 404 and 500 views by specifying a ``handler403`` in
your URLconf::
handler403 = 'mysite.views.my_custom_permission_denied_view'
+
+.. _http_bad_request_view:
+
+The 400 (bad request) view
+--------------------------
+
+When a :exc:`~django.core.exceptions.SuspiciousOperation` is raised in Django,
+the it may be handled by a component of Django (for example resetting the
+session data). If not specifically handled, Django will consider the current
+request a 'bad request' instead of a server error.
+
+The view ``django.views.defaults.bad_request``, is otherwise very similar to
+the ``server_error`` view, but returns with the status code 400 indicating that
+the error condition was the result of a client operation.
+
+Like the ``server_error`` view, the default ``bad_request`` should suffice for
+99% of Web applications, but if you want to override the view, you can specify
+``handler400`` in your URLconf, like so::
+
+ handler400 = 'mysite.views.my_custom_bad_request_view'
+
+``bad_request`` views are also only used when :setting:`DEBUG` is ``False``.
diff --git a/docs/topics/i18n/timezones.txt b/docs/topics/i18n/timezones.txt
index e4a043b08f..5ed60d0a94 100644
--- a/docs/topics/i18n/timezones.txt
+++ b/docs/topics/i18n/timezones.txt
@@ -54,6 +54,8 @@ FAQ <time-zones-faq>`.
Concepts
========
+.. _naive_vs_aware_datetimes:
+
Naive and aware datetime objects
--------------------------------
diff --git a/docs/topics/i18n/translation.txt b/docs/topics/i18n/translation.txt
index 5b4ffea528..ce6697908f 100644
--- a/docs/topics/i18n/translation.txt
+++ b/docs/topics/i18n/translation.txt
@@ -80,6 +80,7 @@ In this example, the text ``"Welcome to my site."`` is marked as a translation
string::
from django.utils.translation import ugettext as _
+ from django.http import HttpResponse
def my_view(request):
output = _("Welcome to my site.")
@@ -89,6 +90,7 @@ Obviously, you could code this without using the alias. This example is
identical to the previous one::
from django.utils.translation import ugettext
+ from django.http import HttpResponse
def my_view(request):
output = ugettext("Welcome to my site.")
@@ -192,6 +194,7 @@ of its value.)
For example::
from django.utils.translation import ungettext
+ from django.http import HttpResponse
def hello_world(request, count):
page = ungettext(
@@ -208,6 +211,7 @@ languages as the ``count`` variable.
Lets see a slightly more complex usage example::
from django.utils.translation import ungettext
+ from myapp.models import Report
count = Report.objects.count()
if count == 1:
@@ -283,6 +287,7 @@ For example::
or::
+ from django.db import models
from django.utils.translation import pgettext_lazy
class MyThing(models.Model):
@@ -328,6 +333,7 @@ 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.db import models
from django.utils.translation import ugettext_lazy as _
class MyThing(models.Model):
@@ -336,8 +342,6 @@ model, do the following::
You can mark names of ``ForeignKey``, ``ManyTomanyField`` or ``OneToOneField``
relationship as translatable by using their ``verbose_name`` options::
- from django.utils.translation import ugettext_lazy as _
-
class MyThing(models.Model):
kind = models.ForeignKey(ThingKind, related_name='kinds',
verbose_name=_('kind'))
@@ -355,6 +359,7 @@ It is recommended to always provide explicit
relying on the fallback English-centric and somewhat naïve determination of
verbose names Django performs by looking at the model's class name::
+ from django.db import models
from django.utils.translation import ugettext_lazy as _
class MyThing(models.Model):
@@ -370,6 +375,7 @@ Model methods ``short_description`` attribute values
For model methods, you can provide translations to Django and the admin site
with the ``short_description`` attribute::
+ from django.db import models
from django.utils.translation import ugettext_lazy as _
class MyThing(models.Model):
@@ -404,6 +410,7 @@ If you ever see output that looks like ``"hello
If you don't like the long ``ugettext_lazy`` name, you can just alias it as
``_`` (underscore), like so::
+ from django.db import models
from django.utils.translation import ugettext_lazy as _
class MyThing(models.Model):
@@ -429,6 +436,9 @@ definition. Therefore, you are authorized to pass a key name instead of an
integer as the ``number`` argument. Then ``number`` will be looked up in the
dictionary under that key during string interpolation. Here's example::
+ from django import forms
+ from django.utils.translation import ugettext_lazy
+
class MyForm(forms.Form):
error_message = ungettext_lazy("You only provided %(num)d argument",
"You only provided %(num)d arguments", 'num')
@@ -461,6 +471,7 @@ that concatenates its contents *and* converts them to strings only when the
result is included in a string. For example::
from django.utils.translation import string_concat
+ from django.utils.translation import ugettext_lazy
...
name = ugettext_lazy('John Lennon')
instrument = ugettext_lazy('guitar')
@@ -687,7 +698,7 @@ or with the ``{#`` ... ``#}`` :ref:`one-line comment constructs <template-commen
.. code-block:: html+django
- {# Translators: Label of a button that triggers search{% endcomment #}
+ {# Translators: Label of a button that triggers search #}
<button type="submit">{% trans "Go" %}</button>
{# Translators: This is a text of the base template #}
@@ -722,6 +733,31 @@ or with the ``{#`` ... ``#}`` :ref:`one-line comment constructs <template-commen
msgid "Ambiguous translatable block of text"
msgstr ""
+.. templatetag:: language
+
+Switching language in templates
+-------------------------------
+
+If you want to select a language within a template, you can use the
+``language`` template tag:
+
+.. code-block:: html+django
+
+ {% load i18n %}
+
+ {% get_current_language as LANGUAGE_CODE %}
+ <!-- Current language: {{ LANGUAGE_CODE }} -->
+ <p>{% trans "Welcome to our page" %}</p>
+
+ {% language 'en' %}
+ {% get_current_language as LANGUAGE_CODE %}
+ <!-- Current language: {{ LANGUAGE_CODE }} -->
+ <p>{% trans "Welcome to our page" %}</p>
+ {% endlanguage %}
+
+While the first occurrence of "Welcome to our page" uses the current language,
+the second will always be in English.
+
.. _template-translation-vars:
Other tags
@@ -1126,13 +1162,11 @@ active language. Example::
.. _reversing_in_templates:
-.. templatetag:: language
-
Reversing in templates
----------------------
If localized URLs get reversed in templates they always use the current
-language. To link to a URL in another language use the ``language``
+language. To link to a URL in another language use the :ttag:`language`
template tag. It enables the given language in the enclosed template section:
.. code-block:: html+django
@@ -1640,6 +1674,8 @@ preference available as ``request.LANGUAGE_CODE`` for each
:class:`~django.http.HttpRequest`. Feel free to read this value in your view
code. Here's a simple example::
+ from django.http import HttpResponse
+
def hello_world(request, count):
if request.LANGUAGE_CODE == 'de-at':
return HttpResponse("You prefer to read Austrian German.")
diff --git a/docs/topics/logging.txt b/docs/topics/logging.txt
index a31dc01cc5..a88201ad47 100644
--- a/docs/topics/logging.txt
+++ b/docs/topics/logging.txt
@@ -394,7 +394,7 @@ requirements of logging in Web server environment.
Loggers
-------
-Django provides three built-in loggers.
+Django provides four built-in loggers.
``django``
~~~~~~~~~~
@@ -434,6 +434,35 @@ For performance reasons, SQL logging is only enabled when
``settings.DEBUG`` is set to ``True``, regardless of the logging
level or handlers that are installed.
+``django.security.*``
+~~~~~~~~~~~~~~~~~~~~~~
+
+The security loggers will receive messages on any occurrence of
+:exc:`~django.core.exceptions.SuspiciousOperation`. There is a sub-logger for
+each sub-type of SuspiciousOperation. The level of the log event depends on
+where the exception is handled. Most occurrences are logged as a warning, while
+any ``SuspiciousOperation`` that reaches the WSGI handler will be logged as an
+error. For example, when an HTTP ``Host`` header is included in a request from
+a client that does not match :setting:`ALLOWED_HOSTS`, Django will return a 400
+response, and an error message will be logged to the
+``django.security.DisallowedHost`` logger.
+
+Only the parent ``django.security`` logger is configured by default, and all
+child loggers will propagate to the parent logger. The ``django.security``
+logger is configured the same as the ``django.request`` logger, and any error
+events will be mailed to admins. Requests resulting in a 400 response due to
+a ``SuspiciousOperation`` will not be logged to the ``django.request`` logger,
+but only to the ``django.security`` logger.
+
+To silence a particular type of SuspiciousOperation, you can override that
+specific logger following this example::
+
+ 'loggers': {
+ 'django.security.DisallowedHost': {
+ 'handlers': ['null'],
+ 'propagate': False,
+ },
+
Handlers
--------
diff --git a/docs/topics/python3.txt b/docs/topics/python3.txt
index 22e609c75c..9a0438e9e5 100644
--- a/docs/topics/python3.txt
+++ b/docs/topics/python3.txt
@@ -201,8 +201,8 @@ According to :pep:`3333`:
Specifically, :attr:`HttpResponse.content <django.http.HttpResponse.content>`
contains ``bytes``, which may become an issue if you compare it with a
``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
+:meth:`~django.test.SimpleTestCase.assertContains` and
+:meth:`~django.test.SimpleTestCase.assertNotContains`. These methods accept a
response and a unicode string as arguments.
Coding guidelines
diff --git a/docs/topics/testing/advanced.txt b/docs/topics/testing/advanced.txt
index cefb770469..b7f49d2b97 100644
--- a/docs/topics/testing/advanced.txt
+++ b/docs/topics/testing/advanced.txt
@@ -113,7 +113,8 @@ two databases.
Controlling creation order for test databases
---------------------------------------------
-By default, Django will always create the ``default`` database first.
+By default, Django will assume all databases depend on the ``default``
+database and therefore always create the ``default`` database first.
However, no guarantees are made on the creation order of any other
databases in your test setup.
@@ -129,6 +130,7 @@ can specify the dependencies that exist using the
},
'diamonds': {
# ... db settings
+ 'TEST_DEPENDENCIES': []
},
'clubs': {
# ... db settings
diff --git a/docs/topics/testing/overview.txt b/docs/topics/testing/overview.txt
index fc2b393898..2b1db5e501 100644
--- a/docs/topics/testing/overview.txt
+++ b/docs/topics/testing/overview.txt
@@ -21,17 +21,16 @@ module defines tests using a class-based approach.
.. admonition:: unittest2
- Python 2.7 introduced some major changes to the unittest library,
+ Python 2.7 introduced some major changes to the ``unittest`` library,
adding some extremely useful features. To ensure that every Django
project can benefit from these new features, Django ships with a
- copy of unittest2_, a copy of the Python 2.7 unittest library,
- backported for Python 2.6 compatibility.
+ copy of unittest2_, a copy of Python 2.7's ``unittest``, backported for
+ Python 2.6 compatibility.
To access this library, Django provides the
``django.utils.unittest`` module alias. If you are using Python
- 2.7, or you have installed unittest2 locally, Django will map the
- alias to the installed version of the unittest library. Otherwise,
- Django will use its own bundled version of unittest2.
+ 2.7, or you have installed ``unittest2`` locally, Django will map the alias
+ to it. Otherwise, Django will use its own bundled version of ``unittest2``.
To use this alias, simply use::
@@ -41,8 +40,8 @@ module defines tests using a class-based approach.
import unittest
- If you want to continue to use the base unittest library, you can --
- you just won't get any of the nice new unittest2 features.
+ If you want to continue to use the legacy ``unittest`` library, you can --
+ you just won't get any of the nice new ``unittest2`` features.
.. _unittest2: http://pypi.python.org/pypi/unittest2
@@ -697,7 +696,7 @@ Use the ``django.test.client.Client`` class to make requests.
After you call this method, the test client will have all the cookies
and session data cleared to defaults. Subsequent requests will appear
- to come from an AnonymousUser.
+ to come from an :class:`~django.contrib.auth.models.AnonymousUser`.
Testing responses
~~~~~~~~~~~~~~~~~
@@ -858,24 +857,46 @@ SimpleTestCase
.. class:: SimpleTestCase()
-A very thin subclass of :class:`unittest.TestCase`, it extends it with some
-basic functionality like:
+A thin subclass of :class:`unittest.TestCase`, it extends it with some basic
+functionality like:
* Saving and restoring the Python warning machinery state.
-* Checking that a callable :meth:`raises a certain exception <SimpleTestCase.assertRaisesMessage>`.
-* :meth:`Testing form field rendering <SimpleTestCase.assertFieldOutput>`.
-* Testing server :ref:`HTML responses for the presence/lack of a given fragment <assertions>`.
-* The ability to run tests with :ref:`modified settings <overriding-settings>`
+* Some useful assertions like:
+
+ * Checking that a callable :meth:`raises a certain exception
+ <SimpleTestCase.assertRaisesMessage>`.
+ * Testing form field :meth:`rendering and error treatment
+ <SimpleTestCase.assertFieldOutput>`.
+ * Testing :meth:`HTML responses for the presence/lack of a given fragment
+ <SimpleTestCase.assertContains>`.
+ * Verifying that a template :meth:`has/hasn't been used to generate a given
+ response content <SimpleTestCase.assertTemplateUsed>`.
+ * Verifying a HTTP :meth:`redirect <SimpleTestCase.assertRedirects>` is
+ performed by the app.
+ * Robustly testing two :meth:`HTML fragments <SimpleTestCase.assertHTMLEqual>`
+ for equality/inequality or :meth:`containment <SimpleTestCase.assertInHTML>`.
+ * Robustly testing two :meth:`XML fragments <SimpleTestCase.assertXMLEqual>`
+ for equality/inequality.
+ * Robustly testing two :meth:`JSON fragments <SimpleTestCase.assertJSONEqual>`
+ for equality.
+
+* The ability to run tests with :ref:`modified settings <overriding-settings>`.
+* Using the :attr:`~SimpleTestCase.client` :class:`~django.test.client.Client`.
+* Custom test-time :attr:`URL maps <SimpleTestCase.urls>`.
+
+.. versionchanged:: 1.6
+
+ The latter two features were moved from ``TransactionTestCase`` to
+ ``SimpleTestCase`` in Django 1.6.
If you need any of the other more complex and heavyweight Django-specific
features like:
-* Using the :attr:`~TestCase.client` :class:`~django.test.client.Client`.
* Testing or using the ORM.
-* Database :attr:`~TestCase.fixtures`.
-* Custom test-time :attr:`URL maps <TestCase.urls>`.
+* Database :attr:`~TransactionTestCase.fixtures`.
* Test :ref:`skipping based on database backend features <skipping-tests>`.
-* The remaining specialized :ref:`assert* <assertions>` methods.
+* The remaining specialized :meth:`assert*
+ <TransactionTestCase.assertQuerysetEqual>` methods.
then you should use :class:`~django.test.TransactionTestCase` or
:class:`~django.test.TestCase` instead.
@@ -904,14 +925,23 @@ to test the effects of commit and rollback:
* 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.
+ back at the end of the test. Both explicit commits like
+ ``transaction.commit()`` and implicit ones that may be caused by
+ ``Model.save()`` are replaced with a ``nop`` operation. This guarantees 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.
+.. warning::
+
+ While ``commit`` and ``rollback`` operations still *appear* to work when
+ used in ``TestCase``, no actual commit or rollback will be performed by the
+ database. This can cause your tests to pass or fail unexpectedly. Always
+ use ``TransactionalTestCase`` when testing transactional behavior.
+
.. note::
.. versionchanged:: 1.5
@@ -923,7 +953,7 @@ to test the effects of commit and rollback:
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
+ Tests should not depend on this behavior, but for legacy tests that do, the
:attr:`~TransactionTestCase.reset_sequences` attribute can be used until
the test has been properly updated.
@@ -1137,9 +1167,9 @@ Test cases features
Default test client
~~~~~~~~~~~~~~~~~~~
-.. attribute:: TestCase.client
+.. attribute:: SimpleTestCase.client
-Every test case in a ``django.test.TestCase`` instance has access to an
+Every test case in a ``django.test.*TestCase`` instance has access to an
instance of a Django test client. This client can be accessed as
``self.client``. This client is recreated for each test, so you don't have to
worry about state (such as cookies) carrying over from one test to another.
@@ -1176,10 +1206,10 @@ This means, instead of instantiating a ``Client`` in each test::
Customizing the test client
~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.. attribute:: TestCase.client_class
+.. attribute:: SimpleTestCase.client_class
If you want to use a different ``Client`` class (for example, a subclass
-with customized behavior), use the :attr:`~TestCase.client_class` class
+with customized behavior), use the :attr:`~SimpleTestCase.client_class` class
attribute::
from django.test import TestCase
@@ -1200,11 +1230,12 @@ attribute::
Fixture loading
~~~~~~~~~~~~~~~
-.. attribute:: TestCase.fixtures
+.. attribute:: TransactionTestCase.fixtures
A test case for a database-backed Web site isn't much use if there isn't any
data in the database. To make it easy to put test data into the database,
-Django's custom ``TestCase`` class provides a way of loading **fixtures**.
+Django's custom ``TransactionTestCase`` class provides a way of loading
+**fixtures**.
A fixture is a collection of data that Django knows how to import into a
database. For example, if your site has user accounts, you might set up a
@@ -1273,7 +1304,7 @@ or by the order of test execution.
URLconf configuration
~~~~~~~~~~~~~~~~~~~~~
-.. attribute:: TestCase.urls
+.. attribute:: SimpleTestCase.urls
If your application provides views, you may want to include tests that use the
test client to exercise those views. However, an end user is free to deploy the
@@ -1282,9 +1313,9 @@ tests can't rely upon the fact that your views will be available at a
particular URL.
In order to provide a reliable URL space for your test,
-``django.test.TestCase`` provides the ability to customize the URLconf
+``django.test.*TestCase`` classes provide the ability to customize the URLconf
configuration for the duration of the execution of a test suite. If your
-``TestCase`` instance defines an ``urls`` attribute, the ``TestCase`` will use
+``*TestCase`` instance defines an ``urls`` attribute, the ``*TestCase`` will use
the value of that attribute as the :setting:`ROOT_URLCONF` for the duration
of that test.
@@ -1307,7 +1338,7 @@ URLconf for the duration of the test case.
Multi-database support
~~~~~~~~~~~~~~~~~~~~~~
-.. attribute:: TestCase.multi_db
+.. attribute:: TransactionTestCase.multi_db
Django sets up a test database corresponding to every database that is
defined in the :setting:`DATABASES` definition in your settings
@@ -1340,12 +1371,12 @@ This test case will flush *all* the test databases before running
Overriding settings
~~~~~~~~~~~~~~~~~~~
-.. method:: TestCase.settings
+.. method:: SimpleTestCase.settings
For testing purposes it's often useful to change a setting temporarily and
revert to the original value after running the testing code. For this use case
Django provides a standard Python context manager (see :pep:`343`)
-:meth:`~django.test.TestCase.settings`, which can be used like this::
+:meth:`~django.test.SimpleTestCase.settings`, which can be used like this::
from django.test import TestCase
@@ -1435,8 +1466,8 @@ MEDIA_ROOT, DEFAULT_FILE_STORAGE Default file storage
Emptying the test outbox
~~~~~~~~~~~~~~~~~~~~~~~~
-If you use Django's custom ``TestCase`` class, the test runner will clear the
-contents of the test email outbox at the start of each test case.
+If you use any of Django's custom ``TestCase`` classes, the test runner will
+clear thecontents of the test email outbox at the start of each test case.
For more detail on email services during tests, see `Email services`_ below.
@@ -1486,8 +1517,43 @@ your test suite.
self.assertFieldOutput(EmailField, {'a@a.com': 'a@a.com'}, {'aaa': [u'Enter a valid email address.']})
+.. method:: SimpleTestCase.assertFormError(response, form, field, errors, msg_prefix='')
+
+ Asserts that a field on a form raises the provided list of errors when
+ rendered on the form.
+
+ ``form`` is the name the ``Form`` instance was given in the template
+ context.
+
+ ``field`` is the name of the field on the form to check. If ``field``
+ has a value of ``None``, non-field errors (errors you can access via
+ ``form.non_field_errors()``) will be checked.
+
+ ``errors`` is an error string, or a list of error strings, that are
+ expected as a result of form validation.
+
+.. method:: SimpleTestCase.assertFormsetError(response, formset, form_index, field, errors, msg_prefix='')
+
+ .. versionadded:: 1.6
+
+ Asserts that the ``formset`` raises the provided list of errors when
+ rendered.
+
+ ``formset`` is the name the ``Formset`` instance was given in the template
+ context.
+
+ ``form_index`` is the number of the form within the ``Formset``. If
+ ``form_index`` has a value of ``None``, non-form errors (errors you can
+ access via ``formset.non_form_errors()``) will be checked.
+
+ ``field`` is the name of the field on the form to check. If ``field``
+ has a value of ``None``, non-field errors (errors you can access via
+ ``form.non_field_errors()``) will be checked.
+
+ ``errors`` is an error string, or a list of error strings, that are
+ expected as a result of form validation.
-.. method:: TestCase.assertContains(response, text, count=None, status_code=200, msg_prefix='', html=False)
+.. method:: SimpleTestCase.assertContains(response, text, count=None, status_code=200, msg_prefix='', html=False)
Asserts that a ``Response`` instance produced the given ``status_code`` and
that ``text`` appears in the content of the response. If ``count`` is
@@ -1499,7 +1565,7 @@ your test suite.
attribute ordering is not significant. See
:meth:`~SimpleTestCase.assertHTMLEqual` for more details.
-.. method:: TestCase.assertNotContains(response, text, status_code=200, msg_prefix='', html=False)
+.. method:: SimpleTestCase.assertNotContains(response, text, status_code=200, msg_prefix='', html=False)
Asserts that a ``Response`` instance produced the given ``status_code`` and
that ``text`` does not appears in the content of the response.
@@ -1510,22 +1576,7 @@ your test suite.
attribute ordering is not significant. See
:meth:`~SimpleTestCase.assertHTMLEqual` for more details.
-.. method:: TestCase.assertFormError(response, form, field, errors, msg_prefix='')
-
- Asserts that a field on a form raises the provided list of errors when
- rendered on the form.
-
- ``form`` is the name the ``Form`` instance was given in the template
- context.
-
- ``field`` is the name of the field on the form to check. If ``field``
- has a value of ``None``, non-field errors (errors you can access via
- ``form.non_field_errors()``) will be checked.
-
- ``errors`` is an error string, or a list of error strings, that are
- expected as a result of form validation.
-
-.. method:: TestCase.assertTemplateUsed(response, template_name, msg_prefix='')
+.. method:: SimpleTestCase.assertTemplateUsed(response, template_name, msg_prefix='')
Asserts that the template with the given name was used in rendering the
response.
@@ -1539,15 +1590,15 @@ your test suite.
with self.assertTemplateUsed(template_name='index.html'):
render_to_string('index.html')
-.. method:: TestCase.assertTemplateNotUsed(response, template_name, msg_prefix='')
+.. method:: SimpleTestCase.assertTemplateNotUsed(response, template_name, msg_prefix='')
Asserts that the template with the given name was *not* used in rendering
the response.
You can use this as a context manager in the same way as
- :meth:`~TestCase.assertTemplateUsed`.
+ :meth:`~SimpleTestCase.assertTemplateUsed`.
-.. method:: TestCase.assertRedirects(response, expected_url, status_code=302, target_status_code=200, msg_prefix='')
+.. method:: SimpleTestCase.assertRedirects(response, expected_url, status_code=302, target_status_code=200, msg_prefix='')
Asserts that the response return a ``status_code`` redirect status, it
redirected to ``expected_url`` (including any GET data), and the final
@@ -1557,44 +1608,6 @@ your test suite.
``target_status_code`` will be the url and status code for the final
point of the redirect chain.
-.. method:: TestCase.assertQuerysetEqual(qs, values, transform=repr, ordered=True)
-
- Asserts that a queryset ``qs`` returns a particular list of values ``values``.
-
- The comparison of the contents of ``qs`` and ``values`` is performed using
- the function ``transform``; by default, this means that the ``repr()`` of
- each value is compared. Any other callable can be used if ``repr()`` doesn't
- provide a unique or helpful comparison.
-
- By default, the comparison is also ordering dependent. If ``qs`` doesn't
- provide an implicit ordering, you can set the ``ordered`` parameter to
- ``False``, which turns the comparison into a Python set comparison.
-
- .. versionchanged:: 1.6
-
- The method now checks for undefined order and raises ``ValueError``
- if undefined order is spotted. The ordering is seen as undefined if
- the given ``qs`` isn't ordered and the comparison is against more
- than one ordered values.
-
-.. method:: TestCase.assertNumQueries(num, func, *args, **kwargs)
-
- Asserts that when ``func`` is called with ``*args`` and ``**kwargs`` that
- ``num`` database queries are executed.
-
- If a ``"using"`` key is present in ``kwargs`` it is used as the database
- alias for which to check the number of queries. If you wish to call a
- function with a ``using`` parameter you can do it by wrapping the call with
- a ``lambda`` to add an extra parameter::
-
- self.assertNumQueries(7, lambda: my_function(using=7))
-
- You can also use this as a context manager::
-
- with self.assertNumQueries(2):
- Person.objects.create(name="Aaron")
- Person.objects.create(name="Daniel")
-
.. method:: SimpleTestCase.assertHTMLEqual(html1, html2, msg=None)
Asserts that the strings ``html1`` and ``html2`` are equal. The comparison
@@ -1624,6 +1637,8 @@ your test suite.
``html1`` and ``html2`` must be valid HTML. An ``AssertionError`` will be
raised if one of them cannot be parsed.
+ Output in case of error can be customized with the ``msg`` argument.
+
.. method:: SimpleTestCase.assertHTMLNotEqual(html1, html2, msg=None)
Asserts that the strings ``html1`` and ``html2`` are *not* equal. The
@@ -1633,6 +1648,8 @@ your test suite.
``html1`` and ``html2`` must be valid HTML. An ``AssertionError`` will be
raised if one of them cannot be parsed.
+ Output in case of error can be customized with the ``msg`` argument.
+
.. method:: SimpleTestCase.assertXMLEqual(xml1, xml2, msg=None)
.. versionadded:: 1.5
@@ -1644,6 +1661,8 @@ your test suite.
syntax differences. When unvalid XML is passed in any parameter, an
``AssertionError`` is always raised, even if both string are identical.
+ Output in case of error can be customized with the ``msg`` argument.
+
.. method:: SimpleTestCase.assertXMLNotEqual(xml1, xml2, msg=None)
.. versionadded:: 1.5
@@ -1652,6 +1671,68 @@ your test suite.
comparison is based on XML semantics. See
:meth:`~SimpleTestCase.assertXMLEqual` for details.
+ Output in case of error can be customized with the ``msg`` argument.
+
+.. method:: SimpleTestCase.assertInHTML(needle, haystack, count=None, msg_prefix='')
+
+ .. versionadded:: 1.5
+
+ Asserts that the HTML fragment ``needle`` is contained in the ``haystack`` one.
+
+ If the ``count`` integer argument is specified, then additionally the number
+ of ``needle`` occurrences will be strictly verified.
+
+ Whitespace in most cases is ignored, and attribute ordering is not
+ significant. The passed-in arguments must be valid HTML.
+
+.. method:: SimpleTestCase.assertJSONEqual(raw, expected_data, msg=None)
+
+ .. versionadded:: 1.5
+
+ Asserts that the JSON fragments ``raw`` and ``expected_data`` are equal.
+ Usual JSON non-significant whitespace rules apply as the heavyweight is
+ delegated to the :mod:`json` library.
+
+ Output in case of error can be customized with the ``msg`` argument.
+
+.. method:: TransactionTestCase.assertQuerysetEqual(qs, values, transform=repr, ordered=True)
+
+ Asserts that a queryset ``qs`` returns a particular list of values ``values``.
+
+ The comparison of the contents of ``qs`` and ``values`` is performed using
+ the function ``transform``; by default, this means that the ``repr()`` of
+ each value is compared. Any other callable can be used if ``repr()`` doesn't
+ provide a unique or helpful comparison.
+
+ By default, the comparison is also ordering dependent. If ``qs`` doesn't
+ provide an implicit ordering, you can set the ``ordered`` parameter to
+ ``False``, which turns the comparison into a Python set comparison.
+
+ .. versionchanged:: 1.6
+
+ The method now checks for undefined order and raises ``ValueError``
+ if undefined order is spotted. The ordering is seen as undefined if
+ the given ``qs`` isn't ordered and the comparison is against more
+ than one ordered values.
+
+.. method:: TransactionTestCase.assertNumQueries(num, func, *args, **kwargs)
+
+ Asserts that when ``func`` is called with ``*args`` and ``**kwargs`` that
+ ``num`` database queries are executed.
+
+ If a ``"using"`` key is present in ``kwargs`` it is used as the database
+ alias for which to check the number of queries. If you wish to call a
+ function with a ``using`` parameter you can do it by wrapping the call with
+ a ``lambda`` to add an extra parameter::
+
+ self.assertNumQueries(7, lambda: my_function(using=7))
+
+ You can also use this as a context manager::
+
+ with self.assertNumQueries(2):
+ Person.objects.create(name="Aaron")
+ Person.objects.create(name="Daniel")
+
.. _topics-testing-email:
Email services
@@ -1701,7 +1782,7 @@ and contents::
self.assertEqual(mail.outbox[0].subject, 'Subject here')
As noted :ref:`previously <emptying-test-outbox>`, the test outbox is emptied
-at the start of every test in a Django ``TestCase``. To empty the outbox
+at the start of every test in a Django ``*TestCase``. To empty the outbox
manually, assign the empty list to ``mail.outbox``::
from django.core import mail