summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorAndrew Godwin <andrew@aeracode.org>2013-04-18 17:16:39 +0100
committerAndrew Godwin <andrew@aeracode.org>2013-04-18 17:16:39 +0100
commit7f3678dc4cd7146c49bac3fb8f5211f647636aa3 (patch)
treefdd2a60ccd1a9a3162d89f970db44502e35a3b3f /docs
parentb62e82365ad56ca930f7abb1d1dbdf9ce5a7c7c3 (diff)
parent93c1576f17f6c5ee73f94f8de007f3c33010dc81 (diff)
Merge branch 'master' into schema-alteration
Conflicts: django/db/backends/__init__.py django/db/backends/mysql/base.py django/db/backends/oracle/base.py django/db/backends/oracle/creation.py django/db/backends/postgresql_psycopg2/base.py django/db/backends/sqlite3/base.py django/db/models/fields/related.py
Diffstat (limited to 'docs')
-rw-r--r--docs/Makefile2
-rw-r--r--docs/_ext/djangodocs.py16
-rw-r--r--docs/_ext/literals_to_xrefs.py2
-rw-r--r--docs/_theme/djangodocs/static/djangodocs.css6
-rw-r--r--docs/conf.py5
-rw-r--r--docs/faq/admin.txt2
-rw-r--r--docs/faq/general.txt28
-rw-r--r--docs/faq/install.txt6
-rw-r--r--docs/faq/usage.txt9
-rw-r--r--docs/howto/auth-remote-user.txt2
-rw-r--r--docs/howto/custom-management-commands.txt117
-rw-r--r--docs/howto/custom-model-fields.txt45
-rw-r--r--docs/howto/custom-template-tags.txt50
-rw-r--r--docs/howto/deployment/checklist.txt205
-rw-r--r--docs/howto/deployment/index.txt1
-rw-r--r--docs/howto/deployment/wsgi/apache-auth.txt8
-rw-r--r--docs/howto/deployment/wsgi/gunicorn.txt6
-rw-r--r--docs/howto/deployment/wsgi/index.txt99
-rw-r--r--docs/howto/deployment/wsgi/modwsgi.txt4
-rw-r--r--docs/howto/deployment/wsgi/uwsgi.txt8
-rw-r--r--docs/howto/error-reporting.txt67
-rw-r--r--docs/howto/index.txt3
-rw-r--r--docs/howto/initial-data.txt4
-rw-r--r--docs/howto/legacy-databases.txt31
-rw-r--r--docs/howto/outputting-csv.txt6
-rw-r--r--docs/howto/outputting-pdf.txt4
-rw-r--r--docs/howto/static-files.txt508
-rw-r--r--docs/howto/static-files/deployment.txt159
-rw-r--r--docs/howto/static-files/index.txt119
-rw-r--r--docs/index.txt23
-rw-r--r--docs/internals/_images/djangotickets.pngbin52003 -> 0 bytes
-rw-r--r--docs/internals/_images/triage_process.graffle2141
-rw-r--r--docs/internals/_images/triage_process.pdfbin0 -> 59051 bytes
-rw-r--r--docs/internals/_images/triage_process.svg3
-rw-r--r--docs/internals/committers.txt34
-rw-r--r--docs/internals/contributing/committing-code.txt2
-rw-r--r--docs/internals/contributing/new-contributors.txt9
-rw-r--r--docs/internals/contributing/triaging-tickets.txt150
-rw-r--r--docs/internals/contributing/writing-code/coding-style.txt23
-rw-r--r--docs/internals/contributing/writing-code/submitting-patches.txt6
-rw-r--r--docs/internals/contributing/writing-code/unit-tests.txt34
-rw-r--r--docs/internals/contributing/writing-code/working-with-git.txt4
-rw-r--r--docs/internals/deprecation.txt149
-rw-r--r--docs/internals/git.txt2
-rw-r--r--docs/internals/howto-release-django.txt320
-rw-r--r--docs/internals/index.txt1
-rw-r--r--docs/internals/release-process.txt156
-rw-r--r--docs/intro/_images/admin02.pngbin64260 -> 32850 bytes
-rw-r--r--docs/intro/_images/admin02t.pngbin24726 -> 16401 bytes
-rw-r--r--docs/intro/_images/admin03.pngbin75434 -> 40583 bytes
-rw-r--r--docs/intro/_images/admin03t.pngbin28131 -> 19722 bytes
-rw-r--r--docs/intro/contributing.txt22
-rw-r--r--docs/intro/index.txt7
-rw-r--r--docs/intro/install.txt9
-rw-r--r--docs/intro/overview.txt12
-rw-r--r--docs/intro/reusable-apps.txt86
-rw-r--r--docs/intro/tutorial01.txt163
-rw-r--r--docs/intro/tutorial02.txt134
-rw-r--r--docs/intro/tutorial03.txt102
-rw-r--r--docs/intro/tutorial04.txt30
-rw-r--r--docs/intro/tutorial05.txt18
-rw-r--r--docs/intro/tutorial06.txt125
-rw-r--r--docs/intro/whatsnext.txt10
-rw-r--r--docs/make.bat2
-rw-r--r--docs/misc/api-stability.txt157
-rw-r--r--docs/ref/authbackends.txt33
-rw-r--r--docs/ref/class-based-views/base.txt43
-rw-r--r--docs/ref/class-based-views/flattened-index.txt116
-rw-r--r--docs/ref/class-based-views/generic-date-based.txt19
-rw-r--r--docs/ref/class-based-views/generic-display.txt40
-rw-r--r--docs/ref/class-based-views/generic-editing.txt42
-rw-r--r--docs/ref/class-based-views/mixins-date-based.txt25
-rw-r--r--docs/ref/class-based-views/mixins-editing.txt62
-rw-r--r--docs/ref/class-based-views/mixins-multiple-object.txt22
-rw-r--r--docs/ref/class-based-views/mixins-simple.txt21
-rw-r--r--docs/ref/class-based-views/mixins-single-object.txt45
-rw-r--r--docs/ref/clickjacking.txt43
-rw-r--r--docs/ref/contrib/admin/actions.txt6
-rw-r--r--docs/ref/contrib/admin/admindocs.txt2
-rw-r--r--docs/ref/contrib/admin/index.txt248
-rw-r--r--docs/ref/contrib/auth.txt433
-rw-r--r--docs/ref/contrib/comments/custom.txt38
-rw-r--r--docs/ref/contrib/comments/example.txt18
-rw-r--r--docs/ref/contrib/comments/forms.txt14
-rw-r--r--docs/ref/contrib/comments/index.txt23
-rw-r--r--docs/ref/contrib/comments/models.txt17
-rw-r--r--docs/ref/contrib/comments/moderation.txt27
-rw-r--r--docs/ref/contrib/comments/settings.txt33
-rw-r--r--docs/ref/contrib/comments/signals.txt16
-rw-r--r--docs/ref/contrib/contenttypes.txt60
-rw-r--r--docs/ref/contrib/csrf.txt71
-rw-r--r--docs/ref/contrib/databrowse.txt89
-rw-r--r--docs/ref/contrib/flatpages.txt18
-rw-r--r--docs/ref/contrib/formtools/form-preview.txt16
-rw-r--r--docs/ref/contrib/formtools/form-wizard.txt80
-rw-r--r--docs/ref/contrib/formtools/index.txt2
-rw-r--r--docs/ref/contrib/gis/commands.txt3
-rw-r--r--docs/ref/contrib/gis/db-api.txt17
-rw-r--r--docs/ref/contrib/gis/feeds.txt10
-rw-r--r--docs/ref/contrib/gis/gdal.txt70
-rw-r--r--docs/ref/contrib/gis/geoip.txt51
-rw-r--r--docs/ref/contrib/gis/geoquerysets.txt7
-rw-r--r--docs/ref/contrib/gis/geos.txt14
-rwxr-xr-xdocs/ref/contrib/gis/install/create_template_postgis-debian.sh7
-rw-r--r--docs/ref/contrib/gis/install/geolibs.txt16
-rw-r--r--docs/ref/contrib/gis/install/index.txt24
-rw-r--r--docs/ref/contrib/gis/install/postgis.txt16
-rw-r--r--docs/ref/contrib/gis/model-api.txt5
-rw-r--r--docs/ref/contrib/gis/testing.txt2
-rw-r--r--docs/ref/contrib/gis/tutorial.txt17
-rw-r--r--docs/ref/contrib/humanize.txt4
-rw-r--r--docs/ref/contrib/index.txt21
-rw-r--r--docs/ref/contrib/markup.txt77
-rw-r--r--docs/ref/contrib/messages.txt143
-rw-r--r--docs/ref/contrib/redirects.txt9
-rw-r--r--docs/ref/contrib/sitemaps.txt51
-rw-r--r--docs/ref/contrib/sites.txt25
-rw-r--r--docs/ref/contrib/staticfiles.txt177
-rw-r--r--docs/ref/contrib/syndication.txt72
-rw-r--r--docs/ref/databases.txt187
-rw-r--r--docs/ref/django-admin.txt160
-rw-r--r--docs/ref/exceptions.txt33
-rw-r--r--docs/ref/files/file.txt4
-rw-r--r--docs/ref/files/storage.txt4
-rw-r--r--docs/ref/forms/api.txt119
-rw-r--r--docs/ref/forms/fields.txt85
-rw-r--r--docs/ref/forms/formsets.txt16
-rw-r--r--docs/ref/forms/index.txt2
-rw-r--r--docs/ref/forms/models.txt60
-rw-r--r--docs/ref/forms/validation.txt20
-rw-r--r--docs/ref/forms/widgets.txt87
-rw-r--r--docs/ref/index.txt2
-rw-r--r--docs/ref/middleware.txt23
-rw-r--r--docs/ref/models/fields.txt168
-rw-r--r--docs/ref/models/instances.txt17
-rw-r--r--docs/ref/models/options.txt14
-rw-r--r--docs/ref/models/querysets.txt259
-rw-r--r--docs/ref/models/relations.txt15
-rw-r--r--docs/ref/request-response.txt45
-rw-r--r--docs/ref/settings.txt1334
-rw-r--r--docs/ref/signals.txt78
-rw-r--r--docs/ref/template-response.txt62
-rw-r--r--docs/ref/templates/api.txt35
-rw-r--r--docs/ref/templates/builtins.txt106
-rw-r--r--docs/ref/unicode.txt8
-rw-r--r--docs/ref/urlresolvers.txt2
-rw-r--r--docs/ref/urls.txt33
-rw-r--r--docs/ref/utils.txt72
-rw-r--r--docs/ref/validators.txt6
-rw-r--r--docs/ref/views.txt48
-rw-r--r--docs/releases/0.96.txt7
-rw-r--r--docs/releases/1.0-porting-guide.txt27
-rw-r--r--docs/releases/1.1-alpha-1.txt12
-rw-r--r--docs/releases/1.1-beta-1.txt4
-rw-r--r--docs/releases/1.1.txt35
-rw-r--r--docs/releases/1.2-alpha-1.txt22
-rw-r--r--docs/releases/1.2-beta-1.txt6
-rw-r--r--docs/releases/1.2.4.txt2
-rw-r--r--docs/releases/1.2.txt44
-rw-r--r--docs/releases/1.3-alpha-1.txt65
-rw-r--r--docs/releases/1.3-beta-1.txt25
-rw-r--r--docs/releases/1.3.txt68
-rw-r--r--docs/releases/1.4-alpha-1.txt29
-rw-r--r--docs/releases/1.4-beta-1.txt29
-rw-r--r--docs/releases/1.4.txt41
-rw-r--r--docs/releases/1.5-alpha-1.txt45
-rw-r--r--docs/releases/1.5-beta-1.txt38
-rw-r--r--docs/releases/1.5.1.txt28
-rw-r--r--docs/releases/1.5.txt158
-rw-r--r--docs/releases/1.6.txt504
-rw-r--r--docs/releases/index.txt1
-rw-r--r--docs/topics/auth.txt2696
-rw-r--r--docs/topics/auth/customizing.txt1092
-rw-r--r--docs/topics/auth/default.txt1092
-rw-r--r--docs/topics/auth/index.txt89
-rw-r--r--docs/topics/auth/passwords.txt225
-rw-r--r--docs/topics/cache.txt29
-rw-r--r--docs/topics/class-based-views/generic-display.txt20
-rw-r--r--docs/topics/class-based-views/generic-editing.txt78
-rw-r--r--docs/topics/class-based-views/index.txt70
-rw-r--r--docs/topics/class-based-views/intro.txt289
-rw-r--r--docs/topics/class-based-views/mixins.txt222
-rw-r--r--docs/topics/conditional-view-processing.txt2
-rw-r--r--docs/topics/db/aggregation.txt69
-rw-r--r--docs/topics/db/examples/many_to_many.txt47
-rw-r--r--docs/topics/db/examples/one_to_one.txt2
-rw-r--r--docs/topics/db/managers.txt36
-rw-r--r--docs/topics/db/models.txt66
-rw-r--r--docs/topics/db/multi-db.txt45
-rw-r--r--docs/topics/db/optimization.txt2
-rw-r--r--docs/topics/db/queries.txt56
-rw-r--r--docs/topics/db/sql.txt74
-rw-r--r--docs/topics/db/tablespaces.txt3
-rw-r--r--docs/topics/db/transactions.txt756
-rw-r--r--docs/topics/files.txt7
-rw-r--r--docs/topics/forms/formsets.txt103
-rw-r--r--docs/topics/forms/index.txt24
-rw-r--r--docs/topics/forms/modelforms.txt88
-rw-r--r--docs/topics/http/decorators.txt2
-rw-r--r--docs/topics/http/file-uploads.txt8
-rw-r--r--docs/topics/http/sessions.txt136
-rw-r--r--docs/topics/http/shortcuts.txt18
-rw-r--r--docs/topics/http/urls.txt140
-rw-r--r--docs/topics/http/views.txt4
-rw-r--r--docs/topics/i18n/timezones.txt11
-rw-r--r--docs/topics/i18n/translation.txt188
-rw-r--r--docs/topics/index.txt5
-rw-r--r--docs/topics/install.txt4
-rw-r--r--docs/topics/localflavor.txt (renamed from docs/ref/contrib/localflavor.txt)102
-rw-r--r--docs/topics/logging.txt28
-rw-r--r--docs/topics/pagination.txt11
-rw-r--r--docs/topics/python3.txt90
-rw-r--r--docs/topics/security.txt76
-rw-r--r--docs/topics/serialization.txt124
-rw-r--r--docs/topics/settings.txt3
-rw-r--r--docs/topics/signals.txt5
-rw-r--r--docs/topics/signing.txt3
-rw-r--r--docs/topics/templates.txt2
-rw-r--r--docs/topics/testing/_images/django_unittest_classes_hierarchy.graffle (renamed from docs/topics/_images/django_unittest_classes_hierarchy.graffle)0
-rw-r--r--docs/topics/testing/_images/django_unittest_classes_hierarchy.pdf (renamed from docs/topics/_images/django_unittest_classes_hierarchy.pdf)bin51979 -> 51979 bytes
-rw-r--r--docs/topics/testing/_images/django_unittest_classes_hierarchy.svg (renamed from docs/topics/_images/django_unittest_classes_hierarchy.svg)0
-rw-r--r--docs/topics/testing/advanced.txt425
-rw-r--r--docs/topics/testing/doctests.txt81
-rw-r--r--docs/topics/testing/index.txt111
-rw-r--r--docs/topics/testing/overview.txt (renamed from docs/topics/testing.txt)1058
225 files changed, 13809 insertions, 8418 deletions
diff --git a/docs/Makefile b/docs/Makefile
index f6293a8e7f..2a8bcd7101 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -10,7 +10,7 @@ BUILDDIR = _build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+ALLSPHINXOPTS = -n -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
diff --git a/docs/_ext/djangodocs.py b/docs/_ext/djangodocs.py
index 3d85147952..572bcd2e29 100644
--- a/docs/_ext/djangodocs.py
+++ b/docs/_ext/djangodocs.py
@@ -65,19 +65,13 @@ class VersionDirective(Directive):
def run(self):
env = self.state.document.settings.env
- arg0 = self.arguments[0]
- is_nextversion = env.config.django_next_version == arg0
ret = []
node = addnodes.versionmodified()
ret.append(node)
- if not is_nextversion:
- if len(self.arguments) == 1:
- linktext = 'Please see the release notes </releases/%s>' % (arg0)
- xrefs = roles.XRefRole()('doc', linktext, linktext, self.lineno, self.state)
- node.extend(xrefs[0])
- node['version'] = arg0
- else:
+ if self.arguments[0] == env.config.django_next_version:
node['version'] = "Development version"
+ else:
+ node['version'] = self.arguments[0]
node['type'] = self.name
if len(self.arguments) == 2:
inodes, messages = self.state.inline_text(self.arguments[1], self.lineno+1)
@@ -126,7 +120,7 @@ class DjangoHTMLTranslator(SmartyPantsHTMLTranslator):
# which is a bit less obvious that I'd like.
#
# FIXME: these messages are all hardcoded in English. We need to change
- # that to accomodate other language docs, but I can't work out how to make
+ # that to accommodate other language docs, but I can't work out how to make
# that work.
#
version_text = {
@@ -210,7 +204,7 @@ class DjangoStandaloneHTMLBuilder(StandaloneHTMLBuilder):
if t == "templatefilter" and l == "ref/templates/builtins"],
}
outfilename = os.path.join(self.outdir, "templatebuiltins.js")
- with open(outfilename, 'wb') as fp:
+ with open(outfilename, 'w') as fp:
fp.write('var django_template_builtins = ')
json.dump(templatebuiltins, fp)
fp.write(';\n')
diff --git a/docs/_ext/literals_to_xrefs.py b/docs/_ext/literals_to_xrefs.py
index d7b3cfc549..6feeca992e 100644
--- a/docs/_ext/literals_to_xrefs.py
+++ b/docs/_ext/literals_to_xrefs.py
@@ -110,7 +110,7 @@ def fixliterals(fname):
#
# The following is taken from django.utils.termcolors and is copied here to
-# avoid the dependancy.
+# avoid the dependency.
#
diff --git a/docs/_theme/djangodocs/static/djangodocs.css b/docs/_theme/djangodocs/static/djangodocs.css
index 4efb7e04f3..c8d223382d 100644
--- a/docs/_theme/djangodocs/static/djangodocs.css
+++ b/docs/_theme/djangodocs/static/djangodocs.css
@@ -90,8 +90,8 @@ table.docutils thead th p { margin: 0; padding: 0; }
table.docutils { border-collapse:collapse; }
/*** code blocks ***/
-.literal { white-space:nowrap; }
-.literal { color:#234f32; }
+.literal { color:#234f32; white-space:nowrap; }
+dt > tt.literal { white-space: normal; }
#sidebar .literal { color:white; background:transparent; font-size:11px; }
h4 .literal { color: #234f32; font-size: 13px; }
pre { font-size:small; background:#E0FFB8; border:1px solid #94da3a; border-width:1px 0; margin: 1em 0; padding: .3em .4em; overflow: hidden; line-height: 1.3em;}
@@ -115,7 +115,7 @@ div.admonition-behind-the-scenes { padding-left:65px; background:url(docicons-be
/*** versoinadded/changes ***/
div.versionadded, div.versionchanged { }
-div.versionadded span.title, div.versionchanged span.title { font-weight: bold; }
+div.versionadded span.title, div.versionchanged span.title, div.deprecated span.title { font-weight: bold; }
/*** p-links ***/
a.headerlink { color: #c60f0f; font-size: 0.8em; padding: 0 4px 0 4px; text-decoration: none; visibility: hidden; }
diff --git a/docs/conf.py b/docs/conf.py
index f58e4ecb2e..a654e3c4d6 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -70,8 +70,8 @@ else:
release = django_release()
-# The next version to be released
-django_next_version = '1.7'
+# The "development version" of Django
+django_next_version = '1.6'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -110,6 +110,7 @@ intersphinx_mapping = {
'python': ('http://docs.python.org/2.7', None),
'sphinx': ('http://sphinx.pocoo.org/', None),
'six': ('http://packages.python.org/six/', None),
+ 'simplejson': ('http://simplejson.readthedocs.org/en/latest/', None),
}
# Python's docs don't change every week.
diff --git a/docs/faq/admin.txt b/docs/faq/admin.txt
index 30d452cbe2..1d9a7c7427 100644
--- a/docs/faq/admin.txt
+++ b/docs/faq/admin.txt
@@ -49,7 +49,7 @@ How do I limit admin access so that objects can only be edited by the users who
The :class:`~django.contrib.admin.ModelAdmin` class also provides customization
hooks that allow you to control the visibility and editability of objects in the
admin. Using the same trick of extracting the user from the request, the
-:meth:`~django.contrib.admin.ModelAdmin.queryset` and
+:meth:`~django.contrib.admin.ModelAdmin.get_queryset` and
:meth:`~django.contrib.admin.ModelAdmin.has_change_permission` can be used to
control the visibility and editability of objects in the admin.
diff --git a/docs/faq/general.txt b/docs/faq/general.txt
index dc569840d1..5db3141f82 100644
--- a/docs/faq/general.txt
+++ b/docs/faq/general.txt
@@ -188,3 +188,31 @@ If you want to find Django-capable people in your local area, try
https://people.djangoproject.com/ .
.. _developers for hire page: https://code.djangoproject.com/wiki/DevelopersForHire
+
+How do I cite Django?
+---------------------
+
+It's difficult to give an official citation format, for two reasons: citation
+formats can vary wildly between publications, and citation standards for
+software are still a matter of some debate.
+
+For example, `APA style`_, would dictate something like::
+
+ Django (Version 1.5) [Computer Software]. (2013). Retrieved from http://djangoproject.com.
+
+However, the only true guide is what your publisher will accept, so get a copy
+of those guidelines and fill in the gaps as best you can.
+
+If your referencing style guide requires a publisher name, use "Django Software
+Foundation".
+
+If you need a publishing location, use "Lawrence, Kansas".
+
+If you need a web address, use http://djangoproject.com.
+
+If you need a name, just use "Django", without any tagline.
+
+If you need a publication date, use the year of release of the version you're
+referencing (e.g., 2013 for v1.5)
+
+.. _APA style: http://www.apastyle.org
diff --git a/docs/faq/install.txt b/docs/faq/install.txt
index a92c9d87ea..5a4cab94cf 100644
--- a/docs/faq/install.txt
+++ b/docs/faq/install.txt
@@ -68,8 +68,10 @@ Django version Python versions
1.1 2.3, 2.4, 2.5, 2.6
1.2 2.4, 2.5, 2.6, 2.7
1.3 2.4, 2.5, 2.6, 2.7
-**1.4** **2.5, 2.6, 2.7**
-*1.5 (future)* *2.6, 2.7* and *3.2.3, 3.3 (experimental)*
+1.4 2.5, 2.6, 2.7
+1.5 2.6.5, 2.7 and 3.2.3, 3.3 (experimental)
+**1.6** **2.6.5, 2.7** and **3.2.3, 3.3**
+*1.7 (future)* *2.7, 3.3 (to be confirmed)*
============== ===============
Can I use Django with Python 3?
diff --git a/docs/faq/usage.txt b/docs/faq/usage.txt
index 151454398d..be3839e08f 100644
--- a/docs/faq/usage.txt
+++ b/docs/faq/usage.txt
@@ -52,10 +52,11 @@ Using a :class:`~django.db.models.FileField` or an
#. All that will be stored in your database is a path to the file
(relative to :setting:`MEDIA_ROOT`). You'll most likely want to use the
- convenience :attr:`~django.core.files.File.url` attribute provided by
- Django. For example, if your :class:`~django.db.models.ImageField` is
- called ``mug_shot``, you can get the absolute path to your image in a
- template with ``{{ object.mug_shot.url }}``.
+ convenience :attr:`~django.db.models.fields.files.FieldFile.url` attribute
+ provided by Django. For example, if your
+ :class:`~django.db.models.ImageField` is called ``mug_shot``, you can get
+ the absolute path to your image in a template with
+ ``{{ object.mug_shot.url }}``.
How do I make a variable available to all my templates?
-------------------------------------------------------
diff --git a/docs/howto/auth-remote-user.txt b/docs/howto/auth-remote-user.txt
index deab794cb1..d59bb25a85 100644
--- a/docs/howto/auth-remote-user.txt
+++ b/docs/howto/auth-remote-user.txt
@@ -27,6 +27,8 @@ use of the ``REMOTE_USER`` value using the ``RemoteUserMiddleware`` and
Configuration
=============
+.. class:: django.contrib.auth.middleware.RemoteUserMiddleware
+
First, you must add the
:class:`django.contrib.auth.middleware.RemoteUserMiddleware` to the
:setting:`MIDDLEWARE_CLASSES` setting **after** the
diff --git a/docs/howto/custom-management-commands.txt b/docs/howto/custom-management-commands.txt
index 12e8ec2494..7a31fc44e3 100644
--- a/docs/howto/custom-management-commands.txt
+++ b/docs/howto/custom-management-commands.txt
@@ -2,6 +2,8 @@
Writing custom django-admin commands
====================================
+.. module:: django.core.management
+
Applications can register their own actions with ``manage.py``. For example,
you might want to add a ``manage.py`` action for a Django app that you're
distributing. In this document, we will be building a custom ``closepoll``
@@ -63,12 +65,18 @@ look like this:
self.stdout.write('Successfully closed poll "%s"' % poll_id)
+.. _management-commands-output:
+
.. note::
When you are using management commands and wish to provide console
output, you should write to ``self.stdout`` and ``self.stderr``,
instead of printing to ``stdout`` and ``stderr`` directly. By
using these proxies, it becomes much easier to test your custom
- command.
+ command. Note also that you don't need to end messages with a newline
+ character, it will be added automatically, unless you specify the ``ending``
+ parameter::
+
+ self.stdout.write("Unterminated line", ending='')
The new custom command can be called using ``python manage.py closepoll
<poll_id>``.
@@ -110,54 +118,61 @@ In addition to being able to add custom command line options, all
:doc:`management commands</ref/django-admin>` can accept some
default options such as :djadminopt:`--verbosity` and :djadminopt:`--traceback`.
-.. admonition:: Management commands and locales
+.. _management-commands-and-locales:
- The :meth:`BaseCommand.execute` method sets the hardcoded ``en-us`` locale
- because the commands shipped with Django perform several tasks
- (for example, user-facing content rendering and database population) that
- require a system-neutral string language (for which we use ``en-us``).
+Management commands and locales
+===============================
- If your custom management command uses another locale, you should manually
- activate and deactivate it in your :meth:`~BaseCommand.handle` or
- :meth:`~NoArgsCommand.handle_noargs` method using the functions provided by
- the I18N support code:
+By default, the :meth:`BaseCommand.execute` method sets the hardcoded 'en-us'
+locale because some commands shipped with Django perform several tasks
+(for example, user-facing content rendering and database population) that
+require a system-neutral string language (for which we use 'en-us').
- .. code-block:: python
+If, for some reason, your custom management command needs to use a fixed locale
+different from 'en-us', you should manually activate and deactivate it in your
+:meth:`~BaseCommand.handle` or :meth:`~NoArgsCommand.handle_noargs` method using
+the functions provided by the I18N support code:
- from django.core.management.base import BaseCommand, CommandError
- from django.utils import translation
+.. code-block:: python
- class Command(BaseCommand):
- ...
- can_import_settings = True
+ from django.core.management.base import BaseCommand, CommandError
+ from django.utils import translation
- def handle(self, *args, **options):
+ class Command(BaseCommand):
+ ...
+ can_import_settings = True
- # Activate a fixed locale, e.g. Russian
- translation.activate('ru')
+ def handle(self, *args, **options):
+
+ # Activate a fixed locale, e.g. Russian
+ translation.activate('ru')
- # Or you can activate the LANGUAGE_CODE
- # chosen in the settings:
- #
- #from django.conf import settings
- #translation.activate(settings.LANGUAGE_CODE)
+ # Or you can activate the LANGUAGE_CODE # chosen in the settings:
+ #
+ #from django.conf import settings
+ #translation.activate(settings.LANGUAGE_CODE)
- # Your command logic here
- # ...
+ # Your command logic here
+ # ...
- translation.deactivate()
+ translation.deactivate()
- Take into account though, that system management commands typically have to
- be very careful about running in non-uniform locales, so:
+Another need might be that your command simply should use the locale set in
+settings and Django should be kept from forcing it to 'en-us'. You can achieve
+it by using the :data:`BaseCommand.leave_locale_alone` option.
- * Make sure the :setting:`USE_I18N` setting is always ``True`` when running
- the command (this is one good example of the potential problems stemming
- from a dynamic runtime environment that Django commands avoid offhand by
- always using a fixed locale).
+When working on the scenarios described above though, take into account that
+system management commands typically have to be very careful about running in
+non-uniform locales, so you might need to:
- * Review the code of your command and the code it calls for behavioral
- differences when locales are changed and evaluate its impact on
- predictable behavior of your command.
+* Make sure the :setting:`USE_I18N` setting is always ``True`` when running
+ the command (this is a good example of the potential problems stemming
+ from a dynamic runtime environment that Django commands avoid offhand by
+ always using a fixed locale).
+
+* Review the code of your command and the code it calls for behavioral
+ differences when locales are changed and evaluate its impact on
+ predictable behavior of your command.
Command objects
===============
@@ -220,6 +235,29 @@ All attributes can be set in your derived class and can be used in
rather than all applications' models, call
:meth:`~BaseCommand.validate` from :meth:`~BaseCommand.handle`.
+.. attribute:: BaseCommand.leave_locale_alone
+
+ A boolean indicating whether the locale set in settings should be preserved
+ during the execution of the command instead of being forcibly set to 'en-us'.
+
+ Default value is ``False``.
+
+ Make sure you know what you are doing if you decide to change the value of
+ this option in your custom command if it creates database content that
+ is locale-sensitive and such content shouldn't contain any translations (like
+ it happens e.g. with django.contrim.auth permissions) as making the locale
+ differ from the de facto default 'en-us' might cause unintended effects. See
+ the `Management commands and locales`_ section above for further details.
+
+ This option can't be ``False`` when the
+ :data:`~BaseCommand.can_import_settings` option is set to ``False`` too
+ because attempting to set the locale needs access to settings. This condition
+ will generate a :class:`CommandError`.
+
+.. versionadded:: 1.6
+
+The ``leave_locale_alone`` option was added in Django 1.6.
+
Methods
-------
@@ -261,6 +299,13 @@ the :meth:`~BaseCommand.handle` method must be implemented.
The actual logic of the command. Subclasses must implement this method.
+.. method:: BaseCommand.validate(app=None, display_num_errors=False)
+
+ Validates the given app, raising :class:`CommandError` for any errors.
+
+ If ``app`` is None, then all installed apps are validated.
+
+
.. _ref-basecommand-subclasses:
BaseCommand subclasses
diff --git a/docs/howto/custom-model-fields.txt b/docs/howto/custom-model-fields.txt
index 1e9d5d8701..8993872cff 100644
--- a/docs/howto/custom-model-fields.txt
+++ b/docs/howto/custom-model-fields.txt
@@ -19,7 +19,7 @@ only the common types, such as ``VARCHAR`` and ``INTEGER``. For more obscure
column types, such as geographic polygons or even user-created types such as
`PostgreSQL custom types`_, you can define your own Django ``Field`` subclasses.
-.. _PostgreSQL custom types: http://www.postgresql.org/docs/8.2/interactive/sql-createtype.html
+.. _PostgreSQL custom types: http://www.postgresql.org/docs/current/interactive/sql-createtype.html
Alternatively, you may have a complex Python object that can somehow be
serialized to fit into a standard database column type. This is another case
@@ -153,8 +153,8 @@ class, from which everything is descended.
Initializing your new field is a matter of separating out any arguments that are
specific to your case from the common arguments and passing the latter to the
-:meth:`~django.db.models.Field.__init__` method of
-:class:`~django.db.models.Field` (or your parent class).
+``__init__()`` method of :class:`~django.db.models.Field` (or your parent
+class).
In our example, we'll call our field ``HandField``. (It's a good idea to call
your :class:`~django.db.models.Field` subclass ``<Something>Field``, so it's
@@ -199,20 +199,20 @@ The :meth:`~django.db.models.Field.__init__` method takes the following
parameters:
* :attr:`~django.db.models.Field.verbose_name`
-* :attr:`~django.db.models.Field.name`
+* ``name``
* :attr:`~django.db.models.Field.primary_key`
-* :attr:`~django.db.models.Field.max_length`
+* :attr:`~django.db.models.CharField.max_length`
* :attr:`~django.db.models.Field.unique`
* :attr:`~django.db.models.Field.blank`
* :attr:`~django.db.models.Field.null`
* :attr:`~django.db.models.Field.db_index`
-* :attr:`~django.db.models.Field.rel`: Used for related fields (like
- :class:`ForeignKey`). For advanced use only.
+* ``rel``: Used for related fields (like :class:`ForeignKey`). For advanced
+ use only.
* :attr:`~django.db.models.Field.default`
* :attr:`~django.db.models.Field.editable`
-* :attr:`~django.db.models.Field.serialize`: If ``False``, the field will
- not be serialized when the model is passed to Django's :doc:`serializers
- </topics/serialization>`. Defaults to ``True``.
+* ``serialize``: If ``False``, the field will not be serialized when the model
+ is passed to Django's :doc:`serializers </topics/serialization>`. Defaults to
+ ``True``.
* :attr:`~django.db.models.Field.unique_for_date`
* :attr:`~django.db.models.Field.unique_for_month`
* :attr:`~django.db.models.Field.unique_for_year`
@@ -222,9 +222,9 @@ parameters:
* :attr:`~django.db.models.Field.db_tablespace`: Only for index creation, if the
backend supports :doc:`tablespaces </topics/db/tablespaces>`. You can usually
ignore this option.
-* :attr:`~django.db.models.Field.auto_created`: True if the field was
- automatically created, as for the `OneToOneField` used by model
- inheritance. For advanced use only.
+* ``auto_created``: ``True`` if the field was automatically created, as for the
+ :class:`~django.db.models.OneToOneField` used by model inheritance. For
+ advanced use only.
All of the options without an explanation in the above list have the same
meaning they do for normal Django fields. See the :doc:`field documentation
@@ -443,7 +443,7 @@ Python object type we want to store in the model's attribute. If anything is
going wrong during value conversion, you should raise a
:exc:`~django.core.exceptions.ValidationError` exception.
-**Remember:** If your custom field needs the :meth:`to_python` method to be
+**Remember:** If your custom field needs the :meth:`.to_python` method to be
called when it is created, you should be using `The SubfieldBase metaclass`_
mentioned earlier. Otherwise :meth:`.to_python` won't be called
automatically.
@@ -602,12 +602,11 @@ Returns the default form field to use when this field is displayed in a model.
This method is called by the :class:`~django.forms.ModelForm` helper.
All of the ``kwargs`` dictionary is passed directly to the form field's
-:meth:`~django.forms.Field__init__` method. Normally, all you need to do is
-set up a good default for the ``form_class`` argument and then delegate further
-handling to the parent class. This might require you to write a custom form
-field (and even a form widget). See the :doc:`forms documentation
-</topics/forms/index>` for information about this, and take a look at the code in
-:mod:`django.contrib.localflavor` for some examples of custom widgets.
+``__init__()`` method. Normally, all you need to do is set up a good default
+for the ``form_class`` argument and then delegate further handling to the
+parent class. This might require you to write a custom form field (and even a
+form widget). See the :doc:`forms documentation </topics/forms/index>` for
+information about this.
Continuing our ongoing example, we can write the :meth:`.formfield` method as::
@@ -668,7 +667,7 @@ Converting field data for serialization
.. method:: Field.value_to_string(self, obj)
This method is used by the serializers to convert the field into a string for
-output. Calling :meth:`Field._get_val_from_obj(obj)` is the best way to get the
+output. Calling ``Field._get_val_from_obj(obj)`` is the best way to get the
value to serialize. For example, since our ``HandField`` uses strings for its
data storage anyway, we can reuse some existing conversion code::
@@ -692,12 +691,12 @@ smoothly:
a field that's similar to what you want and extend it a little bit,
instead of creating an entirely new field from scratch.
-2. Put a :meth:`__str__` or :meth:`__unicode__` method on the class you're
+2. Put a ``__str__()`` or ``__unicode__()`` method on the class you're
wrapping up as a field. There are a lot of places where the default
behavior of the field code is to call
:func:`~django.utils.encoding.force_text` on the value. (In our
examples in this document, ``value`` would be a ``Hand`` instance, not a
- ``HandField``). So if your :meth:`__unicode__` method automatically
+ ``HandField``). So if your ``__unicode__()`` method automatically
converts to the string form of your Python object, you can save yourself
a lot of work.
diff --git a/docs/howto/custom-template-tags.txt b/docs/howto/custom-template-tags.txt
index 70b6288bee..0d35654a04 100644
--- a/docs/howto/custom-template-tags.txt
+++ b/docs/howto/custom-template-tags.txt
@@ -114,6 +114,8 @@ your function. Example:
Registering custom filters
~~~~~~~~~~~~~~~~~~~~~~~~~~
+.. method:: django.template.Library.filter
+
Once you've written your filter definition, you need to register it with
your ``Library`` instance, to make it available to Django's template language:
@@ -151,6 +153,8 @@ are described in :ref:`filters and auto-escaping <filters-auto-escaping>` and
Template filters that expect strings
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.. method:: django.template.defaultfilters.stringfilter
+
If you're writing a template filter that only expects a string as the first
argument, you should use the decorator ``stringfilter``. This will
convert an object to its string value before being passed to your function:
@@ -328,33 +332,11 @@ Template filter code falls into one of two situations:
handle the auto-escaping issues and return a safe string, the
``is_safe`` flag won't change anything either way.
-.. versionchanged:: 1.4
-
-``is_safe`` and ``needs_autoescape`` used to be attributes of the filter
-function; this syntax is deprecated.
-
-.. code-block:: python
-
- @register.filter
- def myfilter(value):
- return value
- myfilter.is_safe = True
-
-.. code-block:: python
-
- @register.filter
- def initial_letter_filter(text, autoescape=None):
- # ...
- return mark_safe(result)
- initial_letter_filter.needs_autoescape = True
-
.. _filters-timezones:
Filters and time zones
~~~~~~~~~~~~~~~~~~~~~~
-.. versionadded:: 1.4
-
If you write a custom filter that operates on :class:`~datetime.datetime`
objects, you'll usually register it with the ``expects_localtime`` flag set to
``True``:
@@ -722,6 +704,8 @@ cannot resolve the string passed to it in the current context of the page.
Simple tags
~~~~~~~~~~~
+.. method:: django.template.Library.simple_tag
+
Many template tags take a number of arguments -- strings or template variables
-- and return a string after doing some processing based solely on
the input arguments and some external information. For example, the
@@ -784,8 +768,6 @@ Or, using decorator syntax:
For more information on how the ``takes_context`` option works, see the section
on :ref:`inclusion tags<howto-custom-template-tags-inclusion-tags>`.
-.. versionadded:: 1.4
-
If you need to rename your tag, you can provide a custom name for it:
.. code-block:: python
@@ -796,8 +778,6 @@ If you need to rename your tag, you can provide a custom name for it:
def some_function(value):
return value - 2
-.. versionadded:: 1.4
-
``simple_tag`` functions may accept any number of positional or keyword
arguments. For example:
@@ -885,16 +865,14 @@ template loader, we'd register the tag like this:
# Here, register is a django.template.Library instance, as before
register.inclusion_tag('results.html')(show_results)
-.. versionchanged:: 1.4
-
- Alternatively it is possible to register the inclusion tag using a
- :class:`django.template.Template` instance:
+Alternatively it is possible to register the inclusion tag using a
+:class:`django.template.Template` instance:
- .. code-block:: python
+.. code-block:: python
- from django.template.loader import get_template
- t = get_template('results.html')
- register.inclusion_tag(t)(show_results)
+ from django.template.loader import get_template
+ t = get_template('results.html')
+ register.inclusion_tag(t)(show_results)
As always, decorator syntax works as well, so we could have written:
@@ -952,8 +930,6 @@ The ``takes_context`` parameter defaults to ``False``. When it's set to
``True``, the tag is passed the context object, as in this example. That's the
only difference between this case and the previous ``inclusion_tag`` example.
-.. versionadded:: 1.4
-
``inclusion_tag`` functions may accept any number of positional or keyword
arguments. For example:
@@ -1066,8 +1042,6 @@ context-updating template tag, you might want to consider using an
Assignment tags
~~~~~~~~~~~~~~~
-.. versionadded:: 1.4
-
To ease the creation of tags setting a variable in the context, Django provides
a helper function, ``assignment_tag``. This function works the same way as
:ref:`simple_tag<howto-custom-template-tags-simple-tags>`, except that it
diff --git a/docs/howto/deployment/checklist.txt b/docs/howto/deployment/checklist.txt
new file mode 100644
index 0000000000..b092048870
--- /dev/null
+++ b/docs/howto/deployment/checklist.txt
@@ -0,0 +1,205 @@
+====================
+Deployment checklist
+====================
+
+The Internet is a hostile environment. Before deploying your Django project,
+you should take some time to review your settings, with security, performance,
+and operations in mind.
+
+Django includes many :doc:`security features </topics/security>`. Some are
+built-in and always enabled. Others are optional because they aren't always
+appropriate, or because they're inconvenient for development. For example,
+forcing HTTPS may not be suitable for all websites, and it's impractical for
+local development.
+
+Performance optimizations are another category of trade-offs with convenience.
+For instance, caching is useful in production, less so for local development.
+Error reporting needs are also widely different.
+
+The following checklist includes settings that:
+
+- must be set properly for Django to provide the expected level of security;
+- are expected to be different in each environment;
+- enable optional security features;
+- enable performance optimizations;
+- provide error reporting.
+
+Many of these settings are sensitive and should be treated as confidential. If
+you're releasing the source code for your project, a common practice is to
+publish suitable settings for development, and to use a private settings
+module for production.
+
+Critical settings
+=================
+
+:setting:`SECRET_KEY`
+---------------------
+
+**The secret key must be a large random value and it must be kept secret.**
+
+Make sure that the key used in production isn't used anywhere else and avoid
+committing it to source control. This reduces the number of vectors from which
+an attacker may acquire the key.
+
+Instead of hardcoding the secret key in your settings module, consider loading
+it from an environment variable::
+
+ import os
+ SECRET_KEY = os.environ['SECRET_KEY']
+
+or from a file::
+
+ with open('/etc/secret_key.txt') as f:
+ SECRET_KEY = f.read().strip()
+
+:setting:`DEBUG`
+----------------
+
+**You must never enable debug in production.**
+
+You're certainly developing your project with :setting:`DEBUG = True <DEBUG>`,
+since this enables handy features like full tracebacks in your browser.
+
+For a production environment, though, this is a really bad idea, because it
+leaks lots of information about your project: excerpts of your source code,
+local variables, settings, libraries used, etc.
+
+Environment-specific settings
+=============================
+
+:setting:`ALLOWED_HOSTS`
+------------------------
+
+When :setting:`DEBUG = False <DEBUG>`, Django doesn't work at all without a
+suitable value for :setting:`ALLOWED_HOSTS`.
+
+This setting is required to protect your site against some CSRF attacks. If
+you use a wildcard, you must perform your own validation of the ``Host`` HTTP
+header, or otherwise ensure that you aren't vulnerable to this category of
+attacks.
+
+:setting:`CACHES`
+-----------------
+
+If you're using a cache, connection parameters may be different in development
+and in production.
+
+Cache servers often have weak authentication. Make sure they only accept
+connections from your application servers.
+
+:setting:`DATABASES`
+--------------------
+
+Database connection parameters are probably different in development and in
+production.
+
+Database passwords are very sensitive. You should protect them exactly like
+:setting:`SECRET_KEY`.
+
+For maximum security, make sure database servers only accept connections from
+your application servers.
+
+If you haven't set up backups for your database, do it right now!
+
+:setting:`EMAIL_BACKEND` and related settings
+---------------------------------------------
+
+If your site sends emails, these values need to be set correctly.
+
+:setting:`STATIC_ROOT` and :setting:`STATIC_URL`
+------------------------------------------------
+
+Static files are automatically served by the development server. In
+production, you must define a :setting:`STATIC_ROOT` directory where
+:djadmin:`collectstatic` will copy them.
+
+See :doc:`/howto/static-files/index` for more information.
+
+:setting:`MEDIA_ROOT` and :setting:`MEDIA_URL`
+----------------------------------------------
+
+Media files are uploaded by your users. They're untrusted! Make sure your web
+server never attempt to interpret them. For instance, if a user uploads a
+``.php`` file , the web server shouldn't execute it.
+
+Now is a good time to check your backup strategy for these files.
+
+HTTPS
+=====
+
+Any website which allows users to log in should enforce site-wide HTTPS to
+avoid transmitting access tokens in clear. In Django, access tokens include
+the login/password, the session cookie, and password reset tokens. (You can't
+do much to protect password reset tokens if you're sending them by email.)
+
+Protecting sensitive areas such as the user account or the admin isn't
+sufficient, because the same session cookie is used for HTTP and HTTPS. Your
+web server must redirect all HTTP traffic to HTTPS, and only transmit HTTPS
+requests to Django.
+
+Once you've set up HTTPS, enable the following settings.
+
+:setting:`CSRF_COOKIE_SECURE`
+-----------------------------
+
+Set this to ``True`` to avoid transmitting the CSRF cookie over HTTP
+accidentally.
+
+:setting:`SESSION_COOKIE_SECURE`
+--------------------------------
+
+Set this to ``True`` to avoid transmitting the session cookie over HTTP
+accidentally.
+
+Performance optimizations
+=========================
+
+Setting :setting:`DEBUG = False <DEBUG>` disables several features that are
+only useful in development. In addition, you can tune the following settings.
+
+:setting:`TEMPLATE_LOADERS`
+---------------------------
+
+Enabling the cached template loader often improves performance drastically, as
+it avoids compiling each template every time it needs to be rendered. See the
+:ref:`template loaders docs <template-loaders>` for more information.
+
+Error reporting
+===============
+
+By the time you push your code to production, it's hopefully robust, but you
+can't rule out unexpected errors. Thankfully, Django can capture errors and
+notify you accordingly.
+
+:setting:`LOGGING`
+------------------
+
+Review your logging configuration before putting your website in production,
+and check that it works as expected as soon as you have received some traffic.
+
+See :doc:`/topics/logging` for details on logging.
+
+:setting:`ADMINS` and :setting:`MANAGERS`
+-----------------------------------------
+
+:setting:`ADMINS` will be notified of 500 errors by email.
+
+:setting:`MANAGERS` will be notified of 404 errors.
+:setting:`IGNORABLE_404_URLS` can help filter out spurious reports.
+
+See :doc:`/howto/error-reporting` for details on error reporting by email.
+
+.. admonition: Error reporting by email doesn't scale very well
+
+ Consider using an error monitoring system such as Sentry_ before your
+ inbox is flooded by reports. Sentry can also aggregate logs.
+
+ .. _Sentry: http://sentry.readthedocs.org/en/latest/
+
+Miscellaneous
+=============
+
+:setting:`ALLOWED_INCLUDE_ROOTS`
+--------------------------------
+
+This setting is required if you're using the :ttag:`ssi` template tag.
diff --git a/docs/howto/deployment/index.txt b/docs/howto/deployment/index.txt
index 8e27a031d5..ed4bcf3d4a 100644
--- a/docs/howto/deployment/index.txt
+++ b/docs/howto/deployment/index.txt
@@ -11,6 +11,7 @@ ways to easily deploy Django:
wsgi/index
fastcgi
+ checklist
If you're new to deploying Django and/or Python, we'd recommend you try
:doc:`mod_wsgi </howto/deployment/wsgi/modwsgi>` first. In most cases it'll be
diff --git a/docs/howto/deployment/wsgi/apache-auth.txt b/docs/howto/deployment/wsgi/apache-auth.txt
index 5f700f1cb3..d06e89cd0e 100644
--- a/docs/howto/deployment/wsgi/apache-auth.txt
+++ b/docs/howto/deployment/wsgi/apache-auth.txt
@@ -4,7 +4,7 @@ Authenticating against Django's user database from Apache
Since keeping multiple authentication databases in sync is a common problem when
dealing with Apache, you can configure Apache to authenticate against Django's
-:doc:`authentication system </topics/auth>` directly. This requires Apache
+:doc:`authentication system </topics/auth/index>` directly. This requires Apache
version >= 2.2 and mod_wsgi >= 2.0. For example, you could:
* Serve static/media files directly from Apache only to authenticated users.
@@ -69,9 +69,9 @@ application :doc:`that is created by django-admin.py startproject
LoadModule auth_basic_module modules/mod_auth_basic.so
LoadModule authz_user_module modules/mod_authz_user.so
-Finally, edit your WSGI script ``mysite.wsgi`` to tie Apache's
-authentication to your site's authentication mechanisms by importing the
-check_user function:
+Finally, edit your WSGI script ``mysite.wsgi`` to tie Apache's authentication
+to your site's authentication mechanisms by importing the ``check_password``
+function:
.. code-block:: python
diff --git a/docs/howto/deployment/wsgi/gunicorn.txt b/docs/howto/deployment/wsgi/gunicorn.txt
index c4483291a3..14c80af0a0 100644
--- a/docs/howto/deployment/wsgi/gunicorn.txt
+++ b/docs/howto/deployment/wsgi/gunicorn.txt
@@ -48,6 +48,12 @@ ensure that is to run this command from the same directory as your
Using Gunicorn's Django integration
===================================
+.. note::
+
+ If you are using Django 1.4 or newer, it’s highly recommended to simply run
+ your application with the WSGI interface using the ``gunicorn`` command
+ as described above.
+
To use Gunicorn's built-in Django integration, first add ``"gunicorn"`` to
:setting:`INSTALLED_APPS`. Then run ``python manage.py run_gunicorn``.
diff --git a/docs/howto/deployment/wsgi/index.txt b/docs/howto/deployment/wsgi/index.txt
index 769d406b1b..ad32366bf8 100644
--- a/docs/howto/deployment/wsgi/index.txt
+++ b/docs/howto/deployment/wsgi/index.txt
@@ -8,9 +8,10 @@ servers and applications.
.. _WSGI: http://www.wsgi.org
Django's :djadmin:`startproject` management command sets up a simple default
-WSGI configuration for you, which you can tweak as needed for your project, and
-direct any WSGI-compliant webserver to use. Django includes getting-started
-documentation for the following WSGI servers:
+WSGI configuration for you, which you can tweak as needed for your project,
+and direct any WSGI-compliant application server to use.
+
+Django includes getting-started documentation for the following WSGI servers:
.. toctree::
:maxdepth: 1
@@ -23,34 +24,76 @@ documentation for the following WSGI servers:
The ``application`` object
--------------------------
-One key concept of deploying with WSGI is to specify a central ``application``
-callable object which the webserver uses to communicate with your code. This is
-commonly specified as an object named ``application`` in a Python module
-accessible to the server.
+The key concept of deploying with WSGI is the ``application`` callable which
+the application server uses to communicate with your code. It's commonly
+provided as an object named ``application`` in a Python module accessible to
+the server.
+
+The :djadmin:`startproject` command creates a file
+:file:`<project_name>/wsgi.py` that contains such an ``application`` callable.
+
+It's used both by Django's development server and in production WSGI
+deployments.
+
+WSGI servers obtain the path to the ``application`` callable from their
+configuration. Django's built-in servers, namely the :djadmin:`runserver` and
+:djadmin:`runfcgi` commands, read it from the :setting:`WSGI_APPLICATION`
+setting. By default, it's set to ``<project_name>.wsgi.application``, which
+points to the ``application`` callable in :file:`<project_name>/wsgi.py`.
-.. versionchanged:: 1.4
+Configuring the settings module
+-------------------------------
-The :djadmin:`startproject` command creates a :file:`projectname/wsgi.py` that
-contains such an application callable.
+When the WSGI server loads your application, Django needs to import the
+settings module — that's where your entire application is defined.
+
+Django uses the :envvar:`DJANGO_SETTINGS_MODULE` environment variable to
+locate the appropriate settings module. It must contain the dotted path to the
+settings module. You can use a different value for development and production;
+it all depends on how you organize your settings.
+
+If this variable isn't set, the default :file:`wsgi.py` sets it to
+``mysite.settings``, where ``mysite`` is the name of your project. That's how
+:djadmin:`runserver` discovers the default settings file by default.
.. note::
- Upgrading from a previous release of Django and don't have a :file:`wsgi.py`
- file in your project? You can simply add one to your project's top-level
- Python package (probably next to :file:`settings.py` and :file:`urls.py`)
- with the contents below. If you want :djadmin:`runserver` to also make use
- of this WSGI file, you can also add ``WSGI_APPLICATION =
- "mysite.wsgi.application"`` in your settings (replacing ``mysite`` with the
- name of your project).
+ Since environment variables are process-wide, this doesn't work when you
+ run multiple Django sites in the same process. This happens with mod_wsgi.
-Initially this file contains::
+ To avoid this problem, use mod_wsgi's daemon mode with each site in its
+ own daemon process, or override the value from the environment by
+ enforcing ``os.environ["DJANGO_SETTINGS_MODULE"] = "mysite.settings"`` in
+ your :file:`wsgi.py`.
- import os
+Applying WSGI middleware
+------------------------
+
+To apply `WSGI middleware`_ you can simply wrap the application object. For
+instance you could add these lines at the bottom of :file:`wsgi.py`::
+
+ from helloworld.wsgi import HelloWorldApplication
+ application = HelloWorldApplication(application)
+
+You could also replace the Django WSGI application with a custom WSGI
+application that later delegates to the Django WSGI application, if you want
+to combine a Django application with a WSGI application of another framework.
+
+.. _`WSGI middleware`: http://www.python.org/dev/peps/pep-3333/#middleware-components-that-play-both-sides
+
+Upgrading from Django < 1.4
+---------------------------
+
+If you're upgrading from Django 1.3.x or earlier, you don't have a
+:file:`wsgi.py` file in your project.
+
+You can simply add one to your project's top-level Python package (probably
+next to :file:`settings.py` and :file:`urls.py`) with the contents below::
+
+ import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
- # This application object is used by the development server
- # as well as any WSGI server configured to use this file.
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
@@ -60,14 +103,6 @@ environment variable. You'll need to edit this line to replace ``mysite`` with
the name of your project package, so the path to your settings module is
correct.
-To apply `WSGI middleware`_ you can simply wrap the application object
-in the same file::
-
- from helloworld.wsgi import HelloWorldApplication
- application = HelloWorldApplication(application)
-
-You could also replace the Django WSGI application with a custom WSGI
-application that later delegates to the Django WSGI application, if you want to
-combine a Django application with a WSGI application of another framework.
-
-.. _`WSGI middleware`: http://www.python.org/dev/peps/pep-3333/#middleware-components-that-play-both-sides
+Also add ``WSGI_APPLICATION = "mysite.wsgi.application"`` in your settings, so
+that :djadmin:`runserver` finds your ``application`` callable. Don't forget to
+replace ``mysite`` with the name of your project in this line.
diff --git a/docs/howto/deployment/wsgi/modwsgi.txt b/docs/howto/deployment/wsgi/modwsgi.txt
index ead04a4643..7749192358 100644
--- a/docs/howto/deployment/wsgi/modwsgi.txt
+++ b/docs/howto/deployment/wsgi/modwsgi.txt
@@ -18,8 +18,8 @@ The `official mod_wsgi documentation`_ is fantastic; it's your source for all
the details about how to use mod_wsgi. You'll probably want to start with the
`installation and configuration documentation`_.
-.. _official mod_wsgi documentation: http://www.modwsgi.org/
-.. _installation and configuration documentation: http://www.modwsgi.org/wiki/InstallationInstructions
+.. _official mod_wsgi documentation: http://code.google.com/p/modwsgi/
+.. _installation and configuration documentation: http://code.google.com/p/modwsgi/wiki/InstallationInstructions
Basic configuration
===================
diff --git a/docs/howto/deployment/wsgi/uwsgi.txt b/docs/howto/deployment/wsgi/uwsgi.txt
index b5d438450e..5b40d5f2f7 100644
--- a/docs/howto/deployment/wsgi/uwsgi.txt
+++ b/docs/howto/deployment/wsgi/uwsgi.txt
@@ -9,6 +9,14 @@ container server coded in pure C.
.. _uWSGI: http://projects.unbit.it/uwsgi/
+.. seealso::
+
+ The uWSGI docs offer a `tutorial`_ covering Django, nginx, and uWSGI (one
+ possible deployment setup of many). The docs below are focused on how to
+ integrate Django with uWSGI.
+
+ .. _tutorial: https://uwsgi.readthedocs.org/en/latest/tutorials/Django_and_nginx.html
+
Prerequisite: uWSGI
===================
diff --git a/docs/howto/error-reporting.txt b/docs/howto/error-reporting.txt
index 78e797b607..27f11f4936 100644
--- a/docs/howto/error-reporting.txt
+++ b/docs/howto/error-reporting.txt
@@ -39,8 +39,8 @@ By default, Django will send email from root@localhost. However, some mail
providers reject all email from this address. To use a different sender
address, modify the :setting:`SERVER_EMAIL` setting.
-To disable this behavior, just remove all entries from the :setting:`ADMINS`
-setting.
+To activate this behavior, put the email addresses of the recipients in the
+:setting:`ADMINS` setting.
.. seealso::
@@ -54,18 +54,24 @@ setting.
Django can also be configured to email errors about broken links (404 "page
not found" errors). Django sends emails about 404 errors when:
-* :setting:`DEBUG` is ``False``
+* :setting:`DEBUG` is ``False``;
-* :setting:`SEND_BROKEN_LINK_EMAILS` is ``True``
-
-* Your :setting:`MIDDLEWARE_CLASSES` setting includes ``CommonMiddleware``
- (which it does by default).
+* Your :setting:`MIDDLEWARE_CLASSES` setting includes
+ :class:`django.middleware.common.BrokenLinkEmailsMiddleware`.
If those conditions are met, Django will email the users listed in the
:setting:`MANAGERS` setting whenever your code raises a 404 and the request has
a referer. (It doesn't bother to email for 404s that don't have a referer --
those are usually just people typing in broken URLs or broken Web 'bots).
+.. note::
+
+ :class:`~django.middleware.common.BrokenLinkEmailsMiddleware` must appear
+ before other middleware that intercepts 404 errors, such as
+ :class:`~django.middleware.locale.LocaleMiddleware` or
+ :class:`~django.contrib.flatpages.middleware.FlatpageFallbackMiddleware`.
+ Put it towards the top of your :setting:`MIDDLEWARE_CLASSES` setting.
+
You can tell Django to stop reporting particular 404s by tweaking the
:setting:`IGNORABLE_404_URLS` setting. It should be a tuple of compiled
regular expression objects. For example::
@@ -92,30 +98,17 @@ crawlers often request::
(Note that these are regular expressions, so we put a backslash in front of
periods to escape them.)
-The best way to disable this behavior is to set
-:setting:`SEND_BROKEN_LINK_EMAILS` to ``False``.
-
.. seealso::
404 errors are logged using the logging framework. By default, these log
records are ignored, but you can use them for error reporting by writing a
handler and :doc:`configuring logging </topics/logging>` appropriately.
-.. seealso::
-
- .. versionchanged:: 1.4
-
- Previously, two settings were used to control which URLs not to report:
- :setting:`IGNORABLE_404_STARTS` and :setting:`IGNORABLE_404_ENDS`. They
- were replaced by :setting:`IGNORABLE_404_URLS`.
-
.. _filtering-error-reports:
Filtering error reports
-----------------------
-.. versionadded:: 1.4
-
Filtering sensitive information
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -123,7 +116,7 @@ Error reports are really helpful for debugging errors, so it is generally
useful to record as much relevant information about those errors as possible.
For example, by default Django records the `full traceback`_ for the
exception raised, each `traceback frame`_'s local variables, and the
-:class:`HttpRequest`'s :ref:`attributes<httprequest-attributes>`.
+:class:`~django.http.HttpRequest`'s :ref:`attributes<httprequest-attributes>`.
However, sometimes certain types of information may be too sensitive and thus
may not be appropriate to be kept track of, for example a user's password or
@@ -163,13 +156,27 @@ production environment (that is, where :setting:`DEBUG` is set to ``False``):
def my_function():
...
+ .. admonition:: When using mutiple decorators
+
+ If the variable you want to hide is also a function argument (e.g.
+ '``user``' in the following example), and if the decorated function has
+ mutiple decorators, then make sure to place ``@sensitive_variables`` at
+ the top of the decorator chain. This way it will also hide the function
+ argument as it gets passed through the other decorators::
+
+ @sensitive_variables('user', 'pw', 'cc')
+ @some_decorator
+ @another_decorator
+ def process_info(user):
+ ...
+
.. function:: sensitive_post_parameters(*parameters)
- If one of your views receives an :class:`HttpRequest` object with
- :attr:`POST parameters<HttpRequest.POST>` susceptible to contain sensitive
- information, you may prevent the values of those parameters from being
- included in the error reports using the ``sensitive_post_parameters``
- decorator::
+ If one of your views receives an :class:`~django.http.HttpRequest` object
+ with :attr:`POST parameters<django.http.HttpRequest.POST>` susceptible to
+ contain sensitive information, you may prevent the values of those
+ parameters from being included in the error reports using the
+ ``sensitive_post_parameters`` decorator::
from django.views.decorators.debug import sensitive_post_parameters
@@ -193,12 +200,8 @@ production environment (that is, where :setting:`DEBUG` is set to ``False``):
def my_view(request):
...
-.. note::
-
- .. versionchanged:: 1.4
-
- Since version 1.4, all POST parameters are systematically filtered out of
- error reports for certain :mod:`contrib.views.auth` views (``login``,
+ All POST parameters are systematically filtered out of error reports for
+ certain :mod:`django.contrib.auth.views` views (``login``,
``password_reset_confirm``, ``password_change``, and ``add_view`` and
``user_change_password`` in the ``auth`` admin) to prevent the leaking of
sensitive information such as user passwords.
diff --git a/docs/howto/index.txt b/docs/howto/index.txt
index d39222be26..9d5b067a82 100644
--- a/docs/howto/index.txt
+++ b/docs/howto/index.txt
@@ -21,7 +21,8 @@ you quickly accomplish common tasks.
legacy-databases
outputting-csv
outputting-pdf
- static-files
+ static-files/index
+ static-files/deployment
.. seealso::
diff --git a/docs/howto/initial-data.txt b/docs/howto/initial-data.txt
index eca2e2c4f9..cea07bfea3 100644
--- a/docs/howto/initial-data.txt
+++ b/docs/howto/initial-data.txt
@@ -153,8 +153,8 @@ each app, Django looks for a file called
``<appname>/sql/<modelname>.<backend>.sql``, where ``<appname>`` is
your app directory, ``<modelname>`` is the model's name in lowercase
and ``<backend>`` is the last part of the module name provided for the
-:setting:`ENGINE` in your settings file (e.g., if you have defined a
-database with an :setting:`ENGINE` value of
+:setting:`ENGINE <DATABASE-ENGINE>` in your settings file (e.g., if you have
+defined a database with an :setting:`ENGINE <DATABASE-ENGINE>` value of
``django.db.backends.sqlite3``, Django will look for
``<appname>/sql/<modelname>.sqlite3.sql``).
diff --git a/docs/howto/legacy-databases.txt b/docs/howto/legacy-databases.txt
index 1b04e6d77c..6846e4b2df 100644
--- a/docs/howto/legacy-databases.txt
+++ b/docs/howto/legacy-databases.txt
@@ -21,7 +21,7 @@ setting and assigning values to the following keys for the ``'default'``
connection:
* :setting:`NAME`
-* :setting:`ENGINE`
+* :setting:`ENGINE <DATABASE-ENGINE>`
* :setting:`USER`
* :setting:`PASSWORD`
* :setting:`HOST`
@@ -49,6 +49,35 @@ Once you've cleaned up your models, name the file ``models.py`` and put it in
the Python package that holds your app. Then add the app to your
:setting:`INSTALLED_APPS` setting.
+If your plan is that your Django application(s) modify data (i.e. edit, remove
+records and create new ones) in the existing database tables corresponding to
+any of the introspected models then one of the manual review and edit steps
+you need to perform on the resulting ``models.py`` file is to change the
+Python declaration of each one of these models to specify it is a
+:attr:`managed <django.db.models.Options.managed>` one. For example, consider
+this generated model definition:
+
+.. parsed-literal::
+
+ class Person(models.Model):
+ id = models.IntegerField(primary_key=True)
+ first_name = models.ChaField(max_length=70)
+ class Meta:
+ **managed = False**
+ db_table = 'CENSUS_PERSONS'
+
+If you wanted to modify existing data on your ``CENSUS_PERSONS`` SQL table
+with Django you'd need to change the ``managed`` option highlighted above to
+``True`` (or simply remove it to let it because ``True`` is its default value).
+
+This serves as an explicit opt-in to give your nascent Django project write
+access to your precious data on a model by model basis.
+
+.. versionchanged:: 1.6
+
+The behavior by which introspected models are created as unmanaged ones is new
+in Django 1.6.
+
Install the core Django tables
==============================
diff --git a/docs/howto/outputting-csv.txt b/docs/howto/outputting-csv.txt
index bcc6f3827b..1f9efb5a4b 100644
--- a/docs/howto/outputting-csv.txt
+++ b/docs/howto/outputting-csv.txt
@@ -20,7 +20,7 @@ Here's an example::
def some_view(request):
# Create the HttpResponse object with the appropriate CSV header.
- response = HttpResponse(mimetype='text/csv')
+ response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="somefilename.csv"'
writer = csv.writer(response)
@@ -92,7 +92,7 @@ Here's an example, which generates the same CSV file as above::
def some_view(request):
# Create the HttpResponse object with the appropriate CSV header.
- response = HttpResponse(mimetype='text/csv')
+ response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="somefilename.csv"'
# The data is hard-coded here, but you could load it from a database or
@@ -111,7 +111,7 @@ Here's an example, which generates the same CSV file as above::
The only difference between this example and the previous example is that this
one uses template loading instead of the CSV module. The rest of the code --
-such as the ``mimetype='text/csv'`` -- is the same.
+such as the ``content_type='text/csv'`` -- is the same.
Then, create the template ``my_template_name.txt``, with this template code:
diff --git a/docs/howto/outputting-pdf.txt b/docs/howto/outputting-pdf.txt
index 9d87b97710..d15f94f7f4 100644
--- a/docs/howto/outputting-pdf.txt
+++ b/docs/howto/outputting-pdf.txt
@@ -51,7 +51,7 @@ Here's a "Hello World" example::
def some_view(request):
# Create the HttpResponse object with the appropriate PDF headers.
- response = HttpResponse(mimetype='application/pdf')
+ response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="somefilename.pdf"'
# Create the PDF object, using the response object as its "file."
@@ -120,7 +120,7 @@ Here's the above "Hello World" example rewritten to use :mod:`io`::
def some_view(request):
# Create the HttpResponse object with the appropriate PDF headers.
- response = HttpResponse(mimetype='application/pdf')
+ response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="somefilename.pdf"'
buffer = BytesIO()
diff --git a/docs/howto/static-files.txt b/docs/howto/static-files.txt
deleted file mode 100644
index 964b5fab61..0000000000
--- a/docs/howto/static-files.txt
+++ /dev/null
@@ -1,508 +0,0 @@
-=====================
-Managing static files
-=====================
-
-Django developers mostly concern themselves with the dynamic parts of web
-applications -- the views and templates that render anew for each request. But
-web applications have other parts: the static files (images, CSS,
-Javascript, etc.) that are needed to render a complete web page.
-
-For small projects, this isn't a big deal, because you can just keep the
-static files somewhere your web server can find it. However, in bigger
-projects -- especially those comprised of multiple apps -- dealing with the
-multiple sets of static files provided by each application starts to get
-tricky.
-
-That's what ``django.contrib.staticfiles`` is for: it collects static files
-from each of your applications (and any other places you specify) into a
-single location that can easily be served in production.
-
-.. note::
-
- If you've used the `django-staticfiles`_ third-party app before, then
- ``django.contrib.staticfiles`` will look very familiar. That's because
- they're essentially the same code: ``django.contrib.staticfiles`` started
- its life as `django-staticfiles`_ and was merged into Django 1.3.
-
- If you're upgrading from ``django-staticfiles``, please see `Upgrading from
- django-staticfiles`_, below, for a few minor changes you'll need to make.
-
-.. _django-staticfiles: http://pypi.python.org/pypi/django-staticfiles/
-
-Using ``django.contrib.staticfiles``
-====================================
-
-Basic usage
------------
-
-1. Put your static files somewhere that ``staticfiles`` will find them.
-
- By default, this means within ``static/`` subdirectories of apps in your
- :setting:`INSTALLED_APPS`.
-
- Your project will probably also have static assets that aren't tied to a
- particular app. The :setting:`STATICFILES_DIRS` setting is a tuple of
- filesystem directories to check when loading static files. It's a search
- path that is by default empty. See the :setting:`STATICFILES_DIRS` docs
- how to extend this list of additional paths.
-
- Additionally, see the documentation for the :setting:`STATICFILES_FINDERS`
- setting for details on how ``staticfiles`` finds your files.
-
-2. Make sure that ``django.contrib.staticfiles`` is included in your
- :setting:`INSTALLED_APPS`.
-
- For :ref:`local development<staticfiles-development>`, if you are using
- :ref:`runserver<staticfiles-runserver>` or adding
- :ref:`staticfiles_urlpatterns<staticfiles-development>` to your
- URLconf, you're done with the setup -- your static files will
- automatically be served at the default (for
- :djadmin:`newly created<startproject>` projects) :setting:`STATIC_URL`
- of ``/static/``.
-
-3. You'll probably need to refer to these files in your templates. The
- easiest method is to use the included context processor which allows
- template code like:
-
- .. code-block:: html+django
-
- <img src="{{ STATIC_URL }}images/hi.jpg" alt="Hi!" />
-
- See :ref:`staticfiles-in-templates` for more details, **including** an
- alternate method using a template tag.
-
-Deploying static files in a nutshell
-------------------------------------
-
-When you're ready to move out of local development and deploy your project:
-
-1. Set the :setting:`STATIC_URL` setting to the public URL for your static
- files (in most cases, the default value of ``/static/`` is just fine).
-
-2. Set the :setting:`STATIC_ROOT` setting to point to the filesystem path
- you'd like your static files collected to when you use the
- :djadmin:`collectstatic` management command. For example::
-
- STATIC_ROOT = "/home/jacob/projects/mysite.com/sitestatic"
-
-3. Run the :djadmin:`collectstatic` management command::
-
- ./manage.py collectstatic
-
- This'll churn through your static file storage and copy them into the
- directory given by :setting:`STATIC_ROOT`.
-
-4. Deploy those files by configuring your webserver of choice to serve the
- files in :setting:`STATIC_ROOT` at :setting:`STATIC_URL`.
-
- :ref:`staticfiles-production` covers some common deployment strategies
- for static files.
-
-Those are the **basics**. For more details on common configuration options,
-read on; for a detailed reference of the settings, commands, and other bits
-included with the framework see
-:doc:`the staticfiles reference </ref/contrib/staticfiles>`.
-
-.. note::
-
- In previous versions of Django, it was common to place static assets in
- :setting:`MEDIA_ROOT` along with user-uploaded files, and serve them both
- at :setting:`MEDIA_URL`. Part of the purpose of introducing the
- ``staticfiles`` app is to make it easier to keep static files separate
- from user-uploaded files.
-
- For this reason, you need to make your :setting:`MEDIA_ROOT` and
- :setting:`MEDIA_URL` different from your :setting:`STATIC_ROOT` and
- :setting:`STATIC_URL`. You will need to arrange for serving of files in
- :setting:`MEDIA_ROOT` yourself; ``staticfiles`` does not deal with
- user-uploaded files at all. You can, however, use
- :func:`django.views.static.serve` view for serving :setting:`MEDIA_ROOT`
- in development; see :ref:`staticfiles-other-directories`.
-
-.. _staticfiles-in-templates:
-
-Referring to static files in templates
-======================================
-
-At some point, you'll probably need to link to static files in your templates.
-You could, of course, simply hardcode the path to you assets in the templates:
-
-.. code-block:: html
-
- <img src="http://static.example.com/static/myimage.jpg" alt="Sample image" />
-
-Of course, there are some serious problems with this: it doesn't work well in
-development, and it makes it *very* hard to change where you've deployed your
-static files. If, for example, you wanted to switch to using a content
-delivery network (CDN), then you'd need to change more or less every single
-template.
-
-A far better way is to use the value of the :setting:`STATIC_URL` setting
-directly in your templates. This means that a switch of static files servers
-only requires changing that single value. Much better!
-
-Django includes multiple built-in ways of using this setting in your
-templates: a context processor and a template tag.
-
-With a context processor
-------------------------
-
-The included context processor is the easy way. Simply make sure
-``'django.core.context_processors.static'`` is in your
-:setting:`TEMPLATE_CONTEXT_PROCESSORS`. It's there by default, and if you're
-editing that setting by hand it should look something like::
-
- TEMPLATE_CONTEXT_PROCESSORS = (
- 'django.core.context_processors.debug',
- 'django.core.context_processors.i18n',
- 'django.core.context_processors.media',
- 'django.core.context_processors.static',
- 'django.contrib.auth.context_processors.auth',
- 'django.contrib.messages.context_processors.messages',
- )
-
-Once that's done, you can refer to :setting:`STATIC_URL` in your templates:
-
-.. code-block:: html+django
-
- <img src="{{ STATIC_URL }}images/hi.jpg" alt="Hi!" />
-
-If ``{{ STATIC_URL }}`` isn't working in your template, you're probably not
-using :class:`~django.template.RequestContext` when rendering the template.
-
-As a brief refresher, context processors add variables into the contexts of
-every template. However, context processors require that you use
-:class:`~django.template.RequestContext` when rendering templates. This happens
-automatically if you're using a :doc:`generic view </ref/class-based-views/index>`,
-but in views written by hand you'll need to explicitly use ``RequestContext``
-To see how that works, and to read more details, check out
-:ref:`subclassing-context-requestcontext`.
-
-Another option is the :ttag:`get_static_prefix` template tag that is part of
-Django's core.
-
-With a template tag
--------------------
-
-The more powerful tool is the :ttag:`static<staticfiles-static>` template
-tag. It builds the URL for the given relative path by using the configured
-:setting:`STATICFILES_STORAGE` storage.
-
-.. code-block:: html+django
-
- {% load staticfiles %}
- <img src="{% static "images/hi.jpg" %}" alt="Hi!"/>
-
-It is also able to consume standard context variables, e.g. assuming a
-``user_stylesheet`` variable is passed to the template:
-
-.. code-block:: html+django
-
- {% load staticfiles %}
- <link rel="stylesheet" href="{% static user_stylesheet %}" type="text/css" media="screen" />
-
-.. note::
-
- There is also a template tag named :ttag:`static` in Django's core set
- of :ref:`built in template tags<ref-templates-builtins-tags>` which has
- the same argument signature but only uses `urlparse.urljoin()`_ with the
- :setting:`STATIC_URL` setting and the given path. This has the
- disadvantage of not being able to easily switch the storage backend
- without changing the templates, so in doubt use the ``staticfiles``
- :ttag:`static<staticfiles-static>`
- template tag.
-
-.. _`urlparse.urljoin()`: http://docs.python.org/library/urlparse.html#urlparse.urljoin
-
-.. _staticfiles-development:
-
-Serving static files in development
-===================================
-
-The static files tools are mostly designed to help with getting static files
-successfully deployed into production. This usually means a separate,
-dedicated static file server, which is a lot of overhead to mess with when
-developing locally. Thus, the ``staticfiles`` app ships with a
-**quick and dirty helper view** that you can use to serve files locally in
-development.
-
-This view is automatically enabled and will serve your static files at
-:setting:`STATIC_URL` when you use the built-in
-:ref:`runserver<staticfiles-runserver>` management command.
-
-To enable this view if you are using some other server for local development,
-you'll add a couple of lines to your URLconf. The first line goes at the top
-of the file, and the last line at the bottom::
-
- from django.contrib.staticfiles.urls import staticfiles_urlpatterns
-
- # ... the rest of your URLconf goes here ...
-
- urlpatterns += staticfiles_urlpatterns()
-
-This will inspect your :setting:`STATIC_URL` setting and wire up the view
-to serve static files accordingly. Don't forget to set the
-:setting:`STATICFILES_DIRS` setting appropriately to let
-``django.contrib.staticfiles`` know where to look for files additionally to
-files in app directories.
-
-.. warning::
-
- This will only work if :setting:`DEBUG` is ``True``.
-
- That's because this view is **grossly inefficient** and probably
- **insecure**. This is only intended for local development, and should
- **never be used in production**.
-
- Additionally, when using ``staticfiles_urlpatterns`` your
- :setting:`STATIC_URL` setting can't be empty or a full URL, such as
- ``http://static.example.com/``.
-
-For a few more details on how the ``staticfiles`` can be used during
-development, see :ref:`staticfiles-development-view`.
-
-.. _staticfiles-other-directories:
-
-Serving other directories
--------------------------
-
-.. currentmodule:: django.views.static
-.. function:: serve(request, path, document_root, show_indexes=False)
-
-There may be files other than your project's static assets that, for
-convenience, you'd like to have Django serve for you in local development.
-The :func:`~django.views.static.serve` view can be used to serve any directory
-you give it. (Again, this view is **not** hardened for production
-use, and should be used only as a development aid; you should serve these files
-in production using a real front-end webserver).
-
-The most likely example is user-uploaded content in :setting:`MEDIA_ROOT`.
-``staticfiles`` is intended for static assets and has no built-in handling
-for user-uploaded files, but you can have Django serve your
-:setting:`MEDIA_ROOT` by appending something like this to your URLconf::
-
- from django.conf import settings
-
- # ... the rest of your URLconf goes here ...
-
- if settings.DEBUG:
- urlpatterns += patterns('',
- url(r'^media/(?P<path>.*)$', 'django.views.static.serve', {
- 'document_root': settings.MEDIA_ROOT,
- }),
- )
-
-Note, the snippet assumes your :setting:`MEDIA_URL` has a value of
-``'/media/'``. This will call the :func:`~django.views.static.serve` view,
-passing in the path from the URLconf and the (required) ``document_root``
-parameter.
-
-.. currentmodule:: django.conf.urls.static
-.. function:: static(prefix, view='django.views.static.serve', **kwargs)
-
-Since it can become a bit cumbersome to define this URL pattern, Django
-ships with a small URL helper function
-:func:`~django.conf.urls.static.static` that takes as parameters the prefix
-such as :setting:`MEDIA_URL` and a dotted path to a view, such as
-``'django.views.static.serve'``. Any other function parameter will be
-transparently passed to the view.
-
-An example for serving :setting:`MEDIA_URL` (``'/media/'``) during
-development::
-
- from django.conf import settings
- from django.conf.urls.static import static
-
- urlpatterns = patterns('',
- # ... the rest of your URLconf goes here ...
- ) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
-
-.. note::
-
- This helper function will only be operational in debug mode and if
- the given prefix is local (e.g. ``/static/``) and not a URL (e.g.
- ``http://static.example.com/``).
-
-.. _staticfiles-production:
-
-Serving static files in production
-==================================
-
-The basic outline of putting static files into production is simple: run the
-:djadmin:`collectstatic` command when static files change, then arrange for
-the collected static files directory (:setting:`STATIC_ROOT`) to be moved to
-the static file server and served.
-
-Of course, as with all deployment tasks, the devil's in the details. Every
-production setup will be a bit different, so you'll need to adapt the basic
-outline to fit your needs. Below are a few common patterns that might help.
-
-Serving the app and your static files from the same server
-----------------------------------------------------------
-
-If you want to serve your static files from the same server that's already
-serving your site, the basic outline gets modified to look something like:
-
-* Push your code up to the deployment server.
-* On the server, run :djadmin:`collectstatic` to copy all the static files
- into :setting:`STATIC_ROOT`.
-* Point your web server at :setting:`STATIC_ROOT`. For example, here's
- :ref:`how to do this under Apache and mod_wsgi <serving-files>`.
-
-You'll probably want to automate this process, especially if you've got
-multiple web servers. There's any number of ways to do this automation, but
-one option that many Django developers enjoy is `Fabric`__.
-
-__ http://fabfile.org/
-
-Below, and in the following sections, we'll show off a few example fabfiles
-(i.e. Fabric scripts) that automate these file deployment options. The syntax
-of a fabfile is fairly straightforward but won't be covered here; consult
-`Fabric's documentation`__, for a complete explanation of the syntax..
-
-__ http://docs.fabfile.org/
-
-So, a fabfile to deploy static files to a couple of web servers might look
-something like::
-
- from fabric.api import *
-
- # Hosts to deploy onto
- env.hosts = ['www1.example.com', 'www2.example.com']
-
- # Where your project code lives on the server
- env.project_root = '/home/www/myproject'
-
- def deploy_static():
- with cd(env.project_root):
- run('./manage.py collectstatic -v0 --noinput')
-
-Serving static files from a dedicated server
---------------------------------------------
-
-Most larger Django apps use a separate Web server -- i.e., one that's not also
-running Django -- for serving static files. This server often runs a different
-type of web server -- faster but less full-featured. Some good choices are:
-
-* lighttpd_
-* Nginx_
-* TUX_
-* Cherokee_
-* A stripped-down version of Apache_
-
-.. _lighttpd: http://www.lighttpd.net/
-.. _Nginx: http://wiki.nginx.org/Main
-.. _TUX: http://en.wikipedia.org/wiki/TUX_web_server
-.. _Apache: http://httpd.apache.org/
-.. _Cherokee: http://www.cherokee-project.com/
-
-Configuring these servers is out of scope of this document; check each
-server's respective documentation for instructions.
-
-Since your static file server won't be running Django, you'll need to modify
-the deployment strategy to look something like:
-
-* When your static files change, run :djadmin:`collectstatic` locally.
-* Push your local :setting:`STATIC_ROOT` up to the static file server
- into the directory that's being served. ``rsync`` is a good
- choice for this step since it only needs to transfer the
- bits of static files that have changed.
-
-Here's how this might look in a fabfile::
-
- from fabric.api import *
- from fabric.contrib import project
-
- # Where the static files get collected locally
- env.local_static_root = '/tmp/static'
-
- # Where the static files should go remotely
- env.remote_static_root = '/home/www/static.example.com'
-
- @roles('static')
- def deploy_static():
- local('./manage.py collectstatic')
- project.rsync_project(
- remote_dir = env.remote_static_root,
- local_dir = env.local_static_root,
- delete = True
- )
-
-.. _staticfiles-from-cdn:
-
-Serving static files from a cloud service or CDN
-------------------------------------------------
-
-Another common tactic is to serve static files from a cloud storage provider
-like Amazon's S3__ and/or a CDN (content delivery network). This lets you
-ignore the problems of serving static files, and can often make for
-faster-loading webpages (especially when using a CDN).
-
-When using these services, the basic workflow would look a bit like the above,
-except that instead of using ``rsync`` to transfer your static files to the
-server you'd need to transfer the static files to the storage provider or CDN.
-
-There's any number of ways you might do this, but if the provider has an API a
-:doc:`custom file storage backend </howto/custom-file-storage>` will make the
-process incredibly simple. If you've written or are using a 3rd party custom
-storage backend, you can tell :djadmin:`collectstatic` to use it by setting
-:setting:`STATICFILES_STORAGE` to the storage engine.
-
-For example, if you've written an S3 storage backend in
-``myproject.storage.S3Storage`` you could use it with::
-
- STATICFILES_STORAGE = 'myproject.storage.S3Storage'
-
-Once that's done, all you have to do is run :djadmin:`collectstatic` and your
-static files would be pushed through your storage package up to S3. If you
-later needed to switch to a different storage provider, it could be as simple
-as changing your :setting:`STATICFILES_STORAGE` setting.
-
-For details on how you'd write one of these backends,
-:doc:`/howto/custom-file-storage`.
-
-.. seealso::
-
- The `django-storages`__ project is a 3rd party app that provides many
- storage backends for many common file storage APIs (including `S3`__).
-
-__ http://s3.amazonaws.com/
-__ http://code.larlet.fr/django-storages/
-__ http://django-storages.readthedocs.org/en/latest/backends/amazon-S3.html
-
-Upgrading from ``django-staticfiles``
-=====================================
-
-``django.contrib.staticfiles`` began its life as `django-staticfiles`_. If
-you're upgrading from `django-staticfiles`_ older than 1.0 (e.g. 0.3.4) to
-``django.contrib.staticfiles``, you'll need to make a few changes:
-
-* Application files should now live in a ``static`` directory in each app
- (`django-staticfiles`_ used the name ``media``, which was slightly
- confusing).
-
-* The management commands ``build_static`` and ``resolve_static`` are now
- called :djadmin:`collectstatic` and :djadmin:`findstatic`.
-
-* The settings ``STATICFILES_PREPEND_LABEL_APPS``,
- ``STATICFILES_MEDIA_DIRNAMES`` and ``STATICFILES_EXCLUDED_APPS`` were
- removed.
-
-* The setting ``STATICFILES_RESOLVERS`` was removed, and replaced by the
- new :setting:`STATICFILES_FINDERS`.
-
-* The default for :setting:`STATICFILES_STORAGE` was renamed from
- ``staticfiles.storage.StaticFileStorage`` to
- ``staticfiles.storage.StaticFilesStorage``
-
-* If using :ref:`runserver<staticfiles-runserver>` for local development
- (and the :setting:`DEBUG` setting is ``True``), you no longer need to add
- anything to your URLconf for serving static files in development.
-
-Learn more
-==========
-
-This document has covered the basics and some common usage patterns. For
-complete details on all the settings, commands, template tags, and other pieces
-include in ``django.contrib.staticfiles``, see :doc:`the staticfiles reference
-</ref/contrib/staticfiles>`.
diff --git a/docs/howto/static-files/deployment.txt b/docs/howto/static-files/deployment.txt
new file mode 100644
index 0000000000..865a5c6b41
--- /dev/null
+++ b/docs/howto/static-files/deployment.txt
@@ -0,0 +1,159 @@
+======================
+Deploying static files
+======================
+
+.. seealso::
+
+ For an introduction to the use of :mod:`django.contrib.staticfiles`, see
+ :doc:`/howto/static-files/index`.
+
+.. _staticfiles-production:
+
+Serving static files in production
+==================================
+
+The basic outline of putting static files into production is simple: run the
+:djadmin:`collectstatic` command when static files change, then arrange for
+the collected static files directory (:setting:`STATIC_ROOT`) to be moved to
+the static file server and served. Depending on :setting:`STATICFILES_STORAGE`,
+files may need to be moved to a new location manually or the :func:`post_process
+<django.contrib.staticfiles.storage.StaticFilesStorage.post_process>` method
+of the ``Storage`` class might take care of that.
+
+Of course, as with all deployment tasks, the devil's in the details. Every
+production setup will be a bit different, so you'll need to adapt the basic
+outline to fit your needs. Below are a few common patterns that might help.
+
+Serving the site and your static files from the same server
+-----------------------------------------------------------
+
+If you want to serve your static files from the same server that's already
+serving your site, the process may look something like:
+
+* Push your code up to the deployment server.
+* On the server, run :djadmin:`collectstatic` to copy all the static files
+ into :setting:`STATIC_ROOT`.
+* Configure your web server to serve the files in :setting:`STATIC_ROOT`
+ under the URL :setting:`STATIC_URL`. For example, here's
+ :ref:`how to do this with Apache and mod_wsgi <serving-files>`.
+
+You'll probably want to automate this process, especially if you've got
+multiple web servers. There's any number of ways to do this automation, but
+one option that many Django developers enjoy is `Fabric
+<http://fabfile.org/>`_.
+
+Below, and in the following sections, we'll show off a few example fabfiles
+(i.e. Fabric scripts) that automate these file deployment options. The syntax
+of a fabfile is fairly straightforward but won't be covered here; consult
+`Fabric's documentation <http://docs.fabfile.org/>`_, for a complete
+explanation of the syntax.
+
+So, a fabfile to deploy static files to a couple of web servers might look
+something like::
+
+ from fabric.api import *
+
+ # Hosts to deploy onto
+ env.hosts = ['www1.example.com', 'www2.example.com']
+
+ # Where your project code lives on the server
+ env.project_root = '/home/www/myproject'
+
+ def deploy_static():
+ with cd(env.project_root):
+ run('./manage.py collectstatic -v0 --noinput')
+
+Serving static files from a dedicated server
+--------------------------------------------
+
+Most larger Django sites use a separate Web server -- i.e., one that's not also
+running Django -- for serving static files. This server often runs a different
+type of web server -- faster but less full-featured. Some common choices are:
+
+* lighttpd_
+* Nginx_
+* TUX_
+* Cherokee_
+* A stripped-down version of Apache_
+
+.. _lighttpd: http://www.lighttpd.net/
+.. _Nginx: http://wiki.nginx.org/Main
+.. _TUX: http://en.wikipedia.org/wiki/TUX_web_server
+.. _Apache: http://httpd.apache.org/
+.. _Cherokee: http://www.cherokee-project.com/
+
+Configuring these servers is out of scope of this document; check each
+server's respective documentation for instructions.
+
+Since your static file server won't be running Django, you'll need to modify
+the deployment strategy to look something like:
+
+* When your static files change, run :djadmin:`collectstatic` locally.
+
+* Push your local :setting:`STATIC_ROOT` up to the static file server into the
+ directory that's being served. `rsync <https://rsync.samba.org/>`_ is a
+ common choice for this step since it only needs to transfer the bits of
+ static files that have changed.
+
+Here's how this might look in a fabfile::
+
+ from fabric.api import *
+ from fabric.contrib import project
+
+ # Where the static files get collected locally. Your STATIC_ROOT setting.
+ env.local_static_root = '/tmp/static'
+
+ # Where the static files should go remotely
+ env.remote_static_root = '/home/www/static.example.com'
+
+ @roles('static')
+ def deploy_static():
+ local('./manage.py collectstatic')
+ project.rsync_project(
+ remote_dir = env.remote_static_root,
+ local_dir = env.local_static_root,
+ delete = True
+ )
+
+.. _staticfiles-from-cdn:
+
+Serving static files from a cloud service or CDN
+------------------------------------------------
+
+Another common tactic is to serve static files from a cloud storage provider
+like Amazon's S3 and/or a CDN (content delivery network). This lets you
+ignore the problems of serving static files and can often make for
+faster-loading webpages (especially when using a CDN).
+
+When using these services, the basic workflow would look a bit like the above,
+except that instead of using ``rsync`` to transfer your static files to the
+server you'd need to transfer the static files to the storage provider or CDN.
+
+There's any number of ways you might do this, but if the provider has an API a
+:doc:`custom file storage backend </howto/custom-file-storage>` will make the
+process incredibly simple. If you've written or are using a 3rd party custom
+storage backend, you can tell :djadmin:`collectstatic` to use it by setting
+:setting:`STATICFILES_STORAGE` to the storage engine.
+
+For example, if you've written an S3 storage backend in
+``myproject.storage.S3Storage`` you could use it with::
+
+ STATICFILES_STORAGE = 'myproject.storage.S3Storage'
+
+Once that's done, all you have to do is run :djadmin:`collectstatic` and your
+static files would be pushed through your storage package up to S3. If you
+later needed to switch to a different storage provider, it could be as simple
+as changing your :setting:`STATICFILES_STORAGE` setting.
+
+For details on how you'd write one of these backends, see
+:doc:`/howto/custom-file-storage`. There are 3rd party apps available that
+provide storage backends for many common file storage APIs. A good starting
+point is the `overview at djangopackages.com
+<https://www.djangopackages.com/grids/g/storage-backends/>`_.
+
+Learn more
+==========
+
+For complete details on all the settings, commands, template tags, and other
+pieces included in :mod:`django.contrib.staticfiles`, see :doc:`the
+staticfiles reference </ref/contrib/staticfiles>`.
diff --git a/docs/howto/static-files/index.txt b/docs/howto/static-files/index.txt
new file mode 100644
index 0000000000..2c98566e88
--- /dev/null
+++ b/docs/howto/static-files/index.txt
@@ -0,0 +1,119 @@
+===================================
+Managing static files (CSS, images)
+===================================
+
+Websites generally need to serve additional files such as images, JavaScript,
+or CSS. In Django, we refer to these files as "static files". Django provides
+:mod:`django.contrib.staticfiles` to help you manage them.
+
+This page describes how you can serve these static files.
+
+Configuring static files
+========================
+
+1. Make sure that ``django.contrib.staticfiles`` is included in your
+ :setting:`INSTALLED_APPS`.
+
+2. In your settings file, define :setting:`STATIC_URL`, for example::
+
+ STATIC_URL = '/static/'
+
+3. In your templates, either hardcode the url like
+ ``/static/my_app/myexample.jpg`` or, preferably, use the
+ :ttag:`static<staticfiles-static>` template tag to build the URL for the given
+ relative path by using the configured :setting:`STATICFILES_STORAGE` storage
+ (this makes it much easier when you want to switch to a content delivery
+ network (CDN) for serving static files).
+
+ .. _staticfiles-in-templates:
+
+ .. code-block:: html+django
+
+ {% load staticfiles %}
+ <img src="{% static "my_app/myexample.jpg" %}" alt="My image"/>
+
+3. Store your static files in a folder called ``static`` in your app. For
+ example ``my_app/static/my_app/myimage.jpg``.
+
+Now, if you use ``./manage.py runserver``, all static files should be served
+automatically at the :setting:`STATIC_URL` and be shown correctly.
+
+Your project will probably also have static assets that aren't tied to a
+particular app. In addition to using a ``static/`` directory inside your apps,
+you can define a list of directories (:setting:`STATICFILES_DIRS`) in your
+settings file where Django will also look for static files. For example::
+
+ STATICFILES_DIRS = (
+ os.path.join(BASE_DIR, "static"),
+ '/var/www/static/',
+ )
+
+See the documentation for the :setting:`STATICFILES_FINDERS` setting for
+details on how ``staticfiles`` finds your files.
+
+.. admonition:: Static file namespacing
+
+ Now we *might* be able to get away with putting our static files directly
+ in ``my_app/static/`` (rather than creating another ``my_app``
+ subdirectory), but it would actually be a bad idea. Django will use the
+ last static file it finds whose name matches, and if you had a static file
+ with the same name in a *different* application, Django would be unable to
+ distinguish between them. We need to be able to point Django at the right
+ one, and the easiest way to ensure this is by *namespacing* them. That is,
+ by putting those static files inside *another* directory named for the
+ application itself.
+
+
+Serving files uploaded by a user
+================================
+
+During development, you can serve user-uploaded media files from
+:setting:`MEDIA_ROOT` using the :func:`django.contrib.staticfiles.views.serve`
+view. This is not suitable for production use! For some common deployment
+strategies, see :doc:`/howto/static-files/deployment`.
+
+For example, if your :setting:`MEDIA_URL` is defined as '/media/', you can do
+this by adding the following snippet to your urls.py::
+
+ from django.conf import settings
+ from django.conf.urls.static import static
+
+ urlpatterns = patterns('',
+ # ... the rest of your URLconf goes here ...
+ ) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
+
+.. note::
+
+ This helper function works only in debug mode and only if
+ the given prefix is local (e.g. ``/static/``) and not a URL (e.g.
+ ``http://static.example.com/``).
+
+Deployment
+==========
+
+:mod:`django.contrib.staticfiles` provides a convenience management command
+for gathering static files in a single directory so you can serve them easily.
+
+1. Set the :setting:`STATIC_ROOT` setting to the directory from which you'd
+ like to serve these files, for example::
+
+ STATIC_ROOT = "/var/www/example.com/static/"
+
+2. Run the :djadmin:`collectstatic` management command::
+
+ ./manage.py collectstatic
+
+ This will copy all files from your static folders into the
+ :setting:`STATIC_ROOT` directory.
+
+3. Use a webserver of your choice to serve the
+ files. :doc:`/howto/static-files/deployment` covers some common deployment
+ strategies for static files.
+
+Learn more
+==========
+
+This document has covered the basics and some common usage patterns. For
+complete details on all the settings, commands, template tags, and other pieces
+included in :mod:`django.contrib.staticfiles`, see :doc:`the staticfiles
+reference </ref/contrib/staticfiles>`.
diff --git a/docs/index.txt b/docs/index.txt
index 9fea8ff3f2..6473aa3168 100644
--- a/docs/index.txt
+++ b/docs/index.txt
@@ -45,7 +45,8 @@ Are you new to Django or to programming? This is the place to start!
:doc:`Part 2 <intro/tutorial02>` |
:doc:`Part 3 <intro/tutorial03>` |
:doc:`Part 4 <intro/tutorial04>` |
- :doc:`Part 5 <intro/tutorial05>`
+ :doc:`Part 5 <intro/tutorial05>` |
+ :doc:`Part 6 <intro/tutorial06>`
* **Advanced Tutorials:**
:doc:`How to write reusable apps <intro/reusable-apps>` |
@@ -98,6 +99,7 @@ to know about views via the links below:
:doc:`Decorators <topics/http/decorators>`
* **Reference:**
+ :doc:`Built-in Views <ref/views>` |
:doc:`Request/response objects <ref/request-response>` |
:doc:`TemplateResponse objects <ref/template-response>`
@@ -180,13 +182,17 @@ testing of Django applications:
:doc:`Overview <ref/django-admin>` |
:doc:`Adding custom commands <howto/custom-management-commands>`
-* **Testing:** :doc:`Overview <topics/testing>`
+* **Testing:**
+ :doc:`Introduction <topics/testing/index>` |
+ :doc:`Writing and running tests <topics/testing/overview>` |
+ :doc:`Advanced topics <topics/testing/advanced>` |
+ :doc:`Doctests <topics/testing/doctests>`
* **Deployment:**
:doc:`Overview <howto/deployment/index>` |
:doc:`WSGI servers <howto/deployment/wsgi/index>` |
:doc:`FastCGI/SCGI/AJP <howto/deployment/fastcgi>` |
- :doc:`Handling static files <howto/static-files>` |
+ :doc:`Deploying static files <howto/static-files/deployment>` |
:doc:`Tracking code errors by email <howto/error-reporting>`
The admin
@@ -217,8 +223,11 @@ Django offers a robust internationalization and localization framework to
assist you in the development of applications for multiple languages and world
regions:
-* :doc:`Internationalization <topics/i18n/index>`
-* :doc:`"Local flavor" <ref/contrib/localflavor>`
+* :doc:`Overview <topics/i18n/index>` |
+ :doc:`Internationalization <topics/i18n/translation>` |
+ :ref:`Localization <how-to-create-language-files>`
+* :doc:`"Local flavor" <topics/localflavor>`
+* :doc:`Time zones </topics/i18n/timezones>`
Python compatibility
====================
@@ -242,12 +251,11 @@ Common Web application tools
Django offers multiple tools commonly needed in the development of Web
applications:
-* :doc:`Authentication <topics/auth>`
+* :doc:`Authentication <topics/auth/index>`
* :doc:`Caching <topics/cache>`
* :doc:`Logging <topics/logging>`
* :doc:`Sending emails <topics/email>`
* :doc:`Syndication feeds (RSS/Atom) <ref/contrib/syndication>`
-* :doc:`Comments <ref/contrib/comments/index>`, :doc:`comment moderation <ref/contrib/comments/moderation>` and :doc:`custom comments <ref/contrib/comments/custom>`
* :doc:`Pagination <topics/pagination>`
* :doc:`Messages framework <ref/contrib/messages>`
* :doc:`Serialization <topics/serialization>`
@@ -263,7 +271,6 @@ Learn about some other core functionalities of the Django framework:
* :doc:`Conditional content processing <topics/conditional-view-processing>`
* :doc:`Content types and generic relations <ref/contrib/contenttypes>`
-* :doc:`Databrowse <ref/contrib/databrowse>`
* :doc:`Flatpages <ref/contrib/flatpages>`
* :doc:`Redirects <ref/contrib/redirects>`
* :doc:`Signals <topics/signals>`
diff --git a/docs/internals/_images/djangotickets.png b/docs/internals/_images/djangotickets.png
deleted file mode 100644
index 34a2a41852..0000000000
--- a/docs/internals/_images/djangotickets.png
+++ /dev/null
Binary files differ
diff --git a/docs/internals/_images/triage_process.graffle b/docs/internals/_images/triage_process.graffle
new file mode 100644
index 0000000000..291c0397f7
--- /dev/null
+++ b/docs/internals/_images/triage_process.graffle
@@ -0,0 +1,2141 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>ActiveLayerIndex</key>
+ <integer>0</integer>
+ <key>ApplicationVersion</key>
+ <array>
+ <string>com.omnigroup.OmniGrafflePro</string>
+ <string>139.16.0.171715</string>
+ </array>
+ <key>AutoAdjust</key>
+ <true/>
+ <key>BackgroundGraphic</key>
+ <dict>
+ <key>Bounds</key>
+ <string>{{0, 0}, {559.28997802734375, 782.8900146484375}}</string>
+ <key>Class</key>
+ <string>SolidGraphic</string>
+ <key>ID</key>
+ <integer>2</integer>
+ <key>Style</key>
+ <dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ </dict>
+ <key>BaseZoom</key>
+ <integer>0</integer>
+ <key>CanvasOrigin</key>
+ <string>{0, 0}</string>
+ <key>ColumnAlign</key>
+ <integer>1</integer>
+ <key>ColumnSpacing</key>
+ <real>36</real>
+ <key>CreationDate</key>
+ <string>2012-12-22 15:48:38 +0000</string>
+ <key>Creator</key>
+ <string>Aymeric Augustin</string>
+ <key>DisplayScale</key>
+ <string>1.000 cm = 1.000 cm</string>
+ <key>GraphDocumentVersion</key>
+ <integer>8</integer>
+ <key>GraphicsList</key>
+ <array>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>Head</key>
+ <dict>
+ <key>ID</key>
+ <integer>132</integer>
+ </dict>
+ <key>ID</key>
+ <integer>151</integer>
+ <key>Points</key>
+ <array>
+ <string>{252, 288}</string>
+ <string>{315, 324}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0.501961</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>HeadArrow</key>
+ <string>FilledArrow</string>
+ <key>Legacy</key>
+ <true/>
+ <key>TailArrow</key>
+ <string>0</string>
+ <key>Width</key>
+ <real>2</real>
+ </dict>
+ </dict>
+ <key>Tail</key>
+ <dict>
+ <key>ID</key>
+ <integer>82</integer>
+ <key>Info</key>
+ <integer>1</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>ID</key>
+ <integer>104</integer>
+ <key>OrthogonalBarAutomatic</key>
+ <true/>
+ <key>OrthogonalBarPoint</key>
+ <string>{0, 0}</string>
+ <key>OrthogonalBarPosition</key>
+ <real>-1</real>
+ <key>Points</key>
+ <array>
+ <string>{134.4999955076145, 414}</string>
+ <string>{90, 414}</string>
+ <string>{81, 522}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.6</string>
+ <key>g</key>
+ <string>0.6</string>
+ <key>r</key>
+ <string>0.6</string>
+ </dict>
+ <key>HeadArrow</key>
+ <string>0</string>
+ <key>Legacy</key>
+ <true/>
+ <key>LineType</key>
+ <integer>2</integer>
+ <key>Pattern</key>
+ <integer>1</integer>
+ <key>TailArrow</key>
+ <string>0</string>
+ </dict>
+ </dict>
+ <key>Tail</key>
+ <dict>
+ <key>ID</key>
+ <integer>103</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{135, 405}, {18, 18}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>ID</key>
+ <integer>103</integer>
+ <key>Shape</key>
+ <string>Circle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.6</string>
+ <key>g</key>
+ <string>0.6</string>
+ <key>r</key>
+ <string>0.6</string>
+ </dict>
+ <key>Pattern</key>
+ <integer>1</integer>
+ </dict>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{72, 522}, {342, 36}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>HFlip</key>
+ <string>YES</string>
+ <key>ID</key>
+ <integer>102</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.6</string>
+ <key>g</key>
+ <string>0.6</string>
+ <key>r</key>
+ <string>0.6</string>
+ </dict>
+ <key>Pattern</key>
+ <integer>1</integer>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Pad</key>
+ <integer>4</integer>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;\red102\green102\blue102;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
+
+\f0\i\fs24 \cf2 The ticket has a patch which applies cleanly and includes all needed tests and docs. A core developer can commit it as is.}</string>
+ </dict>
+ <key>VFlip</key>
+ <string>YES</string>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>ID</key>
+ <integer>98</integer>
+ <key>OrthogonalBarAutomatic</key>
+ <true/>
+ <key>OrthogonalBarPoint</key>
+ <string>{0, 0}</string>
+ <key>OrthogonalBarPosition</key>
+ <real>-1</real>
+ <key>Points</key>
+ <array>
+ <string>{134.4999955076145, 324}</string>
+ <string>{90, 324}</string>
+ <string>{81, 198}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.6</string>
+ <key>g</key>
+ <string>0.6</string>
+ <key>r</key>
+ <string>0.6</string>
+ </dict>
+ <key>HeadArrow</key>
+ <string>0</string>
+ <key>Legacy</key>
+ <true/>
+ <key>LineType</key>
+ <integer>2</integer>
+ <key>Pattern</key>
+ <integer>1</integer>
+ <key>TailArrow</key>
+ <string>0</string>
+ </dict>
+ </dict>
+ <key>Tail</key>
+ <dict>
+ <key>ID</key>
+ <integer>97</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{135, 315}, {18, 18}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>ID</key>
+ <integer>97</integer>
+ <key>Shape</key>
+ <string>Circle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.6</string>
+ <key>g</key>
+ <string>0.6</string>
+ <key>r</key>
+ <string>0.6</string>
+ </dict>
+ <key>Pattern</key>
+ <integer>1</integer>
+ </dict>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{72, 144}, {99, 54}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>HFlip</key>
+ <string>YES</string>
+ <key>ID</key>
+ <integer>96</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.6</string>
+ <key>g</key>
+ <string>0.6</string>
+ <key>r</key>
+ <string>0.6</string>
+ </dict>
+ <key>Pattern</key>
+ <integer>1</integer>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Pad</key>
+ <integer>4</integer>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;\red102\green102\blue102;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
+
+\f0\i\fs24 \cf2 The ticket is a bug and should be fixed.}</string>
+ </dict>
+ <key>VFlip</key>
+ <string>YES</string>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{243, 279}, {18, 18}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>ID</key>
+ <integer>91</integer>
+ <key>Shape</key>
+ <string>Circle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.6</string>
+ <key>g</key>
+ <string>0.6</string>
+ <key>r</key>
+ <string>0.6</string>
+ </dict>
+ <key>Pattern</key>
+ <integer>1</integer>
+ </dict>
+ </dict>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>ID</key>
+ <integer>90</integer>
+ <key>Points</key>
+ <array>
+ <string>{252, 278.49999548068274}</string>
+ <string>{252, 198}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.6</string>
+ <key>g</key>
+ <string>0.6</string>
+ <key>r</key>
+ <string>0.6</string>
+ </dict>
+ <key>HeadArrow</key>
+ <string>0</string>
+ <key>Legacy</key>
+ <true/>
+ <key>LineType</key>
+ <integer>1</integer>
+ <key>Pattern</key>
+ <integer>1</integer>
+ <key>TailArrow</key>
+ <string>0</string>
+ </dict>
+ </dict>
+ <key>Tail</key>
+ <dict>
+ <key>ID</key>
+ <integer>91</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{189, 144}, {243, 54}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>HFlip</key>
+ <string>YES</string>
+ <key>ID</key>
+ <integer>89</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.6</string>
+ <key>g</key>
+ <string>0.6</string>
+ <key>r</key>
+ <string>0.6</string>
+ </dict>
+ <key>Pattern</key>
+ <integer>1</integer>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Pad</key>
+ <integer>4</integer>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;\red102\green102\blue102;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
+
+\f0\i\fs24 \cf2 The ticket was already reported, was already rejected, isn't a bug, doesn't contain enough information, or can't be reproduced.}</string>
+ </dict>
+ <key>VFlip</key>
+ <string>YES</string>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>Head</key>
+ <dict>
+ <key>ID</key>
+ <integer>10</integer>
+ </dict>
+ <key>ID</key>
+ <integer>60</integer>
+ <key>Points</key>
+ <array>
+ <string>{144, 396}</string>
+ <string>{144, 450}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0.501961</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>HeadArrow</key>
+ <string>FilledArrow</string>
+ <key>Legacy</key>
+ <true/>
+ <key>TailArrow</key>
+ <string>0</string>
+ <key>Width</key>
+ <real>2</real>
+ </dict>
+ </dict>
+ <key>Tail</key>
+ <dict>
+ <key>ID</key>
+ <integer>11</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>ID</key>
+ <integer>82</integer>
+ <key>Points</key>
+ <array>
+ <string>{198, 288}</string>
+ <string>{252, 288}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0.501961</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>HeadArrow</key>
+ <string>0</string>
+ <key>Legacy</key>
+ <true/>
+ <key>TailArrow</key>
+ <string>0</string>
+ <key>Width</key>
+ <real>2</real>
+ </dict>
+ </dict>
+ <key>Tail</key>
+ <dict>
+ <key>ID</key>
+ <integer>12</integer>
+ <key>Info</key>
+ <integer>3</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>Head</key>
+ <dict>
+ <key>ID</key>
+ <integer>11</integer>
+ </dict>
+ <key>ID</key>
+ <integer>54</integer>
+ <key>Points</key>
+ <array>
+ <string>{144, 306}</string>
+ <string>{144, 360}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0.501961</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>HeadArrow</key>
+ <string>FilledArrow</string>
+ <key>Legacy</key>
+ <true/>
+ <key>TailArrow</key>
+ <string>0</string>
+ <key>Width</key>
+ <real>2</real>
+ </dict>
+ </dict>
+ <key>Tail</key>
+ <dict>
+ <key>ID</key>
+ <integer>12</integer>
+ <key>Info</key>
+ <integer>1</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>Head</key>
+ <dict>
+ <key>ID</key>
+ <integer>130</integer>
+ </dict>
+ <key>ID</key>
+ <integer>131</integer>
+ <key>Points</key>
+ <array>
+ <string>{198, 468}</string>
+ <string>{315, 468}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.501961</string>
+ <key>g</key>
+ <string>0.25098</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>HeadArrow</key>
+ <string>FilledArrow</string>
+ <key>Legacy</key>
+ <true/>
+ <key>TailArrow</key>
+ <string>0</string>
+ <key>Width</key>
+ <real>2</real>
+ </dict>
+ </dict>
+ <key>Tail</key>
+ <dict>
+ <key>ID</key>
+ <integer>10</integer>
+ <key>Info</key>
+ <integer>3</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>Head</key>
+ <dict>
+ <key>ID</key>
+ <integer>135</integer>
+ <key>Info</key>
+ <integer>4</integer>
+ </dict>
+ <key>ID</key>
+ <integer>136</integer>
+ <key>Points</key>
+ <array>
+ <string>{252, 288}</string>
+ <string>{315, 432}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0.501961</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>HeadArrow</key>
+ <string>FilledArrow</string>
+ <key>Legacy</key>
+ <true/>
+ <key>TailArrow</key>
+ <string>0</string>
+ <key>Width</key>
+ <real>2</real>
+ </dict>
+ </dict>
+ <key>Tail</key>
+ <dict>
+ <key>ID</key>
+ <integer>82</integer>
+ <key>Info</key>
+ <integer>1</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>Head</key>
+ <dict>
+ <key>ID</key>
+ <integer>137</integer>
+ <key>Info</key>
+ <integer>4</integer>
+ </dict>
+ <key>ID</key>
+ <integer>138</integer>
+ <key>Points</key>
+ <array>
+ <string>{252, 288}</string>
+ <string>{315, 396}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0.501961</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>HeadArrow</key>
+ <string>FilledArrow</string>
+ <key>Legacy</key>
+ <true/>
+ <key>TailArrow</key>
+ <string>0</string>
+ <key>Width</key>
+ <real>2</real>
+ </dict>
+ </dict>
+ <key>Tail</key>
+ <dict>
+ <key>ID</key>
+ <integer>82</integer>
+ <key>Info</key>
+ <integer>1</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>Head</key>
+ <dict>
+ <key>ID</key>
+ <integer>139</integer>
+ <key>Info</key>
+ <integer>4</integer>
+ </dict>
+ <key>ID</key>
+ <integer>140</integer>
+ <key>Points</key>
+ <array>
+ <string>{252, 288}</string>
+ <string>{315, 360}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0.501961</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>HeadArrow</key>
+ <string>FilledArrow</string>
+ <key>Legacy</key>
+ <true/>
+ <key>TailArrow</key>
+ <string>0</string>
+ <key>Width</key>
+ <real>2</real>
+ </dict>
+ </dict>
+ <key>Tail</key>
+ <dict>
+ <key>ID</key>
+ <integer>82</integer>
+ <key>Info</key>
+ <integer>1</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>Head</key>
+ <dict>
+ <key>ID</key>
+ <integer>123</integer>
+ <key>Info</key>
+ <integer>4</integer>
+ </dict>
+ <key>ID</key>
+ <integer>124</integer>
+ <key>Points</key>
+ <array>
+ <string>{252, 288}</string>
+ <string>{315, 288}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0.501961</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>HeadArrow</key>
+ <string>FilledArrow</string>
+ <key>Legacy</key>
+ <true/>
+ <key>TailArrow</key>
+ <string>0</string>
+ <key>Width</key>
+ <real>2</real>
+ </dict>
+ </dict>
+ <key>Tail</key>
+ <dict>
+ <key>ID</key>
+ <integer>82</integer>
+ <key>Info</key>
+ <integer>1</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{270, 576}, {81, 18}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica-Bold</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>128</integer>
+ <key>Magnets</key>
+ <array>
+ <string>{0, 1}</string>
+ <string>{0, -1}</string>
+ <string>{1, 0}</string>
+ <string>{-1, 0}</string>
+ </array>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>GradientCenter</key>
+ <string>{0, 0.15238095234285712}</string>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
+
+\f0\i\fs24 \cf0 status}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{72.000000000000057, 596}, {99, 14}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FitText</key>
+ <string>Vertical</string>
+ <key>Flow</key>
+ <string>Resize</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>45</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Align</key>
+ <integer>2</integer>
+ <key>Pad</key>
+ <integer>0</integer>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;\red0\green64\blue128;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qr
+
+\f0\b\fs24 \cf2 Committers}</string>
+ <key>VerticalPad</key>
+ <integer>0</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>ID</key>
+ <integer>44</integer>
+ <key>Points</key>
+ <array>
+ <string>{183.59999999999997, 603}</string>
+ <string>{221.39999999999998, 603}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.501961</string>
+ <key>g</key>
+ <string>0.25098</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>HeadArrow</key>
+ <string>FilledArrow</string>
+ <key>Legacy</key>
+ <true/>
+ <key>LineType</key>
+ <integer>1</integer>
+ <key>TailArrow</key>
+ <string>0</string>
+ <key>Width</key>
+ <real>2</real>
+ </dict>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{72.000000000000057, 578}, {99, 14}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FitText</key>
+ <string>Vertical</string>
+ <key>Flow</key>
+ <string>Resize</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>43</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Align</key>
+ <integer>2</integer>
+ <key>Pad</key>
+ <integer>0</integer>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;\red0\green128\blue0;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qr
+
+\f0\b\fs24 \cf2 Ticket triagers }</string>
+ <key>VerticalPad</key>
+ <integer>0</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>ID</key>
+ <integer>42</integer>
+ <key>Points</key>
+ <array>
+ <string>{183.59999999999997, 585}</string>
+ <string>{221.39999999999998, 585}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0.501961</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>HeadArrow</key>
+ <string>FilledArrow</string>
+ <key>Legacy</key>
+ <true/>
+ <key>LineType</key>
+ <integer>1</integer>
+ <key>TailArrow</key>
+ <string>0</string>
+ <key>Width</key>
+ <real>2</real>
+ </dict>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{270, 594}, {81, 18}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica-Bold</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>129</integer>
+ <key>Magnets</key>
+ <array>
+ <string>{0, 1}</string>
+ <string>{0, -1}</string>
+ <string>{1, 0}</string>
+ <string>{-1, 0}</string>
+ </array>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.3</string>
+ <key>b</key>
+ <string>1</string>
+ <key>g</key>
+ <string>0.501961</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>GradientCenter</key>
+ <string>{0, 0.15238095234285712}</string>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
+
+\f0\fs24 \cf0 in progress}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{351, 576}, {81, 18}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica-Bold</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>125</integer>
+ <key>Magnets</key>
+ <array>
+ <string>{0, 1}</string>
+ <string>{0, -1}</string>
+ <string>{1, 0}</string>
+ <string>{-1, 0}</string>
+ </array>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.3</string>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0</string>
+ <key>r</key>
+ <string>1</string>
+ </dict>
+ <key>GradientCenter</key>
+ <string>{0, 0.15238095234285712}</string>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
+
+\f0\fs24 \cf0 stopped}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{351, 594}, {81, 18}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica-Bold</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>127</integer>
+ <key>Magnets</key>
+ <array>
+ <string>{0, 1}</string>
+ <string>{0, -1}</string>
+ <string>{1, 0}</string>
+ <string>{-1, 0}</string>
+ </array>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.3</string>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0.501961</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>GradientCenter</key>
+ <string>{0, 0.15238095234285712}</string>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
+
+\f0\fs24 \cf0 completed}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>ID</key>
+ <integer>36</integer>
+ <key>Points</key>
+ <array>
+ <string>{288, 243}</string>
+ <string>{432, 243}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>HeadArrow</key>
+ <string>0</string>
+ <key>Legacy</key>
+ <true/>
+ <key>TailArrow</key>
+ <string>0</string>
+ </dict>
+ </dict>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>ID</key>
+ <integer>33</integer>
+ <key>Points</key>
+ <array>
+ <string>{72, 243}</string>
+ <string>{216, 243}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>HeadArrow</key>
+ <string>0</string>
+ <key>Legacy</key>
+ <true/>
+ <key>TailArrow</key>
+ <string>0</string>
+ </dict>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{315, 315}, {90.000000000000014, 18}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica-Bold</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>132</integer>
+ <key>Magnets</key>
+ <array>
+ <string>{0, 1}</string>
+ <string>{0, -1}</string>
+ <string>{1, 0}</string>
+ <string>{-1, 0}</string>
+ </array>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.3</string>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0</string>
+ <key>r</key>
+ <string>1</string>
+ </dict>
+ <key>GradientCenter</key>
+ <string>{0, 0.15238095234285712}</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
+
+\f0\fs24 \cf0 wontfix}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{315, 423}, {90.000000000000014, 18}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica-Bold</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>135</integer>
+ <key>Magnets</key>
+ <array>
+ <string>{0, 1}</string>
+ <string>{0, -1}</string>
+ <string>{1, 0}</string>
+ <string>{-1, 0}</string>
+ </array>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.3</string>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0</string>
+ <key>r</key>
+ <string>1</string>
+ </dict>
+ <key>GradientCenter</key>
+ <string>{0, 0.15238095234285712}</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
+
+\f0\fs24 \cf0 worksforme}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{315, 387}, {90.000000000000014, 18}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica-Bold</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>137</integer>
+ <key>Magnets</key>
+ <array>
+ <string>{0, 1}</string>
+ <string>{0, -1}</string>
+ <string>{1, 0}</string>
+ <string>{-1, 0}</string>
+ </array>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.3</string>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0</string>
+ <key>r</key>
+ <string>1</string>
+ </dict>
+ <key>GradientCenter</key>
+ <string>{0, 0.15238095234285712}</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
+
+\f0\fs24 \cf0 needsinfo}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{315, 351}, {90.000000000000014, 18}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica-Bold</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>139</integer>
+ <key>Magnets</key>
+ <array>
+ <string>{0, 1}</string>
+ <string>{0, -1}</string>
+ <string>{1, 0}</string>
+ <string>{-1, 0}</string>
+ </array>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.3</string>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0</string>
+ <key>r</key>
+ <string>1</string>
+ </dict>
+ <key>GradientCenter</key>
+ <string>{0, 0.15238095234285712}</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
+
+\f0\fs24 \cf0 invalid}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{315, 459}, {90.000000000000014, 18}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica-Bold</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>130</integer>
+ <key>Magnets</key>
+ <array>
+ <string>{0, 1}</string>
+ <string>{0, -1}</string>
+ <string>{1, 0}</string>
+ <string>{-1, 0}</string>
+ </array>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.3</string>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0.501961</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>GradientCenter</key>
+ <string>{0, 0.15238095234285712}</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
+
+\f0\fs24 \cf0 fixed}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{315, 279}, {90.000000000000014, 18}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica-Bold</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>123</integer>
+ <key>Magnets</key>
+ <array>
+ <string>{0, 1}</string>
+ <string>{0, -1}</string>
+ <string>{1, 0}</string>
+ <string>{-1, 0}</string>
+ </array>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.3</string>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0</string>
+ <key>r</key>
+ <string>1</string>
+ </dict>
+ <key>GradientCenter</key>
+ <string>{0, 0.15238095234285712}</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
+
+\f0\fs24 \cf0 duplicate}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{90, 270}, {108, 36}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>12</integer>
+ <key>Magnets</key>
+ <array>
+ <string>{0, 1}</string>
+ <string>{0, -1}</string>
+ <string>{1, 0}</string>
+ <string>{-1, 0}</string>
+ </array>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.3</string>
+ <key>b</key>
+ <string>1</string>
+ <key>g</key>
+ <string>0.501961</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>CornerRadius</key>
+ <real>5</real>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
+
+\f0\fs24 \cf0 Unreviewed}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{90, 360}, {108, 36}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>11</integer>
+ <key>Magnets</key>
+ <array>
+ <string>{0, 1}</string>
+ <string>{0, -1}</string>
+ <string>{1, 0}</string>
+ <string>{-1, 0}</string>
+ </array>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.3</string>
+ <key>b</key>
+ <string>1</string>
+ <key>g</key>
+ <string>0.501961</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>CornerRadius</key>
+ <real>5</real>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
+
+\f0\fs24 \cf0 Accepted}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{90, 450}, {108, 36}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>10</integer>
+ <key>Magnets</key>
+ <array>
+ <string>{0, 1}</string>
+ <string>{0, -1}</string>
+ <string>{1, 0}</string>
+ <string>{-1, 0}</string>
+ </array>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.3</string>
+ <key>b</key>
+ <string>1</string>
+ <key>g</key>
+ <string>0.501961</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>CornerRadius</key>
+ <real>5</real>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
+
+\f0\fs24 \cf0 Ready for Checkin}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{72, 216}, {144, 288}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica</string>
+ <key>Size</key>
+ <real>13</real>
+ </dict>
+ <key>ID</key>
+ <integer>99</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict/>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
+
+\f0\fs28 \cf0 Open tickets\
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
+
+\fs12 \cf0 \
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
+
+\i\fs24 \cf0 triage state}</string>
+ </dict>
+ <key>TextPlacement</key>
+ <integer>0</integer>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{288, 216}, {144, 288}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica</string>
+ <key>Size</key>
+ <real>15</real>
+ </dict>
+ <key>ID</key>
+ <integer>32</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict/>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf370
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
+
+\f0\fs28 \cf0 Closed tickets\
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
+
+\fs12 \cf0 \
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc
+
+\i\fs24 \cf0 resolution}</string>
+ </dict>
+ <key>TextPlacement</key>
+ <integer>0</integer>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{270, 576}, {162, 36}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica-Bold</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>126</integer>
+ <key>Magnets</key>
+ <array>
+ <string>{0, 1}</string>
+ <string>{0, -1}</string>
+ <string>{1, 0}</string>
+ <string>{-1, 0}</string>
+ </array>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>GradientCenter</key>
+ <string>{0, 0.15238095234285712}</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{72, 576}, {162, 36}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Font</key>
+ <string>Helvetica</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>88</integer>
+ <key>Magnets</key>
+ <array>
+ <string>{0, 1}</string>
+ <string>{0, -1}</string>
+ <string>{1, 0}</string>
+ <string>{-1, 0}</string>
+ </array>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>GradientCenter</key>
+ <string>{0, 0.15238095234285712}</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ </dict>
+ </array>
+ <key>GridInfo</key>
+ <dict>
+ <key>ShowsGrid</key>
+ <string>YES</string>
+ <key>SnapsToGrid</key>
+ <string>YES</string>
+ </dict>
+ <key>GuidesLocked</key>
+ <string>NO</string>
+ <key>GuidesVisible</key>
+ <string>YES</string>
+ <key>HPages</key>
+ <integer>1</integer>
+ <key>ImageCounter</key>
+ <integer>1</integer>
+ <key>KeepToScale</key>
+ <false/>
+ <key>Layers</key>
+ <array>
+ <dict>
+ <key>Lock</key>
+ <string>NO</string>
+ <key>Name</key>
+ <string>Calque 1</string>
+ <key>Print</key>
+ <string>YES</string>
+ <key>View</key>
+ <string>YES</string>
+ </dict>
+ </array>
+ <key>LayoutInfo</key>
+ <dict>
+ <key>Animate</key>
+ <string>NO</string>
+ <key>circoMinDist</key>
+ <real>18</real>
+ <key>circoSeparation</key>
+ <real>0.0</real>
+ <key>layoutEngine</key>
+ <string>dot</string>
+ <key>neatoSeparation</key>
+ <real>0.0</real>
+ <key>twopiSeparation</key>
+ <real>0.0</real>
+ </dict>
+ <key>LinksVisible</key>
+ <string>NO</string>
+ <key>MagnetsVisible</key>
+ <string>NO</string>
+ <key>MasterSheets</key>
+ <array/>
+ <key>ModificationDate</key>
+ <string>2013-04-08 16:32:00 +0000</string>
+ <key>Modifier</key>
+ <string>Aymeric Augustin</string>
+ <key>NotesVisible</key>
+ <string>NO</string>
+ <key>Orientation</key>
+ <integer>2</integer>
+ <key>OriginVisible</key>
+ <string>NO</string>
+ <key>PageBreaks</key>
+ <string>YES</string>
+ <key>PrintInfo</key>
+ <dict>
+ <key>NSBottomMargin</key>
+ <array>
+ <string>float</string>
+ <string>41</string>
+ </array>
+ <key>NSHorizonalPagination</key>
+ <array>
+ <string>coded</string>
+ <string>BAtzdHJlYW10eXBlZIHoA4QBQISEhAhOU051bWJlcgCEhAdOU1ZhbHVlAISECE5TT2JqZWN0AIWEASqEhAFxlwCG</string>
+ </array>
+ <key>NSLeftMargin</key>
+ <array>
+ <string>float</string>
+ <string>18</string>
+ </array>
+ <key>NSPaperSize</key>
+ <array>
+ <string>size</string>
+ <string>{595.28997802734375, 841.8900146484375}</string>
+ </array>
+ <key>NSPrintReverseOrientation</key>
+ <array>
+ <string>int</string>
+ <string>0</string>
+ </array>
+ <key>NSRightMargin</key>
+ <array>
+ <string>float</string>
+ <string>18</string>
+ </array>
+ <key>NSTopMargin</key>
+ <array>
+ <string>float</string>
+ <string>18</string>
+ </array>
+ </dict>
+ <key>PrintOnePage</key>
+ <false/>
+ <key>ReadOnly</key>
+ <string>NO</string>
+ <key>RowAlign</key>
+ <integer>1</integer>
+ <key>RowSpacing</key>
+ <real>36</real>
+ <key>SheetTitle</key>
+ <string>Canevas 1</string>
+ <key>SmartAlignmentGuidesActive</key>
+ <string>YES</string>
+ <key>SmartDistanceGuidesActive</key>
+ <string>YES</string>
+ <key>UniqueID</key>
+ <integer>1</integer>
+ <key>UseEntirePage</key>
+ <false/>
+ <key>VPages</key>
+ <integer>1</integer>
+ <key>WindowInfo</key>
+ <dict>
+ <key>CurrentSheet</key>
+ <integer>0</integer>
+ <key>ExpandedCanvases</key>
+ <array/>
+ <key>Frame</key>
+ <string>{{1, 4}, {1190, 874}}</string>
+ <key>ListView</key>
+ <true/>
+ <key>OutlineWidth</key>
+ <integer>142</integer>
+ <key>RightSidebar</key>
+ <false/>
+ <key>ShowRuler</key>
+ <true/>
+ <key>Sidebar</key>
+ <true/>
+ <key>SidebarWidth</key>
+ <integer>120</integer>
+ <key>VisibleRegion</key>
+ <string>{{-195, 118.01801649706192}, {950.4504382015291, 662.16215362855348}}</string>
+ <key>Zoom</key>
+ <real>1.1100000143051147</real>
+ <key>ZoomValues</key>
+ <array>
+ <array>
+ <string>Canevas 1</string>
+ <real>1.1100000143051147</real>
+ <real>1.0499999523162842</real>
+ </array>
+ </array>
+ </dict>
+</dict>
+</plist>
diff --git a/docs/internals/_images/triage_process.pdf b/docs/internals/_images/triage_process.pdf
new file mode 100644
index 0000000000..f731e3e584
--- /dev/null
+++ b/docs/internals/_images/triage_process.pdf
Binary files differ
diff --git a/docs/internals/_images/triage_process.svg b/docs/internals/_images/triage_process.svg
new file mode 100644
index 0000000000..787f5ca647
--- /dev/null
+++ b/docs/internals/_images/triage_process.svg
@@ -0,0 +1,3 @@
+<?xml version="1.0"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="52 133 400 501" width="400pt" height="501pt"><metadata xmlns:dc="http://purl.org/dc/elements/1.1/"><dc:date>2013-04-08 16:32Z</dc:date><!-- Produced by OmniGraffle Professional 5.4.2 --></metadata><defs><filter id="Shadow" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" result="blur" stdDeviation="3.488"/><feOffset in="blur" result="offset" dx="0" dy="4"/><feFlood flood-color="black" flood-opacity=".75" result="flood"/><feComposite in="flood" in2="offset" operator="in"/></filter><font-face font-family="Helvetica" font-size="14" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="-1e3" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-style="italic" font-weight="500"><font-face-src><font-face-name name="Helvetica-Oblique"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -3 7 6" markerWidth="7" markerHeight="6" color="green"><g><path d="M 4.8000002 0 L 0 -1.8000001 L 0 1.8000001 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="532.22656" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-weight="bold"><font-face-src><font-face-name name="Helvetica-Bold"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker_2" viewBox="-1 -3 7 6" markerWidth="7" markerHeight="6" color="#004080"><g><path d="M 4.8000002 0 L 0 -1.8000001 L 0 1.8000001 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canevas 1</title><rect fill="white" width="559.28998" height="782.89"/><g><title>Calque 1</title><g><use xl:href="#id88_Graphic" filter="url(#Shadow)"/><use xl:href="#id126_Graphic" filter="url(#Shadow)"/><use xl:href="#id32_Graphic" filter="url(#Shadow)"/><use xl:href="#id99_Graphic" filter="url(#Shadow)"/><use xl:href="#id10_Graphic" filter="url(#Shadow)"/><use xl:href="#id11_Graphic" filter="url(#Shadow)"/><use xl:href="#id12_Graphic" filter="url(#Shadow)"/><use xl:href="#id123_Graphic" filter="url(#Shadow)"/><use xl:href="#id130_Graphic" filter="url(#Shadow)"/><use xl:href="#id139_Graphic" filter="url(#Shadow)"/><use xl:href="#id137_Graphic" filter="url(#Shadow)"/><use xl:href="#id135_Graphic" filter="url(#Shadow)"/><use xl:href="#id132_Graphic" filter="url(#Shadow)"/></g><g id="id88_Graphic"><rect x="72" y="576" width="162" height="36" fill="white"/></g><g id="id126_Graphic"><rect x="270" y="576" width="162" height="36" fill="white"/></g><g id="id32_Graphic"><rect x="288" y="216" width="144" height="288" fill="white"/><rect x="288" y="216" width="144" height="288" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(293 221)" fill="black"><tspan font-family="Helvetica" font-size="14" font-weight="500" x="23.427734" y="14" textLength="87.14453">Closed tickets</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" x="40.984375" y="35" textLength="52.03125">resolution</tspan></text></g><g id="id99_Graphic"><rect x="72" y="216" width="144" height="288" fill="white"/><rect x="72" y="216" width="144" height="288" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(77 221)" fill="black"><tspan font-family="Helvetica" font-size="14" font-weight="500" x="28.093262" y="14" textLength="77.813477">Open tickets</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" x="37.316406" y="35" textLength="59.367188">triage state</tspan></text></g><g id="id10_Graphic"><path d="M 95 450 L 193 450 C 195.76142 450 198 452.23858 198 455 L 198 481 C 198 483.76142 195.76142 486 193 486 L 95 486 C 92.238576 486 90 483.76142 90 481 C 90 481 90 481 90 481 L 90 455 C 90 452.23858 92.238576 450 95 450 C 95 450 95 450 95 450 Z" fill="#0080ff" fill-opacity=".30000001"/><path d="M 95 450 L 193 450 C 195.76142 450 198 452.23858 198 455 L 198 481 C 198 483.76142 195.76142 486 193 486 L 95 486 C 92.238576 486 90 483.76142 90 481 C 90 481 90 481 90 481 L 90 455 C 90 452.23858 92.238576 450 95 450 C 95 450 95 450 95 450 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(95 454)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="22.987305" y="11" textLength="55.359375">Ready for </tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="27.323242" y="25" textLength="43.353516">Checkin</tspan></text></g><g id="id11_Graphic"><path d="M 95 360 L 193 360 C 195.76142 360 198 362.23858 198 365 L 198 391 C 198 393.76142 195.76142 396 193 396 L 95 396 C 92.238576 396 90 393.76142 90 391 C 90 391 90 391 90 391 L 90 365 C 90 362.23858 92.238576 360 95 360 C 95 360 95 360 95 360 Z" fill="#0080ff" fill-opacity=".30000001"/><path d="M 95 360 L 193 360 C 195.76142 360 198 362.23858 198 365 L 198 391 C 198 393.76142 195.76142 396 193 396 L 95 396 C 92.238576 396 90 393.76142 90 391 C 90 391 90 391 90 391 L 90 365 C 90 362.23858 92.238576 360 95 360 C 95 360 95 360 95 360 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(95 371)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="23.983398" y="11" textLength="50.033203">Accepted</tspan></text></g><g id="id12_Graphic"><path d="M 95 270 L 193 270 C 195.76142 270 198 272.23858 198 275 L 198 301 C 198 303.76142 195.76142 306 193 306 L 95 306 C 92.238576 306 90 303.76142 90 301 C 90 301 90 301 90 301 L 90 275 C 90 272.23858 92.238576 270 95 270 C 95 270 95 270 95 270 Z" fill="#0080ff" fill-opacity=".30000001"/><path d="M 95 270 L 193 270 C 195.76142 270 198 272.23858 198 275 L 198 301 C 198 303.76142 195.76142 306 193 306 L 95 306 C 92.238576 306 90 303.76142 90 301 C 90 301 90 301 90 301 L 90 275 C 90 272.23858 92.238576 270 95 270 C 95 270 95 270 95 270 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(95 281)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="17.318359" y="11" textLength="63.36328">Unreviewed</tspan></text></g><g id="id123_Graphic"><rect x="315" y="279" width="90" height="18" fill="red" fill-opacity=".30000001"/><rect x="315" y="279" width="90" height="18" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(320 281)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="15.982422" y="11" textLength="48.035156">duplicate</tspan></text></g><g id="id130_Graphic"><rect x="315" y="459" width="90" height="18" fill="green" fill-opacity=".30000001"/><rect x="315" y="459" width="90" height="18" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(320 461)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="27.326172" y="11" textLength="6">fi</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="33.326172" y="11" textLength="19.347656">xed</tspan></text></g><g id="id139_Graphic"><rect x="315" y="351" width="90" height="18" fill="red" fill-opacity=".30000001"/><rect x="315" y="351" width="90" height="18" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(320 353)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="22.990234" y="11" textLength="34.019531">invalid</tspan></text></g><g id="id137_Graphic"><rect x="315" y="387" width="90" height="18" fill="red" fill-opacity=".30000001"/><rect x="315" y="387" width="90" height="18" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(320 389)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="13.978516" y="11" textLength="52.04297">needsinfo</tspan></text></g><g id="id135_Graphic"><rect x="315" y="423" width="90" height="18" fill="red" fill-opacity=".30000001"/><rect x="315" y="423" width="90" height="18" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(320 425)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="8.995117" y="11" textLength="62.009766">worksforme</tspan></text></g><g id="id132_Graphic"><rect x="315" y="315" width="90" height="18" fill="red" fill-opacity=".30000001"/><rect x="315" y="315" width="90" height="18" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(320 317)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="21.326172" y="11" textLength="31.347656">wontfi</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="52.673828" y="11" textLength="6">x</tspan></text></g><line x1="72" y1="243" x2="216" y2="243" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="288" y1="243" x2="432" y2="243" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="351" y="594" width="81" height="18" fill="green" fill-opacity=".30000001"/><text transform="translate(356 596)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="7.817383" y="11" textLength="55.365234">completed</tspan></text><rect x="351" y="576" width="81" height="18" fill="red" fill-opacity=".30000001"/><text transform="translate(356 578)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="14.1484375" y="11" textLength="42.703125">stopped</tspan></text><rect x="270" y="594" width="81" height="18" fill="#0080ff" fill-opacity=".30000001"/><text transform="translate(275 596)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="5.819336" y="11" textLength="59.361328">in progress</tspan></text><line x1="183.6" y1="585" x2="208.5" y2="585" marker-end="url(#FilledArrow_Marker)" stroke="green" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><text transform="translate(72 578)" fill="green"><tspan font-family="Helvetica" font-size="12" font-weight="bold" fill="green" x="17.173828" y="11" textLength="7.330078">T</tspan><tspan font-family="Helvetica" font-size="12" font-weight="bold" fill="green" x="24.292969" y="11" textLength="78.041016">icket triagers </tspan></text><line x1="183.6" y1="603" x2="208.5" y2="603" marker-end="url(#FilledArrow_Marker_2)" stroke="#004080" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><text transform="translate(72 596)" fill="#004080"><tspan font-family="Helvetica" font-size="12" font-weight="bold" fill="#004080" x="32.320312" y="11" textLength="66.679688">Committers</tspan></text><rect x="270" y="576" width="81" height="18" fill="white"/><text transform="translate(275 578)" fill="black"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" x="19.492188" y="11" textLength="32.015625">status</tspan></text><line x1="252" y1="288" x2="302.1" y2="288" marker-end="url(#FilledArrow_Marker)" stroke="green" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><line x1="252" y1="288" x2="306.50529" y2="350.29176" marker-end="url(#FilledArrow_Marker)" stroke="green" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><line x1="252" y1="288" x2="308.50006" y2="384.85725" marker-end="url(#FilledArrow_Marker)" stroke="green" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><line x1="252" y1="288" x2="309.82944" y2="420.18157" marker-end="url(#FilledArrow_Marker)" stroke="green" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><line x1="198" y1="468" x2="302.1" y2="468" marker-end="url(#FilledArrow_Marker_2)" stroke="#004080" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><line x1="144" y1="306" x2="144" y2="347.1" marker-end="url(#FilledArrow_Marker)" stroke="green" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><line x1="198" y1="288" x2="252" y2="288" stroke="green" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><line x1="144" y1="396" x2="144" y2="437.1" marker-end="url(#FilledArrow_Marker)" stroke="green" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><rect x="189" y="144" width="243" height="54" fill="white"/><path d="M 432 198 L 189 198 L 189 144 L 432 144 Z" stroke="#999" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="4,4"/><text transform="translate(193 150)" fill="#666"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="#666" x="19.789062" y="11" textLength="198.75586">The ticket was already reported, was </tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="#666" x=".8017578" y="25" textLength="236.73047">already rejected, isn't a bug, doesn't contain </tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="#666" x="1.2792969" y="39" textLength="232.4414">enough information, or can't be reproduced.</tspan></text><line x1="252" y1="278.5" x2="252" y2="198" stroke="#999" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="4,4"/><path d="M 258.36395 281.63605 C 261.87869 285.15076 261.87869 290.84924 258.36395 294.36395 C 254.84924 297.87869 249.15076 297.87869 245.63605 294.36395 C 242.12131 290.84924 242.12131 285.15076 245.63605 281.63605 C 249.15076 278.12131 254.84924 278.12131 258.36395 281.63605" stroke="#999" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="4,4"/><rect x="72" y="144" width="99" height="54" fill="white"/><path d="M 171 198 L 72 198 L 72 144 L 171 144 Z" stroke="#999" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="4,4"/><text transform="translate(76 150)" fill="#666"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="#666" x="8.486328" y="11" textLength="77.36133">The ticket is a </tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="#666" x="4.4638672" y="25" textLength="85.40625">bug and should </tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="#666" x="22.81836" y="39" textLength="22.68164">be fi</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="#666" x="45.5" y="39" textLength="22.68164">xed.</tspan></text><path d="M 150.36395 317.63605 C 153.87869 321.15076 153.87869 326.84924 150.36395 330.36395 C 146.84924 333.87869 141.15076 333.87869 137.63605 330.36395 C 134.12131 326.84924 134.12131 321.15076 137.63605 317.63605 C 141.15076 314.12131 146.84924 314.12131 150.36395 317.63605" stroke="#999" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="4,4"/><path d="M 134.499996 324 L 127.499996 324 L 90 324 L 81 324 L 81 198" stroke="#999" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="4,4"/><rect x="72" y="522" width="342" height="36" fill="white"/><path d="M 414 558 L 72 558 L 72 522 L 414 522 Z" stroke="#999" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="4,4"/><text transform="translate(76 526)" fill="#666"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="#666" x="7.241211" y="11" textLength="322.85156">The ticket has a patch which applies cleanly and includes all </tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="#666" x="5.9052734" y="25" textLength="127.42383">needed tests and docs. </tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="#666" x="132.67285" y="25" textLength="8.0039062">A</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="#666" x="140.02051" y="25" textLength="188.07422"> core developer can commit it as is.</tspan></text><path d="M 150.36395 407.63605 C 153.87869 411.15076 153.87869 416.84924 150.36395 420.36395 C 146.84924 423.8787 141.15076 423.8787 137.63605 420.36395 C 134.12131 416.84924 134.12131 411.15076 137.63605 407.63605 C 141.15076 404.1213 146.84924 404.1213 150.36395 407.63605" stroke="#999" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="4,4"/><path d="M 134.499996 414 L 127.499996 414 L 90 414 L 81 414 L 81 522" stroke="#999" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="4,4"/><line x1="252" y1="288" x2="303.79966" y2="317.5998" marker-end="url(#FilledArrow_Marker)" stroke="green" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/></g></g></svg>
diff --git a/docs/internals/committers.txt b/docs/internals/committers.txt
index 7900dd8cd0..a0649f38a2 100644
--- a/docs/internals/committers.txt
+++ b/docs/internals/committers.txt
@@ -14,8 +14,9 @@ Journal-World`_ of Lawrence, Kansas, USA.
programming", and in technical circles as "the guy who invented Django."
He was lead developer at World Online for 2.5 years, during which time
- Django was developed and implemented on World Online's sites. He's now the
- leader and founder of EveryBlock_, a "news feed for your block".
+ Django was developed and implemented on World Online's sites. He was the
+ leader and founder of EveryBlock_, a "news feed for your block." He now
+ develops for Soundslice_.
Adrian lives in Chicago, USA.
@@ -40,13 +41,15 @@ Journal-World`_ of Lawrence, Kansas, USA.
`Wilson Miner`_
Wilson's design-fu is what makes Django look so nice. He designed the
Web site you're looking at right now, as well as Django's acclaimed admin
- interface. Wilson is the designer for EveryBlock_.
+ interface. Wilson was the designer for EveryBlock and Rdio_. He now
+ designs for Facebook.
Wilson lives in San Francisco, USA.
.. _lawrence journal-world: http://ljworld.com/
.. _adrian holovaty: http://holovaty.com/
.. _everyblock: http://everyblock.com/
+.. _soundslice: http://www.soundslice.com/
.. _simon willison: http://simonwillison.net/
.. _web-development blog: `simon willison`_
.. _jacob kaplan-moss: http://jacobian.org/
@@ -85,6 +88,8 @@ Malcolm Tredinnick
When he's not busy being an International Man of Mystery, Malcolm lives in
Sydney, Australia.
+ *Malcolm passed away on March 17, 2013.*
+
`Russell Keith-Magee`_
Russell studied physics as an undergraduate, and studied neural networks for
his PhD. His first job was with a startup in the defense industry developing
@@ -100,9 +105,9 @@ Malcolm Tredinnick
.. _russell keith-magee: http://cecinestpasun.com/
Joseph Kocherhans
- Joseph is currently a developer at EveryBlock_, and previously worked for
- the Lawrence Journal-World where he built most of the backend for their
- Marketplace site. He often disappears for several days into the woods,
+ Joseph was the director of lead development at EveryBlock and previously
+ developed at the Lawrence Journal-World. He is treasurer of the `Django
+ Software Foundation`_. He often disappears for several days into the woods,
attempts to teach himself computational linguistics, and annoys his
neighbors with his Charango_ playing.
@@ -113,6 +118,7 @@ Joseph Kocherhans
Joseph lives in Chicago, USA.
+.. _django software foundation: https://www.djangoproject.com/foundation/
.. _charango: http://en.wikipedia.org/wiki/Charango
`Luke Plant`_
@@ -419,6 +425,22 @@ Jeremy Dunck
.. _Preston Holmes: http://www.ptone.com/
+`Simon Charette`_
+ Simon is a mathematic student who discovered Django while searching for a
+ replacement framework to an in-house PHP entity. Since that faithful day
+ Django has been a big part of his life. So far, he's been involved in some
+ ORM and forms API fixes.
+
+ Apart from contributing to multiple open source projects he spends most of
+ his spare-time playing `Ultimate Frisbee`_ and working part-time
+ at this awesome place called `Reptiletech`_.
+
+ Simon lives in Montréal, Québec, Canada.
+
+.. _Simon Charette: https://github.com/charettes
+.. _Ultimate Frisbee: http://www.montrealultimate.ca
+.. _Reptiletech: http://www.reptiletech.com
+
Specialists
-----------
diff --git a/docs/internals/contributing/committing-code.txt b/docs/internals/contributing/committing-code.txt
index 67dda02f8b..bc2f97a485 100644
--- a/docs/internals/contributing/committing-code.txt
+++ b/docs/internals/contributing/committing-code.txt
@@ -116,7 +116,7 @@ Practicality beats purity, so it is up to each committer to decide how much
history mangling to do for a pull request. The main points are engaging the
community, getting work done, and having a usable commit history.
-.. _committing-guidlines:
+.. _committing-guidelines:
Committing guidelines
---------------------
diff --git a/docs/internals/contributing/new-contributors.txt b/docs/internals/contributing/new-contributors.txt
index b752503248..0d4ad0cf3f 100644
--- a/docs/internals/contributing/new-contributors.txt
+++ b/docs/internals/contributing/new-contributors.txt
@@ -140,12 +140,3 @@ FAQ
Short answer: No. It's always better to get another set of eyes on a
ticket. If you're having trouble getting that second set of eyes, see
question 1, above.
-
-3. **My ticket has been in DDN forever! What should I do?**
-
- Design Decision Needed requires consensus about the right solution. At the
- very least it needs consensus among the core developers, and ideally it has
- consensus from the community as well. The best way to accomplish this is to
- start a thread on the django-developers mailing list, and for very complex
- issues to start a wiki page summarizing the problem and the possible
- solutions.
diff --git a/docs/internals/contributing/triaging-tickets.txt b/docs/internals/contributing/triaging-tickets.txt
index 84f70fd731..bc6148ca46 100644
--- a/docs/internals/contributing/triaging-tickets.txt
+++ b/docs/internals/contributing/triaging-tickets.txt
@@ -50,9 +50,9 @@ attribute easily tells us what and who each ticket is waiting on.
Since a picture is worth a thousand words, let's start there:
-.. image:: /internals/_images/djangotickets.png
- :height: 451
- :width: 590
+.. image:: /internals/_images/triage_process.*
+ :height: 501
+ :width: 400
:alt: Django's ticket triage workflow
We've got two roles in this diagram:
@@ -119,7 +119,14 @@ Beyond that there are several considerations:
* **Accepted + No Flags**
The ticket is valid, but no one has submitted a patch for it yet. Often this
- means you could safely start writing a patch for it.
+ means you could safely start writing a patch for it. This is generally more
+ true for the case of accepted bugs than accepted features. A ticket for a bug
+ that has been accepted means that the issue has been verified by at least one
+ triager as a legitimate bug - and should probably be fixed if possible. An
+ accepted new feature may only mean that one triager thought the feature would
+ be good to have, but this alone does not represent a consensus view or imply
+ with any certainty that a patch will be accepted for that feature. Seek more
+ feedback before writing an extensive patch if you are in doubt.
* **Accepted + Has Patch**
@@ -128,30 +135,13 @@ Beyond that there are several considerations:
and docs, running the test suite with the included patch, and leaving
feedback on the ticket.
-* **Accepted + Has Patch + (any other flag)**
+* **Accepted + Has Patch + Needs ...**
This means the ticket has been reviewed, and has been found to need further
work. "Needs tests" and "Needs documentation" are self-explanatory. "Patch
needs improvement" will generally be accompanied by a comment on the ticket
explaining what is needed to improve the code.
-Design Decision Needed
-~~~~~~~~~~~~~~~~~~~~~~
-
-This stage is for issues which may be contentious, may be backwards
-incompatible, or otherwise involve high-level design decisions. These issues
-should be discussed either in the ticket comments or on `django-developers`_.
-
-If a ticket has been marked as "DDN", decisions are generally eventually
-made by the core committers, however that is not a requirement. See the
-:ref:`New contributors' FAQ<new-contributors-faq>` for "My ticket has been in
-DDN forever! What should I do?"
-
-This stage will often be used for feature requests. It can also be used for
-issues that *might* be bugs, depending on opinion or interpretation. Obvious
-bugs (such as crashes, incorrect query results, or non-compliance with a
-standard) skip this stage and move straight to "Accepted".
-
Ready For Checkin
~~~~~~~~~~~~~~~~~
@@ -165,39 +155,54 @@ RFC forever! What should I do?"
Someday/Maybe
~~~~~~~~~~~~~
-Generally only used for vague/high-level features or design ideas. These
-tickets are uncommon and overall less useful since they don't describe
+This stage isn't shown on the diagram. It's only used by core developers to
+keep track of high-level ideas or long term feature requests.
+
+These tickets are uncommon and overall less useful since they don't describe
concrete actionable issues. They are enhancement requests that we might
consider adding someday to the framework if an excellent patch is submitted.
-These tickets are not a high priority.
+They are not a high priority.
Other triage attributes
-----------------------
A number of flags, appearing as checkboxes in Trac, can be set on a ticket:
-* Has patch
- This means the ticket has an associated
- :doc:`patch<writing-code/submitting-patches>`. These will be reviewed
- to see if the patch is "good".
+Has patch
+~~~~~~~~~
+
+This means the ticket has an associated
+:doc:`patch<writing-code/submitting-patches>`. These will be reviewed
+to see if the patch is "good".
+
+Needs documentation
+~~~~~~~~~~~~~~~~~~~
-* Needs documentation:
- This flag is used for tickets with patches that need associated
- documentation. Complete documentation of features is a prerequisite
- before we can check them into the codebase.
+This flag is used for tickets with patches that need associated
+documentation. Complete documentation of features is a prerequisite
+before we can check them into the codebase.
-* Needs tests
- This flags the patch as needing associated unit tests. Again, this
- is a required part of a valid patch.
+Needs tests
+~~~~~~~~~~~
-* Patch needs improvement
- This flag means that although the ticket *has* a patch, it's not quite
- ready for checkin. This could mean the patch no longer applies
- cleanly, there is a flaw in the implementation, or that the code
- doesn't meet our standards.
+This flags the patch as needing associated unit tests. Again, this
+is a required part of a valid patch.
-* Easy pickings
- Tickets that would require small, easy, patches.
+Patch needs improvement
+~~~~~~~~~~~~~~~~~~~~~~~
+
+This flag means that although the ticket *has* a patch, it's not quite
+ready for checkin. This could mean the patch no longer applies
+cleanly, there is a flaw in the implementation, or that the code
+doesn't meet our standards.
+
+Easy pickings
+~~~~~~~~~~~~~
+
+Tickets that would require small, easy, patches.
+
+Type
+~~~~
Tickets should be categorized by *type* between:
@@ -211,19 +216,47 @@ Tickets should be categorized by *type* between:
For when nothing is broken but something could be made cleaner,
better, faster, stronger.
-Tickets should also be classified into *components* indicating which area of
+Component
+~~~~~~~~~
+
+Tickets should be classified into *components* indicating which area of
the Django codebase they belong to. This makes tickets better organized and
easier to find.
+Severity
+~~~~~~~~
+
The *severity* attribute is used to identify blockers, that is, issues which
should get fixed before releasing the next version of Django. Typically those
issues are bugs causing regressions from earlier versions or potentially
causing severe data losses. This attribute is quite rarely used and the vast
majority of tickets have a severity of "Normal".
-Finally, it is possible to use the *version* attribute to indicate in which
+Version
+~~~~~~~
+
+It is possible to use the *version* attribute to indicate in which
version the reported bug was identified.
+UI/UX
+~~~~~
+
+This flag is used for tickets that relate to User Interface and User
+Experiences questions. For example, this flag would be appropriate for
+user-facing features in forms or the admin interface.
+
+Cc
+~~
+
+You may add your username or email address to this field to be notified when
+new contributions are made to the ticket.
+
+Keywords
+~~~~~~~~
+
+With this field you may label a ticket with multiple keywords. This can be
+useful, for example, to group several tickets of a same theme.
+
.. _closing-tickets:
Closing Tickets
@@ -301,20 +334,23 @@ developers and bring the issue to django-developers_ instead.
How can I help with triaging?
-----------------------------
-Although the core developers make the big decisions in the ticket triage
-process, there's a lot that general community members can do to help the
-triage process. Really, **ANYONE** can help.
+The triage process is primarily driven by community members. Really,
+**ANYONE** can help.
+
+Core developers may provide feedback on issues they're familiar with, or make
+decisions on controversial ones, but they aren't responsible for triaging
+tickets in general.
-Start by `creating an account on Trac`_. If you have an account but have
-forgotten your password, you can reset it using the `password reset page`_.
+To get involved, start by `creating an account on Trac`_. If you have an
+account but have forgotten your password, you can reset it using the `password
+reset page`_.
Then, you can help out by:
* Closing "Unreviewed" tickets as "invalid", "worksforme" or "duplicate."
-* Promoting "Unreviewed" tickets to "Design decision needed" if a design
- decision needs to be made, or "Accepted" in case of obvious bugs or
- sensible, clearly defined, feature requests.
+* Closing "Unreviewed" tickets as "needsinfo" when they're feature requests
+ requiring a discussion on `django-developers`_.
* Correcting the "Needs tests", "Needs documentation", or "Has patch"
flags for tickets where they are incorrectly set.
@@ -322,22 +358,18 @@ Then, you can help out by:
* Setting the "`Easy pickings`_" flag for tickets that are small and
relatively straightforward.
+* Set the *type* of tickets that are still uncategorized.
+
* Checking that old tickets are still valid. If a ticket hasn't seen
any activity in a long time, it's possible that the problem has been
fixed but the ticket hasn't yet been closed.
-* Contacting the owners of tickets that have been claimed but have not
- seen any recent activity. If the owner doesn't respond after a week
- or so, remove the owner's claim on the ticket.
-
* Identifying trends and themes in the tickets. If there a lot of bug
reports about a particular part of Django, it may indicate we should
consider refactoring that part of the code. If a trend is emerging,
you should raise it for discussion (referencing the relevant tickets)
on `django-developers`_.
-* Set the *type* of tickets that are still uncategorized.
-
* Verify if patches submitted by other users are correct. If they do and
also contain appropriate documentation and tests then move them to the
"Ready for Checkin" stage. If they don't then leave a comment to explain
diff --git a/docs/internals/contributing/writing-code/coding-style.txt b/docs/internals/contributing/writing-code/coding-style.txt
index a699e39bd8..21146600b4 100644
--- a/docs/internals/contributing/writing-code/coding-style.txt
+++ b/docs/internals/contributing/writing-code/coding-style.txt
@@ -136,14 +136,17 @@ Model style
* ``def get_absolute_url()``
* Any custom methods
-* If ``choices`` is defined for a given model field, define the choices as
- a tuple of tuples, with an all-uppercase name, either near the top of
- the model module or just above the model class. Example::
+* If ``choices`` is defined for a given model field, define each choice as
+ a tuple of tuples, with an all-uppercase name as a class attribute on the
+ model. Example::
- DIRECTION_CHOICES = (
- ('U', 'Up'),
- ('D', 'Down'),
- )
+ class MyModel(models.Model):
+ DIRECTION_UP = 'U'
+ DIRECTION_DOWN = 'D'
+ DIRECTION_CHOICES = (
+ (DIRECTION_UP, 'Up'),
+ (DIRECTION_DOWN, 'Down'),
+ )
Use of ``django.conf.settings``
-------------------------------
@@ -177,9 +180,9 @@ That means that the ability for third parties to import the module at the top
level is incompatible with the ability to configure the settings object
manually, or makes it very difficult in some circumstances.
-Instead of the above code, a level of laziness or indirection must be used, such
-as :class:`django.utils.functional.LazyObject`,
-:func:`django.utils.functional.lazy` or ``lambda``.
+Instead of the above code, a level of laziness or indirection must be used,
+such as ``django.utils.functional.LazyObject``,
+``django.utils.functional.lazy()`` or ``lambda``.
Miscellaneous
-------------
diff --git a/docs/internals/contributing/writing-code/submitting-patches.txt b/docs/internals/contributing/writing-code/submitting-patches.txt
index a90dc32605..ed8aad99b3 100644
--- a/docs/internals/contributing/writing-code/submitting-patches.txt
+++ b/docs/internals/contributing/writing-code/submitting-patches.txt
@@ -176,8 +176,10 @@ Compressing JavaScript
~~~~~~~~~~~~~~~~~~~~~~
To simplify the process of providing optimized javascript code, Django
-includes a handy script which should be used to create a "minified" version.
-This script is located at ``django/contrib/admin/static/admin/js/compress.py``.
+includes a handy python script which should be used to create a "minified"
+version. To run it::
+
+ python django/contrib/admin/bin/compress.py
Behind the scenes, ``compress.py`` is a front-end for Google's
`Closure Compiler`_ which is written in Java. However, the Closure Compiler
diff --git a/docs/internals/contributing/writing-code/unit-tests.txt b/docs/internals/contributing/writing-code/unit-tests.txt
index 4e702ff83e..f56bf1cdeb 100644
--- a/docs/internals/contributing/writing-code/unit-tests.txt
+++ b/docs/internals/contributing/writing-code/unit-tests.txt
@@ -7,16 +7,14 @@ code base. It's our policy to make sure all tests pass at all times.
The tests cover:
-* Models and the database API (``tests/modeltests``),
-* Everything else in core Django code (``tests/regressiontests``),
-* :ref:`contrib-apps` (``django/contrib/<app>/tests`` or
- ``tests/regressiontests/<app>_...``).
+* Models, the database API and everything else in core Django core (``tests/``),
+* :ref:`contrib-apps` (``django/contrib/<app>/tests`` or ``tests/<app>_...``).
We appreciate any and all contributions to the test suite!
The Django tests all use the testing infrastructure that ships with Django for
-testing applications. See :doc:`Testing Django applications </topics/testing>`
-for an explanation of how to write new tests.
+testing applications. See :doc:`Testing Django applications
+</topics/testing/overview>` for an explanation of how to write new tests.
.. _running-unit-tests:
@@ -105,9 +103,9 @@ internationalization, type:
./runtests.py --settings=path.to.settings generic_relations i18n
-How do you find out the names of individual tests? Look in
-``tests/modeltests`` and ``tests/regressiontests`` — each directory name
-there is the name of a test. Contrib app names are also valid test names.
+How do you find out the names of individual tests? Look in ``tests/`` — each
+directory name there is the name of a test. Contrib app names are also valid
+test names.
If you just want to run a particular class of tests, you can specify a list of
paths to individual test classes. For example, to run the ``TranslationTests``
@@ -128,13 +126,13 @@ Running the Selenium tests
Some admin tests require Selenium 2, Firefox and Python >= 2.6 to work via a
real Web browser. To allow those tests to run and not be skipped, you must
-install the selenium_ package (version > 2.13) into your Python path.
-
-Then, run the tests normally, for example:
+install the selenium_ package (version > 2.13) into your Python path and run
+the tests with the ``--selenium`` option:
.. code-block:: bash
- ./runtests.py --settings=test_sqlite admin_inlines
+ ./runtests.py --settings=test_sqlite --selenium admin_inlines
+
.. _running-unit-tests-dependencies:
@@ -145,9 +143,6 @@ If you want to run the full suite of tests, you'll need to install a number of
dependencies:
* PyYAML_
-* Markdown_
-* Textile_
-* Docutils_
* setuptools_
* memcached_, plus a :ref:`supported Python binding <memcached>`
* gettext_ (:ref:`gettext_on_windows`)
@@ -160,9 +155,6 @@ Each of these dependencies is optional. If you're missing any of them, the
associated tests will be skipped.
.. _PyYAML: http://pyyaml.org/wiki/PyYAML
-.. _Markdown: http://pypi.python.org/pypi/Markdown/1.7
-.. _Textile: http://pypi.python.org/pypi/textile
-.. _docutils: http://pypi.python.org/pypi/docutils/0.4
.. _setuptools: http://pypi.python.org/pypi/setuptools/
.. _memcached: http://memcached.org/
.. _gettext: http://www.gnu.org/software/gettext/manual/gettext.html
@@ -200,7 +192,7 @@ multiple modules by using a ``tests`` directory in the normal Python way.
For the tests to be found, a ``models.py`` file must exist, even if it's empty.
If you have URLs that need to be mapped, put them in ``tests/urls.py``.
-To run tests for just one contrib app (e.g. ``markup``), use the same
+To run tests for just one contrib app (e.g. ``auth``), use the same
method as above::
- ./runtests.py --settings=settings markup
+ ./runtests.py --settings=settings auth
diff --git a/docs/internals/contributing/writing-code/working-with-git.txt b/docs/internals/contributing/writing-code/working-with-git.txt
index d4a95ae45a..dcfdd9e85b 100644
--- a/docs/internals/contributing/writing-code/working-with-git.txt
+++ b/docs/internals/contributing/writing-code/working-with-git.txt
@@ -81,7 +81,7 @@ commit them::
git commit
When writing the commit message, follow the :ref:`commit message
-guidelines <committing-guidlines>` to ease the work of the committer. If
+guidelines <committing-guidelines>` to ease the work of the committer. If
you're uncomfortable with English, try at least to describe precisely what the
commit does.
@@ -121,7 +121,7 @@ a pull request at GitHub. A good pull request means:
* well-formed messages for each commit: a summary line and then paragraphs
wrapped at 72 characters thereafter -- see the :ref:`committing guidelines
- <committing-guidlines>` for more details,
+ <committing-guidelines>` for more details,
* documentation and tests, if needed -- actually tests are always needed,
except for documentation changes.
diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt
index 414da30ff8..1533e25dc8 100644
--- a/docs/internals/deprecation.txt
+++ b/docs/internals/deprecation.txt
@@ -14,13 +14,13 @@ See the :doc:`Django 1.2 release notes</releases/1.2>` for more details on
these changes.
* ``CsrfResponseMiddleware`` and ``CsrfMiddleware`` will be removed. Use
- the {% csrf_token %} template tag inside forms to enable CSRF
+ the ``{% csrf_token %}`` template tag inside forms to enable CSRF
protection. ``CsrfViewMiddleware`` remains and is enabled by default.
* The old imports for CSRF functionality (``django.contrib.csrf.*``),
which moved to core in 1.2, will be removed.
-* The :mod:`django.contrib.gis.db.backend` module will be removed in favor
+* The ``django.contrib.gis.db.backend`` module will be removed in favor
of the specific backends.
* ``SMTPConnection`` will be removed in favor of a generic Email backend API.
@@ -111,7 +111,7 @@ See the :doc:`Django 1.3 release notes</releases/1.3>` for more details on
these changes.
* Starting Django without a :setting:`SECRET_KEY` will result in an exception
- rather than a `DeprecationWarning`. (This is accelerated from the usual
+ rather than a ``DeprecationWarning``. (This is accelerated from the usual
deprecation path; see the :doc:`Django 1.4 release notes</releases/1.4>`.)
* The ``mod_python`` request handler will be removed. The ``mod_wsgi``
@@ -122,23 +122,23 @@ these changes.
The :attr:`~django.test.client.Response.templates` attribute should be
used instead.
-* The :class:`~django.test.simple.DjangoTestRunner` will be removed.
+* The ``django.test.simple.DjangoTestRunner`` will be removed.
Instead use a unittest-native class. The features of the
- :class:`django.test.simple.DjangoTestRunner` (including fail-fast and
+ ``django.test.simple.DjangoTestRunner`` (including fail-fast and
Ctrl-C test termination) can currently be provided by the unittest-native
- :class:`TextTestRunner`.
+ :class:`~unittest.TextTestRunner`.
* The undocumented function
- :func:`django.contrib.formtools.utils.security_hash` will be removed,
- instead use :func:`django.contrib.formtools.utils.form_hmac`
+ ``django.contrib.formtools.utils.security_hash`` will be removed,
+ instead use ``django.contrib.formtools.utils.form_hmac``
* The function-based generic view modules will be removed in favor of their
class-based equivalents, outlined :doc:`here
</topics/class-based-views/index>`.
-* The :class:`~django.core.servers.basehttp.AdminMediaHandler` will be
+* The ``django.core.servers.basehttp.AdminMediaHandler`` will be
removed. In its place use
- :class:`~django.contrib.staticfiles.handlers.StaticFilesHandler`.
+ ``django.contrib.staticfiles.handlers.StaticFilesHandler``.
* The template tags library ``adminmedia`` and the template tag ``{%
admin_media_prefix %}`` will be removed in favor of the generic static files
@@ -150,8 +150,7 @@ these changes.
an implied string. In 1.4, this behavior is provided by a version of the tag
in the ``future`` template tag library.
-* The :djadmin:`reset` and :djadmin:`sqlreset` management commands
- will be removed.
+* The ``reset`` and ``sqlreset`` management commands will be removed.
* Authentication backends will need to support an inactive user
being passed to all methods dealing with permissions.
@@ -162,15 +161,14 @@ these changes.
a :class:`~django.contrib.gis.geos.GEOSException` when called
on a geometry with no SRID value.
-* :class:`~django.http.CompatCookie` will be removed in favor of
- :class:`~django.http.SimpleCookie`.
+* ``django.http.CompatCookie`` will be removed in favor of
+ ``django.http.SimpleCookie``.
-* :class:`django.core.context_processors.PermWrapper` and
- :class:`django.core.context_processors.PermLookupDict` will be removed in
+* ``django.core.context_processors.PermWrapper`` and
+ ``django.core.context_processors.PermLookupDict`` will be removed in
favor of the corresponding
- :class:`django.contrib.auth.context_processors.PermWrapper` and
- :class:`django.contrib.auth.context_processors.PermLookupDict`,
- respectively.
+ ``django.contrib.auth.context_processors.PermWrapper`` and
+ ``django.contrib.auth.context_processors.PermLookupDict``, respectively.
* The :setting:`MEDIA_URL` or :setting:`STATIC_URL` settings will be
required to end with a trailing slash to ensure there is a consistent
@@ -200,30 +198,37 @@ these changes.
See the :doc:`Django 1.4 release notes</releases/1.4>` for more details on
these changes.
+* ``django.contrib.databrowse`` will be removed.
+
+* ``django.contrib.localflavor`` will be removed following an accelerated
+ deprecation.
+
+* ``django.contrib.markup`` will be removed following an accelerated
+ deprecation.
+
* The compatibility modules ``django.utils.copycompat`` and
``django.utils.hashcompat`` as well as the functions
``django.utils.itercompat.all`` and ``django.utils.itercompat.any`` will
be removed. The Python builtin versions should be used instead.
-* The :func:`~django.views.decorators.csrf.csrf_response_exempt` and
- :func:`~django.views.decorators.csrf.csrf_view_exempt` decorators will
+* The ``csrf_response_exempt`` and ``csrf_view_exempt`` decorators will
be removed. Since 1.4 ``csrf_response_exempt`` has been a no-op (it
returns the same function), and ``csrf_view_exempt`` has been a
synonym for ``django.views.decorators.csrf.csrf_exempt``, which should
be used to replace it.
-* The :class:`~django.core.cache.backends.memcached.CacheClass` backend
+* The ``django.core.cache.backends.memcached.CacheClass`` backend
was split into two in Django 1.3 in order to introduce support for
- PyLibMC. The historical :class:`~django.core.cache.backends.memcached.CacheClass`
- will be removed in favor of :class:`~django.core.cache.backends.memcached.MemcachedCache`.
+ PyLibMC. The historical ``CacheClass`` will be removed in favor of
+ ``django.core.cache.backends.memcached.MemcachedCache``.
* The UK-prefixed objects of ``django.contrib.localflavor.uk`` will only
be accessible through their GB-prefixed names (GB is the correct
ISO 3166 code for United Kingdom).
-* The :setting:`IGNORABLE_404_STARTS` and :setting:`IGNORABLE_404_ENDS`
- settings have been superseded by :setting:`IGNORABLE_404_URLS` in
- the 1.4 release. They will be removed.
+* The ``IGNORABLE_404_STARTS`` and ``IGNORABLE_404_ENDS`` settings have been
+ superseded by :setting:`IGNORABLE_404_URLS` in the 1.4 release. They will be
+ removed.
* The :doc:`form wizard </ref/contrib/formtools/form-wizard>` has been
refactored to use class-based views with pluggable backends in 1.4.
@@ -237,8 +242,8 @@ these changes.
:setting:`LOGGING` setting should include this filter explicitly if
it is desired.
-* The builtin truncation functions :func:`django.utils.text.truncate_words`
- and :func:`django.utils.text.truncate_html_words` will be removed in
+* The builtin truncation functions ``django.utils.text.truncate_words()``
+ and ``django.utils.text.truncate_html_words()`` will be removed in
favor of the ``django.utils.text.Truncator`` class.
* The :class:`~django.contrib.gis.geoip.GeoIP` class was moved to
@@ -251,11 +256,8 @@ these changes.
:data:`~django.conf.urls.handler500`, are now available through
:mod:`django.conf.urls` .
-* The Databrowse contrib module will be removed.
-
-* The functions :func:`~django.core.management.setup_environ` and
- :func:`~django.core.management.execute_manager` will be removed from
- :mod:`django.core.management`. This also means that the old (pre-1.4)
+* The functions ``setup_environ()`` and ``execute_manager()`` will be removed
+ from :mod:`django.core.management`. This also means that the old (pre-1.4)
style of :file:`manage.py` file will no longer work.
* Setting the ``is_safe`` and ``needs_autoescape`` flags as attributes of
@@ -265,8 +267,11 @@ these changes.
in 1.4. The backward compatibility will be removed --
``HttpRequest.raw_post_data`` will no longer work.
-* ``django.contrib.markup`` will be removed following an accelerated
- deprecation.
+* The value for the ``post_url_continue`` parameter in
+ ``ModelAdmin.response_add()`` will have to be either ``None`` (to redirect
+ to the newly created object's edit page) or a pre-formatted url. String
+ formats, such as the previous default ``'../%s/'``, will not be accepted any
+ more.
1.7
---
@@ -284,8 +289,14 @@ these changes.
specified as a plain string instead of a tuple will be removed and raise an
exception.
-* The ``mimetype`` argument to :class:`~django.http.HttpResponse` ``__init__``
- will be removed (``content_type`` should be used instead).
+* The ``mimetype`` argument to the ``__init__`` methods of
+ :class:`~django.http.HttpResponse`,
+ :class:`~django.template.response.SimpleTemplateResponse`, and
+ :class:`~django.template.response.TemplateResponse`, will be removed.
+ ``content_type`` should be used instead. This also applies to the
+ :func:`~django.shortcuts.render_to_response` shortcut and
+ the sitemamp views, :func:`~django.contrib.sitemaps.views.index` and
+ :func:`~django.contrib.sitemaps.views.sitemap`.
* When :class:`~django.http.HttpResponse` is instantiated with an iterator,
or when :attr:`~django.http.HttpResponse.content` is set to an iterator,
@@ -302,6 +313,68 @@ these changes.
* The ``depth`` keyword argument will be removed from
:meth:`~django.db.models.query.QuerySet.select_related`.
+* The undocumented ``get_warnings_state()``/``restore_warnings_state()``
+ functions from :mod:`django.test.utils` and the ``save_warnings_state()``/
+ ``restore_warnings_state()``
+ :ref:`django.test.*TestCase <django-testcase-subclasses>` methods are
+ deprecated. Use the :class:`warnings.catch_warnings` context manager
+ available starting with Python 2.6 instead.
+
+* The undocumented ``check_for_test_cookie`` method in
+ :class:`~django.contrib.auth.forms.AuthenticationForm` will be removed
+ following an accelerated deprecation. Users subclassing this form should
+ remove calls to this method, and instead ensure that their auth related views
+ are CSRF protected, which ensures that cookies are enabled.
+
+1.8
+---
+
+* ``django.contrib.comments`` will be removed.
+
+* The following transaction management APIs will be removed:
+
+ - ``TransactionMiddleware``,
+ - the decorators and context managers ``autocommit``, ``commit_on_success``,
+ and ``commit_manually``, defined in ``django.db.transaction``,
+ - the functions ``commit_unless_managed`` and ``rollback_unless_managed``,
+ also defined in ``django.db.transaction``,
+ - the ``TRANSACTIONS_MANAGED`` setting.
+
+ Upgrade paths are described in the :ref:`transaction management docs
+ <transactions-upgrading-from-1.5>`.
+
+* The :ttag:`cycle` and :ttag:`firstof` template tags will auto-escape their
+ arguments. In 1.6 and 1.7, this behavior is provided by the version of these
+ tags in the ``future`` template tag library.
+
+* The ``SEND_BROKEN_LINK_EMAILS`` setting will be removed. Add the
+ :class:`django.middleware.common.BrokenLinkEmailsMiddleware` middleware to
+ your :setting:`MIDDLEWARE_CLASSES` setting instead.
+
+* ``Model._meta.module_name`` was renamed to ``model_name``.
+
+* Remove the backward compatible shims introduced to rename ``get_query_set``
+ and similar queryset methods. This affects the following classes:
+ ``BaseModelAdmin``, ``ChangeList``, ``BaseCommentNode``,
+ ``GenericForeignKey``, ``Manager``, ``SingleRelatedObjectDescriptor`` and
+ ``ReverseSingleRelatedObjectDescriptor``.
+
+* Remove the backward compatible shims introduced to rename the attributes
+ ``ChangeList.root_query_set`` and ``ChangeList.query_set``.
+
+* ``django.conf.urls.shortcut`` and ``django.views.defaults.shortcut`` will be
+ removed.
+
+* The following private APIs will be removed:
+
+ - ``django.db.close_connection()``
+ - ``django.db.backends.creation.BaseDatabaseCreation.set_autocommit()``
+ - ``django.db.transaction.is_managed()``
+ - ``django.db.transaction.managed()``
+
+* ``django.forms.widgets.RadioInput`` will be removed in favor of
+ ``django.forms.widgets.RadioChoiceInput``.
+
2.0
---
diff --git a/docs/internals/git.txt b/docs/internals/git.txt
index 948f9a1f7e..2b1a279d89 100644
--- a/docs/internals/git.txt
+++ b/docs/internals/git.txt
@@ -134,7 +134,7 @@ part of Django itself, and so are no longer separately maintained:
of Django since the 0.95 release.
* ``multi-auth``: A refactoring of :doc:`Django's bundled
- authentication framework </topics/auth>` which added support for
+ authentication framework </topics/auth/index>` which added support for
:ref:`authentication backends <authentication-backends>`. This has
been part of Django since the 0.95 release.
diff --git a/docs/internals/howto-release-django.txt b/docs/internals/howto-release-django.txt
new file mode 100644
index 0000000000..a49251da76
--- /dev/null
+++ b/docs/internals/howto-release-django.txt
@@ -0,0 +1,320 @@
+=====================
+How is Django Formed?
+=====================
+
+This document explains how to release Django. If you're unlucky enough to
+be driving a release, you should follow these instructions to get the
+package out.
+
+**Please, keep these instructions up-to-date if you make changes!** The point
+here is to be descriptive, not proscriptive, so feel free to streamline or
+otherwise make changes, but **update this document accordingly!**
+
+Overview
+========
+
+There are three types of releases that you might need to make
+
+* Security releases, disclosing and fixing a vulnerability. This'll
+ generally involve two or three simultaneous releases -- e.g.
+ 1.5.x, 1.6.x, and, depending on timing, perhaps a 1.7 alpha/beta/rc.
+
+* Regular version releases, either a final release (e.g. 1.5) or a
+ bugfix update (e.g. 1.5.1).
+
+* Pre-releases, e.g. 1.6 beta or something.
+
+In general the steps are about the same regardless, but there are a few
+differences noted. The short version is:
+
+#. If this is a security release, pre-notify the security distribution list
+ at least one week before the actual release.
+
+#. Proofread (and create if needed) the release notes, looking for
+ organization, writing errors, deprecation timelines, etc. Draft a blog post
+ and email announcement.
+
+#. Update version numbers and create the release package(s)!
+
+#. Upload the package(s) to the ``djangoproject.com`` server.
+
+#. Unless this is a pre-release, add the new version(s) to PyPI.
+
+#. Declare the new version in the admin on ``djangoproject.com``.
+
+#. Post the blog entry and send out the email announcements.
+
+#. Update version numbers post-release.
+
+There are a lot of details, so please read on.
+
+Prerequisites
+=============
+
+You'll need a few things hooked up to make this work:
+
+* A GPG key recorded as an acceptable releaser in the `Django releasers`__
+ document. (If this key is not your default signing key, you'll need to add
+ ``-u you@example.com`` to every GPG signing command below, where
+ ``you@example.com`` is the email address associated with the key you want to
+ use.)
+
+* Access to Django's record on PyPI.
+
+* Access to the ``djangoproject.com`` server to upload files and trigger a
+ deploy.
+
+* Access to the admin on ``djangoproject.com`` as a "Site maintainer".
+
+* Access to post to ``django-announce``.
+
+* If this is a security release, access to the pre-notification distribution
+ list.
+
+If this is your first release, you'll need to coordinate with James and/or
+Jacob to get all these things lined up.
+
+__ https://www.djangoproject.com/m/pgp/django-releasers.txt
+
+Pre-release tasks
+=================
+
+A few items need to be taken care of before even beginning the release process.
+This stuff starts about a week before the release; most of it can be done
+any time leading up to the actual release:
+
+#. If this is a security release, send out pre-notification **one week**
+ before the release. We maintain a list of who gets these pre-notification
+ emails at *FIXME WHERE?*. This email should be signed by the key you'll use
+ for the release, and should include patches for each issue being fixed.
+
+#. As the release approaches, watch Trac to make sure no release blockers
+ are left for the upcoming release.
+
+#. Check with the other committers to make sure they don't have any
+ un-committed changes for the release.
+
+#. Proofread the release notes, including looking at the online
+ version to catch any broken links or reST errors, and make sure the
+ release notes contain the correct date.
+
+#. Double-check that the release notes mention deprecation timelines
+ for any APIs noted as deprecated, and that they mention any changes
+ in Python version support.
+
+#. Double-check that the release notes index has a link to the notes
+ for the new release; this will be in ``docs/releases/index.txt``.
+
+Preparing for release
+=====================
+
+Write the announcement blog post for the release. You can enter it into the
+admin at any time and mark it as inactive. Here are a few examples: `example
+security release announcement`__, `example regular release announcement`__,
+`example pre-release announcement`__.
+
+__ https://www.djangoproject.com/weblog/2013/feb/19/security/
+__ https://www.djangoproject.com/weblog/2012/mar/23/14/
+__ https://www.djangoproject.com/weblog/2012/nov/27/15-beta-1/
+
+Actually rolling the release
+============================
+
+OK, this is the fun part, where we actually push out a release!
+
+#. Check `Jenkins`__ is green for the version(s) you're putting out. You
+ probably shouldn't issue a release until it's green.
+
+ __ http://ci.djangoproject.com
+
+#. A release always begins from a release branch, so you should make sure
+ you're on a stable branch and up-to-date. For example::
+
+ git checkout stable/1.5.x
+ git pull
+
+#. If this is a security release, merge the appropriate patches from
+ ``django-private``. Rebase these patches as necessary to make each one a
+ simple commit on the release branch rather than a merge commit. To ensure
+ this, merge them with the ``--ff-only`` flag; for example::
+
+ git checkout stable/1.5.x
+ git merge --ff-only security/1.5.x
+
+ (This assumes ``security/1.5.x`` is a branch in the ``django-private`` repo
+ containing the necessary security patches for the next release in the 1.5
+ series.)
+
+ If git refuses to merge with ``--ff-only``, switch to the security-patch
+ branch and rebase it on the branch you are about to merge it into (``git
+ checkout security/1.5.x; git rebase stable/1.5.x``) and then switch back and
+ do the merge. Make sure the commit message for each security fix explains
+ that the commit is a security fix and that an announcement will follow
+ (`example security commit`__)
+
+ __ https://github.com/django/django/commit/3ef4bbf495cc6c061789132e3d50a8231a89406b
+
+#. Update version numbers for the release. This has to happen in three
+ places: ``django/__init__.py``, ``docs/conf.py``, and ``setup.py``.
+ Please see `notes on setting the VERSION tuple`_ below for details
+ on ``VERSION``. Here's `an example commit updating version numbers`__
+
+ __ https://github.com/django/django/commit/18d920ea4839fb54f9d2a5dcb555b6a5666ee469
+
+#. If this is a pre-release package, update the "Development Status" trove
+ classifier in ``setup.py`` to reflect this. Otherwise, make sure the
+ classifier is set to ``Development Status :: 5 - Production/Stable``.
+
+#. Tag the release using ``git tag``. For example::
+
+ git tag --sign --message="Django 1.5.1" 1.5.1
+
+ You can check your work by running ``git tag --verify <tag>``.
+
+#. Push your work, including the tag: ``git push --tags``.
+
+#. Make sure you have an absolutely clean tree by running ``git clean -dfx``.
+
+#. Run ``python setup.py sdist`` to generate the release package. This will
+ create the release package in a ``dist/`` directory.
+
+#. Generate the hashes of the release package::
+
+ $ md5sum dist/Django-<version>.tar.gz
+ $ sha1sum dist/Django-<version>.tar.gz
+
+ *FIXME: perhaps we should switch to sha256?*
+
+#. Create a "checksums" file containing the hashes and release information.
+ You can start with `a previous checksums file`__ and replace the
+ dates, keys, links, and checksums. *FIXME: make a template file.*
+
+ __ https://www.djangoproject.com/m/pgp/Django-1.5b1.checksum.txt
+
+#. Sign the checksum file (``gpg --clearsign
+ Django-<version>.checksum.txt``). This generates a signed document,
+ ``Django-<version>.checksum.txt.asc`` which you can then verify using ``gpg
+ --verify Django-<version>.checksum.txt.asc``.
+
+If you're issuing multiple releases, repeat these steps for each release.
+
+Making the release(s) available to the public
+=============================================
+
+Now you're ready to actually put the release out there. To do this:
+
+#. Upload the release package(s) to the djangoproject server; releases go
+ in ``/home/www/djangoproject.com/src/media/releases``, under a
+ directory for the appropriate version number (e.g.
+ ``/home/www/djangoproject.com/src/media/releases/1.5`` for a ``1.5.x``
+ release.).
+
+#. Upload the checksum file(s); these go in
+ ``/home/www/djangoproject.com/src/media/pgp``.
+
+#. Test that the release packages install correctly using ``easy_install``
+ and ``pip``. Here's one method (which requires `virtualenvwrapper`__)::
+
+ $ mktmpenv
+ $ easy_install https://www.djangoproject.com/m/releases/1.5/Django-1.5.1.tar.gz
+ $ deactivate
+ $ mktmpenv
+ $ pip install https://www.djangoproject.com/m/releases/1.5/Django-1.5.1.tar.gz
+ $ deactivate
+
+ This just tests that the tarballs are available (i.e. redirects are up) and
+ that they install correctly, but it'll catch silly mistakes.
+
+ __ https://pypi.python.org/pypi/virtualenvwrapper
+
+#. Ask a few people on IRC to verify the checksums by visiting the checksums
+ file (e.g. https://www.djangoproject.com/m/pgp/Django-1.5b1.checksum.txt)
+ and following the instructions in it. For bonus points, they can also unpack
+ the downloaded release tarball and verify that its contents appear to be
+ correct (proper version numbers, no stray ``.pyc`` or other undesirable
+ files).
+
+#. If this is a release that should land on PyPI (i.e. anything except for
+ a pre-release), register the new package with PyPI by running
+ ``python setup.py register``.
+
+#. Upload the sdist you generated a few steps back through the PyPI web
+ interface. You'll log into PyPI, click "Django" in the right sidebar,
+ find the release you just registered, and click "files" to upload the
+ sdist.
+
+ .. note::
+
+ Why can't we just use ``setup.py sdist upload``? Well, if we do it above
+ that pushes the sdist to PyPI before we've had a chance to sign, review
+ and test it. And we can't just ``setup.py upload`` without ``sdist``
+ because ``setup.py`` prevents that. Nor can we ``sdist upload`` because
+ that would generate a *new* sdist that might not match the file we just
+ signed. Finally, uploading through the web interface is somewhat more
+ secure: it sends the file over HTTPS.
+
+#. Go to the `Add release page in the admin`__, enter the new release number
+ exactly as it appears in the name of the tarball (Django-<version>.tar.gz).
+ So for example enter "1.5.1" or "1.4-rc-2", etc.
+
+ __ https://www.djangoproject.com/admin/releases/release/add/
+
+#. Make the blog post announcing the release live.
+
+#. For a new version release (e.g. 1.5, 1.6), update the default stable version
+ of the docs by flipping the ``is_default`` flag to ``True`` on the
+ appropriate ``DocumentRelease`` object in the ``docs.djangoproject.com``
+ database (this will automatically flip it to ``False`` for all
+ others). *FIXME: I had to do this via fab managepy:shell,docs but we should
+ probably make it possible to do via the admin.*
+
+#. Post the release announcement to the django-announce,
+ django-developers and django-users mailing lists. This should
+ include links to the announcement blog post and the release notes.
+
+Post-release
+============
+
+You're almost done! All that's left to do now is:
+
+#. Update the ``VERSION`` tuple in ``django/__init__.py`` again,
+ incrementing to whatever the next expected release will be. For
+ example, after releasing 1.5.1, update ``VERSION`` to
+ ``VERSION = (1, 5, 2, 'alpha', 0)``.
+
+#. For the first alpha release of a new version (when we create the
+ ``stable/1.?.x`` git branch), you'll want to create a new
+ ``DocumentRelease`` object in the ``docs.djangoproject.com`` database for
+ the new version's docs, and update the ``docs/fixtures/doc_releases.json``
+ JSON fixture. *FIXME: what is the purpose of maintaining this fixture?*
+
+#. Add the release in `Trac's versions list`_ if necessary. Not all versions
+ are declared; take example on previous releases.
+
+.. _Trac's versions list: https://code.djangoproject.com/admin/ticket/versions
+
+Notes on setting the VERSION tuple
+==================================
+
+Django's version reporting is controlled by the ``VERSION`` tuple in
+``django/__init__.py``. This is a five-element tuple, whose elements
+are:
+
+#. Major version.
+#. Minor version.
+#. Micro version.
+#. Status -- can be one of "alpha", "beta", "rc" or "final".
+#. Series number, for alpha/beta/RC packages which run in sequence
+ (allowing, for example, "beta 1", "beta 2", etc.).
+
+For a final release, the status is always "final" and the series
+number is always 0. A series number of 0 with an "alpha" status will
+be reported as "pre-alpha".
+
+Some examples:
+
+* ``(1, 2, 1, 'final', 0)`` --> "1.2.1"
+
+* ``(1, 3, 0, 'alpha', 0)`` --> "1.3 pre-alpha"
+
+* ``(1, 3, 0, 'beta', 2)`` --> "1.3 beta 2"
diff --git a/docs/internals/index.txt b/docs/internals/index.txt
index 3ff4eb62d0..9a80a90286 100644
--- a/docs/internals/index.txt
+++ b/docs/internals/index.txt
@@ -22,3 +22,4 @@ the hood".
release-process
deprecation
git
+ howto-release-django
diff --git a/docs/internals/release-process.txt b/docs/internals/release-process.txt
index 8affddb5e0..29ce3914b4 100644
--- a/docs/internals/release-process.txt
+++ b/docs/internals/release-process.txt
@@ -13,12 +13,12 @@ Since version 1.0, Django's release numbering works as follows:
* ``A`` is the *major version* number, which is only incremented for major
changes to Django, and these changes are not necessarily
- backwards-compatible. That is, code you wrote for Django 1.2 may break
+ backwards-compatible. That is, code you wrote for Django 1.6 may break
when we release Django 2.0.
* ``B`` is the *minor version* number, which is incremented for large yet
- backwards compatible changes. Code written for Django 1.2 will continue
- to work under Django 1.3. Exceptions to this rule will be listed in the
+ backwards compatible changes. Code written for Django 1.6 will continue
+ to work under Django 1.7. Exceptions to this rule will be listed in the
release notes.
* ``C`` is the *micro version* number, which is incremented for bug and
@@ -27,67 +27,62 @@ Since version 1.0, Django's release numbering works as follows:
can't be fixed without breaking backwards-compatibility. If this happens,
the release notes will provide detailed upgrade instructions.
-* In some cases, we'll make alpha, beta, or release candidate releases.
- These are of the form ``A.B alpha/beta/rc N``, which means the ``Nth``
- alpha/beta/release candidate of version ``A.B``.
+* Before a new minor release, we'll make alpha, beta, and release candidate
+ releases. These are of the form ``A.B alpha/beta/rc N``, which means the
+ ``Nth`` alpha/beta/release candidate of version ``A.B``.
-In git, each Django release will have a tag indicating its version
-number, signed with the Django release key. Additionally, each release
-series (X.Y) has its own branch, and bugfix/security releases will be
+In git, each Django release will have a tag indicating its version number,
+signed with the Django release key. Additionally, each release series has its
+own branch, called ``stable/A.B.x``, and bugfix/security releases will be
issued from those branches.
-For more information about how the Django project issues new releases
-for security purposes, please see :doc:`our security policies
-<security>`.
+For more information about how the Django project issues new releases for
+security purposes, please see :doc:`our security policies <security>`.
Major releases
--------------
Major releases (1.0, 2.0, etc.) will happen very infrequently (think "years",
-not "months"), and will probably represent major, sweeping changes to Django.
+not "months"), and may represent major, sweeping changes to Django.
Minor releases
--------------
-Minor release (1.1, 1.2, etc.) will happen roughly every nine months -- see
-`release process`_, below for details.
+Minor release (1.5, 1.6, etc.) will happen roughly every nine months -- see
+`release process`_, below for details. These releases will contain new
+features, improvements to existing features, and such.
.. _internal-release-deprecation-policy:
-These releases will contain new features, improvements to existing features, and
-such. A minor release may deprecate certain features from previous releases. If a
-feature in version ``A.B`` is deprecated, it will continue to work in version
-``A.B+1``. In version ``A.B+2``, use of the feature will raise a
-``DeprecationWarning`` but will continue to work. Version ``A.B+3`` will
-remove the feature entirely.
+A minor release may deprecate certain features from previous releases. If a
+feature is deprecated in version ``A.B``, it will continue to work in versions
+``A.B`` and ``A.B+1`` but raise warnings. It will be removed in version
+``A.B+2``.
-So, for example, if we decided to remove a function that existed in Django 1.0:
+So, for example, if we decided to start the deprecation of a function in
+Django 1.5:
-* Django 1.1 will contain a backwards-compatible replica of the function
- which will raise a ``PendingDeprecationWarning``. This warning is silent
- by default; you need to explicitly turn on display of these warnings.
+* Django 1.5 will contain a backwards-compatible replica of the function which
+ will raise a ``PendingDeprecationWarning``. This warning is silent by
+ default; you can turn on display of these warnings with the ``-Wd`` option
+ of Python.
-* Django 1.2 will contain the backwards-compatible replica, but the warning
+* Django 1.6 will contain the backwards-compatible replica, but the warning
will be promoted to a full-fledged ``DeprecationWarning``. This warning is
*loud* by default, and will likely be quite annoying.
-* Django 1.3 will remove the feature outright.
+* Django 1.7 will remove the feature outright.
Micro releases
--------------
-Micro releases (1.0.1, 1.0.2, 1.1.1, etc.) will be issued at least once half-way
-between minor releases, and probably more often as needed.
+Micro releases (1.5.1, 1.6.2, 1.6.1, etc.) will be issued as needed, often to
+fix security issues.
These releases will be 100% compatible with the associated minor release, unless
this is impossible for security reasons. So the answer to "should I upgrade to
the latest micro release?" will always be "yes."
-Each minor release of Django will have a "release maintainer" appointed. This
-person will be responsible for making sure that bug fixes are applied to both
-trunk and the maintained micro-release branch. This person will also work with
-the release manager to decide when to release the micro releases.
-
.. _backwards-compatibility-policy:
Supported versions
@@ -96,10 +91,10 @@ Supported versions
At any moment in time, Django's developer team will support a set of releases to
varying levels:
-* The current development trunk will get new features and bug fixes
+* The current development master will get new features and bug fixes
requiring major refactoring.
-* Patches applied to the trunk will also be applied to the last minor
+* Patches applied to the master branch must also be applied to the last minor
release, to be released as the next micro release, when they fix critical
problems:
@@ -111,40 +106,42 @@ varying levels:
* Major functionality bugs in newly-introduced features.
- The rule of thumb is that fixes will be backported to the last minor
- release for bugs that would have prevented a release in the first place.
+ The rule of thumb is that fixes will be backported to the last minor release
+ for bugs that would have prevented a release in the first place (release
+ blockers).
-* Security fixes will be applied to the current trunk and the previous two
+* Security fixes will be applied to the current master and the previous two
minor releases.
+* Committers may choose to backport bugfixes at their own discretion,
+ provided they do not introduce backwards incompatibilities.
+
* Documentation fixes generally will be more freely backported to the last
- release branch, at the discretion of the committer, and they don't need to
- meet the "critical fixes only" bar. That's because it's highly advantageous
- to have the docs for the last release be up-to-date and correct, and the
- downside of backporting (risk of introducing regressions) is much less of a
- concern.
+ release branch. That's because it's highly advantageous to have the docs for
+ the last release be up-to-date and correct, and the risk of introducing
+ regressions is much less of a concern.
As a concrete example, consider a moment in time halfway between the release of
-Django 1.3 and 1.4. At this point in time:
+Django 1.6 and 1.7. At this point in time:
-* Features will be added to development trunk, to be released as Django 1.4.
+* Features will be added to development master, to be released as Django 1.7.
-* Critical bug fixes will be applied to a ``1.3.X`` branch, and released as
- 1.3.1, 1.3.2, etc.
+* Critical bug fixes will be applied to the ``stable/1.6.X`` branch, and
+ released as 1.6.1, 1.6.2, etc.
-* Security fixes will be applied to trunk, a ``1.3.X`` branch and a
- ``1.2.X`` branch. They will trigger the release of ``1.3.1``, ``1.2.1``,
- etc.
+* Security fixes will be applied to ``master``, to the ``stable/1.6.X``
+ branch, and to the ``stable/1.5.X`` branch. They will trigger the release of
+ ``1.6.1``, ``1.5.1``, etc.
-* Documentation fixes will be applied to trunk, and, if easily backported, to
- the ``1.3.X`` branch.
+* Documentation fixes will be applied to master, and, if easily backported, to
+ the ``1.6.X`` branch. Bugfixes may also be backported.
.. _release-process:
Release process
===============
-Django uses a time-based release schedule, with minor (i.e. 1.1, 1.2, etc.)
+Django uses a time-based release schedule, with minor (i.e. 1.6, 1.7, etc.)
releases every nine months, or more, depending on features.
After each release, and after a suitable cooling-off period of a few weeks, the
@@ -190,45 +187,56 @@ At the end of phase two, any unfinished "maybe" features will be postponed until
the next release. Though it shouldn't happen, any "must-have" features will
extend phase two, and thus postpone the final release.
-Phase two will culminate with an alpha release.
+Phase two will culminate with an alpha release. At this point, the
+``stable/A.B.x`` branch will be forked from ``master``.
Phase three: bugfixes
~~~~~~~~~~~~~~~~~~~~~
The last third of a release is spent fixing bugs -- no new features will be
-accepted during this time. We'll release a beta release about halfway through,
-and an rc complete with string freeze two weeks before the end of the schedule.
+accepted during this time. We'll try to release a beta release after one month
+and a release candidate after two months.
+
+The release candidate marks the string freeze, and it happens at least two
+weeks before the final release. After this point, new translatable strings
+must not be added.
+
+During this phase, committers will be more and more conservative with
+backports, to avoid introducing regressions. After the release candidate, only
+release blockers and documentation fixes should be backported.
+
+In parallel to this phase, ``master`` can receive new features, to be released
+in the ``A.B+1`` cycle.
Bug-fix releases
----------------
-After a minor release (e.g. 1.1), the previous release will go into bugfix
+After a minor release (e.g. 1.6), the previous release will go into bugfix
mode.
-A branch will be created of the form ``branches/releases/1.0.X`` to track
-bugfixes to the previous release. Critical bugs fixed on trunk must
-*also* be fixed on the bugfix branch; this means that commits need to cleanly
-separate bug fixes from feature additions. The developer who commits a fix to
-trunk will be responsible for also applying the fix to the current bugfix
-branch. Each bugfix branch will have a maintainer who will work with the
-committers to keep them honest on backporting bug fixes.
+A branch will be created of the form ``stable/1.5.x`` to track bugfixes to the
+previous release. Critical bugs fixed on master must *also* be fixed on the
+bugfix branch; this means that commits need to cleanly separate bug fixes from
+feature additions. The developer who commits a fix to master will be
+responsible for also applying the fix to the current bugfix branch.
How this all fits together
--------------------------
Let's look at a hypothetical example for how this all first together. Imagine,
-if you will, a point about halfway between 1.1 and 1.2. At this point,
+if you will, a point about halfway between 1.5 and 1.6. At this point,
development will be happening in a bunch of places:
-* On trunk, development towards 1.2 proceeds with small additions, bugs
+* On master, development towards 1.6 proceeds with small additions, bugs
fixes, etc. being checked in daily.
-* On the branch "branches/releases/1.1.X", fixes for critical bugs found in
- the 1.1 release are checked in as needed. At some point, this branch will
- be released as "1.1.1", "1.1.2", etc.
+* On the branch ``stable/1.5.x``, fixes for critical bugs found in
+ the 1.5 release are checked in as needed. At some point, this branch will
+ be released as "1.5.1", "1.5.2", etc.
-* On the branch "branches/releases/1.0.X", security fixes are made if
- needed and released as "1.0.2", "1.0.3", etc.
+* On the branch ``stable/1.4.x``, security fixes are made if
+ needed and released as "1.4.2", "1.4.3", etc.
-* On feature branches, development of major features is done. These
- branches will be merged into trunk before the end of phase two.
+* Development of major features is done in branches in forks of the main
+ repository. These branches will be merged into ``master`` before "1.6
+ alpha 1".
diff --git a/docs/intro/_images/admin02.png b/docs/intro/_images/admin02.png
index 4b49ebb490..b9810ab7ba 100644
--- a/docs/intro/_images/admin02.png
+++ b/docs/intro/_images/admin02.png
Binary files differ
diff --git a/docs/intro/_images/admin02t.png b/docs/intro/_images/admin02t.png
index d7519d19ab..c0c6a56928 100644
--- a/docs/intro/_images/admin02t.png
+++ b/docs/intro/_images/admin02t.png
Binary files differ
diff --git a/docs/intro/_images/admin03.png b/docs/intro/_images/admin03.png
index 635226c61c..5cf567d5ce 100644
--- a/docs/intro/_images/admin03.png
+++ b/docs/intro/_images/admin03.png
Binary files differ
diff --git a/docs/intro/_images/admin03t.png b/docs/intro/_images/admin03t.png
index 94273cb583..ad15ea60cd 100644
--- a/docs/intro/_images/admin03t.png
+++ b/docs/intro/_images/admin03t.png
Binary files differ
diff --git a/docs/intro/contributing.txt b/docs/intro/contributing.txt
index a343814c02..8747375b0a 100644
--- a/docs/intro/contributing.txt
+++ b/docs/intro/contributing.txt
@@ -20,8 +20,8 @@ For this tutorial, we expect that you have at least a basic understanding of
how Django works. This means you should be comfortable going through the
existing tutorials on :doc:`writing your first Django app</intro/tutorial01>`.
In addition, you should have a good understanding of Python itself. But if you
-don't, `Dive Into Python`__ is a fantastic (and free) online book for beginning
-Python programmers.
+don't, "Dive Into Python" (for `Python 2`__, for `Python 3`__) is a fantastic
+(and free) online book for beginning Python programmers.
Those of you who are unfamiliar with version control systems and Trac will find
that this tutorial and its links include just enough information to get started.
@@ -38,6 +38,7 @@ so that it can be of use to the widest audience.
chat with other Django users who might be able to help.
__ http://diveintopython.net/toc/index.html
+__ http://diveintopython3.net/
__ http://groups.google.com/group/django-developers
__ irc://irc.freenode.net/django-dev
@@ -96,9 +97,10 @@ Download the Django source code repository using the following command::
pip install -e /path/to/your/local/clone/django/
- to link your cloned checkout into a virtual environment. This is a great
- option to isolate your development copy of Django from the rest of your
- system and avoids potential package conflicts.
+ (where ``django`` is the directory of your clone that contains
+ ``setup.py``) to link your cloned checkout into a virtual environment. This
+ is a great option to isolate your development copy of Django from the rest
+ of your system and avoids potential package conflicts.
__ http://www.virtualenv.org
@@ -237,7 +239,7 @@ widget. Before we make those changes though, we're going to write a couple
tests to verify that our modification functions correctly and continues to
function correctly in the future.
-Navigate to Django's ``tests/regressiontests/admin_widgets/`` folder and
+Navigate to Django's ``tests/admin_widgets/`` folder and
open the ``tests.py`` file. Add the following code on line 269 right before the
``AdminFileWidgetTest`` class::
@@ -281,7 +283,7 @@ correctly in a couple different situations.
computer programming, so there's lots of information out there:
* A good first look at writing tests for Django can be found in the
- documentation on :doc:`Testing Django applications</topics/testing/>`.
+ documentation on :doc:`Testing Django applications </topics/testing/overview>`.
* Dive Into Python (a free online book for beginning Python developers)
includes a great `introduction to Unit Testing`__.
* After reading those, if you want something a little meatier to sink
@@ -467,10 +469,10 @@ This patch file contains all your changes and should look this:
Relationship fields
===================
- diff --git a/tests/regressiontests/admin_widgets/tests.py b/tests/regressiontests/admin_widgets/tests.py
+ diff --git a/tests/admin_widgets/tests.py b/tests/admin_widgets/tests.py
index 4b11543..94acc6d 100644
- --- a/tests/regressiontests/admin_widgets/tests.py
- +++ b/tests/regressiontests/admin_widgets/tests.py
+ --- a/tests/admin_widgets/tests.py
+ +++ b/tests/admin_widgets/tests.py
@@ -265,6 +265,35 @@ class AdminSplitDateTimeWidgetTest(DjangoTestCase):
'<p class="datetime">Datum: <input value="01.12.2007" type="text" class="vDateField" name="test_0" size="10" /><br />Zeit: <input value="09:30:00" type="text" class="vTimeField" name="test_1" size="8" /></p>',
)
diff --git a/docs/intro/index.txt b/docs/intro/index.txt
index ea6a3c4d29..9e88402f6d 100644
--- a/docs/intro/index.txt
+++ b/docs/intro/index.txt
@@ -14,6 +14,7 @@ place: read this material to quickly get up and running.
tutorial03
tutorial04
tutorial05
+ tutorial06
reusable-apps
whatsnext
contributing
@@ -28,12 +29,14 @@ place: read this material to quickly get up and running.
`list of Python resources for non-programmers`_
If you already know a few other languages and want to get up to speed with
- Python quickly, we recommend `Dive Into Python`_ (also available in a
+ Python quickly, we recommend "Dive Into Python" (for `Python 2`_, for
+ `Python 3`_, also available in a
`dead-tree version`_). If that's not quite your style, there are quite
a few other `books about Python`_.
.. _python: http://python.org/
.. _list of Python resources for non-programmers: http://wiki.python.org/moin/BeginnersGuide/NonProgrammers
- .. _dive into python: http://diveintopython.net/
+ .. _Python 2: http://diveintopython.net/
+ .. _Python 3: http://diveintopython3.net/
.. _dead-tree version: http://www.amazon.com/exec/obidos/ASIN/1590593561/ref=nosim/jacobian20
.. _books about Python: http://wiki.python.org/moin/PythonBooks
diff --git a/docs/intro/install.txt b/docs/intro/install.txt
index f9b122e62d..3cbc8d88ab 100644
--- a/docs/intro/install.txt
+++ b/docs/intro/install.txt
@@ -78,11 +78,13 @@ Verifying
---------
To verify that Django can be seen by Python, type ``python`` from your shell.
-Then at the Python prompt, try to import Django::
+Then at the Python prompt, try to import Django:
+
+.. parsed-literal::
>>> import django
>>> print(django.get_version())
- 1.5
+ |version|
You may have another version of Django installed.
@@ -90,6 +92,3 @@ That's it!
----------
That's it -- you can now :doc:`move onto the tutorial </intro/tutorial01>`.
-
-
-
diff --git a/docs/intro/overview.txt b/docs/intro/overview.txt
index ba49e3ccf2..8753817256 100644
--- a/docs/intro/overview.txt
+++ b/docs/intro/overview.txt
@@ -56,7 +56,9 @@ Enjoy the free API
==================
With that, you've got a free, and rich, :doc:`Python API </topics/db/queries>` to
-access your data. The API is created on the fly, no code generation necessary::
+access your data. The API is created on the fly, no code generation necessary:
+
+.. code-block:: python
# Import the models we created from our "news" app
>>> from news.models import Reporter, Article
@@ -176,7 +178,7 @@ decouple URLs from Python code.
Here's what a URLconf might look like for the ``Reporter``/``Article``
example above::
- from django.conf.urls import patterns, url, include
+ from django.conf.urls import patterns
urlpatterns = patterns('',
(r'^articles/(\d{4})/$', 'news.views.year_archive'),
@@ -269,16 +271,18 @@ Finally, Django uses the concept of "template inheritance": That's what the
following blocks." In short, that lets you dramatically cut down on redundancy
in templates: each template has to define only what's unique to that template.
-Here's what the "base.html" template might look like:
+Here's what the "base.html" template, including the use of :doc:`static files
+</howto/static-files/index>`, might look like:
.. code-block:: html+django
+ {% load staticfiles %}
<html>
<head>
<title>{% block title %}{% endblock %}</title>
</head>
<body>
- <img src="sitelogo.png" alt="Logo" />
+ <img src="{% static "images/sitelogo.png" %}" alt="Logo" />
{% block content %}{% endblock %}
</body>
</html>
diff --git a/docs/intro/reusable-apps.txt b/docs/intro/reusable-apps.txt
index 6aade4997e..1e0f1da0c5 100644
--- a/docs/intro/reusable-apps.txt
+++ b/docs/intro/reusable-apps.txt
@@ -2,11 +2,11 @@
Advanced tutorial: How to write reusable apps
=============================================
-This advanced tutorial begins where :doc:`Tutorial 5 </intro/tutorial05>` left
-off. We'll be turning our Web-poll into a standalone Python package you can
-reuse in new projects and share with other people.
+This advanced tutorial begins where :doc:`Tutorial 6 </intro/tutorial06>`
+left off. We'll be turning our Web-poll into a standalone Python package
+you can reuse in new projects and share with other people.
-If you haven't recently completed Tutorials 1–5, we encourage you to review
+If you haven't recently completed Tutorials 1–6, we encourage you to review
these so that your example project matches the one described below.
Reusability matters
@@ -50,8 +50,8 @@ projects and ready to publish for others to install and use.
Python package easy for others to install. It can be a little confusing, we
know.
-Completing your reusable app
-============================
+Your project and your reusable app
+==================================
After the previous tutorials, our project should look like this::
@@ -63,82 +63,36 @@ After the previous tutorials, our project should look like this::
urls.py
wsgi.py
polls/
- admin.py
- __init__.py
- models.py
- tests.py
- urls.py
- views.py
-
-You also have a directory somewhere called ``mytemplates`` which you created in
-:doc:`Tutorial 2 </intro/tutorial02>`. You specified its location in the
-TEMPLATE_DIRS setting. This directory should look like this::
-
- mytemplates/
- admin/
- base_site.html
- polls/
- detail.html
- index.html
- results.html
-
-The polls app is already a Python package, thanks to the ``polls/__init__.py``
-file. That's a great start, but we can't just pick up this package and drop it
-into a new project. The polls templates are currently stored in the
-project-wide ``mytemplates`` directory. To make the app self-contained, it
-should also contain the necessary templates.
-
-Inside the ``polls`` app, create a new ``templates`` directory. Now move the
-``polls`` template directory from ``mytemplates`` into the new
-``templates``. Your project should now look like this::
-
- mysite/
- manage.py
- mysite/
__init__.py
- settings.py
- urls.py
- wsgi.py
- polls/
admin.py
- __init__.py
models.py
+ tests.py
+ static/
+ style.css
+ images/
+ background.gif
templates/
polls/
detail.html
index.html
results.html
- tests.py
urls.py
views.py
+ templates/
+ admin/
+ base_site.html
-Your project-wide templates directory should now look like this::
-
- mytemplates/
- admin/
- base_site.html
-
-Looking good! Now would be a good time to confirm that your polls application
-still works correctly. How does Django know how to find the new location of
-the polls templates even though we didn't modify :setting:`TEMPLATE_DIRS`?
-Django has a :setting:`TEMPLATE_LOADERS` setting which contains a list
-of callables that know how to import templates from various sources. One of
-the defaults is :class:`django.template.loaders.app_directories.Loader` which
-looks for a "templates" subdirectory in each of the :setting:`INSTALLED_APPS`.
+You created ``mysite/templates`` in :doc:`Tutorial 2 </intro/tutorial02>`,
+and ``polls/templates`` in :doc:`Tutorial 3 </intro/tutorial03>`. Now perhaps
+it is clearer why we chose to have separate template directories for the
+project and application: everything that is part of the polls application is in
+``polls``. It makes the application self-contained and easier to drop into a
+new project.
The ``polls`` directory could now be copied into a new Django project and
immediately reused. It's not quite ready to be published though. For that, we
need to package the app to make it easy for others to install.
-.. admonition:: Why nested?
-
- Why create a ``polls`` directory under ``templates`` when we're
- already inside the polls app? This directory is needed to avoid conflicts in
- Django's ``app_directories`` template loader. For example, if two
- apps had a template called ``base.html``, without the extra directory it
- wouldn't be possible to distinguish between the two. It's a good convention
- to use the name of your app for this directory.
-
.. _installing-reusable-apps-prerequisites:
Installing some prerequisites
diff --git a/docs/intro/tutorial01.txt b/docs/intro/tutorial01.txt
index 9419f9c4eb..7f69945300 100644
--- a/docs/intro/tutorial01.txt
+++ b/docs/intro/tutorial01.txt
@@ -19,10 +19,15 @@ tell Django is installed and which version by running the following command:
python -c "import django; print(django.get_version())"
-You should see either the version of your Django installation or an error
-telling "No module named django". Check also that the version number matches
-the version of this tutorial. If they don't match, you can refer to the
-tutorial for your version of Django or update Django to the newest version.
+If Django is installed, you should see the version of your installation. If it
+isn't, you'll get an error telling "No module named django".
+
+This tutorial is written for Django |version| and Python 2.x. If the Django
+version doesn't match, you can refer to the tutorial for your version of Django
+or update Django to the newest version. If you are using Python 3.x, be aware
+that your code may need to differ from what is in the tutorial and you should
+continue using the tutorial only if you know what you are doing with Python
+3.x.
See :doc:`How to install Django </topics/install>` for advice on how to remove
older versions of Django and install a newer one.
@@ -127,13 +132,16 @@ The development server
Let's verify this worked. Change into the outer :file:`mysite` directory, if
you haven't already, and run the command ``python manage.py runserver``. You'll
-see the following output on the command line::
+see the following output on the command line:
+
+.. parsed-literal::
Validating models...
- 0 errors found.
- Django version 1.4, using settings 'mysite.settings'
- Development server is running at http://127.0.0.1:8000/
+ 0 errors found
+ |today| - 15:50:53
+ Django version |version|, using settings 'mysite.settings'
+ Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
You've started the Django development server, a lightweight Web server written
@@ -141,7 +149,7 @@ purely in Python. We've included this with Django so you can develop things
rapidly, without having to deal with configuring a production server -- such as
Apache -- until you're ready for production.
-Now's a good time to note: DON'T use this server in anything resembling a
+Now's a good time to note: **Don't** use this server in anything resembling a
production environment. It's intended only for use while developing. (We're in
the business of making Web frameworks, not Web servers.)
@@ -177,39 +185,33 @@ Database setup
--------------
Now, edit :file:`mysite/settings.py`. It's a normal Python module with
-module-level variables representing Django settings. Change the
-following keys in the :setting:`DATABASES` ``'default'`` item to match
-your database connection settings.
+module-level variables representing Django settings.
+
+By default, the configuration uses SQLite. If you're new to databases, or
+you're just interested in trying Django, this is the easiest choice. SQLite is
+included in Python, so you won't need to install anything else to support your
+database.
+
+If you wish to use another database, install the appropriate :ref:`database
+bindings <database-installation>`, and change the following keys in the
+:setting:`DATABASES` ``'default'`` item to match your database connection
+settings:
* :setting:`ENGINE <DATABASE-ENGINE>` -- Either
+ ``'django.db.backends.sqlite3'``,
``'django.db.backends.postgresql_psycopg2'``,
- ``'django.db.backends.mysql'``, ``'django.db.backends.sqlite3'`` or
+ ``'django.db.backends.mysql'``, or
``'django.db.backends.oracle'``. Other backends are :setting:`also available
<DATABASE-ENGINE>`.
-* :setting:`NAME` -- The name of your database. If you're using
- SQLite, the database will be a file on your computer; in that
- case, :setting:`NAME` should be the full absolute path,
- including filename, of that file. If the file doesn't exist, it
- will automatically be created when you synchronize the database
- for the first time (see below).
-
- When specifying the path, always use forward slashes, even on
- Windows (e.g. ``C:/homes/user/mysite/sqlite3.db``).
-
-* :setting:`USER` -- Your database username (not used for SQLite).
+* :setting:`NAME` -- The name of your database. If you're using SQLite, the
+ database will be a file on your computer; in that case, :setting:`NAME`
+ should be the full absolute path, including filename, of that file. The
+ default value, ``os.path.join(BASE_DIR, 'db.sqlite3')``, will store the file
+ in your project directory.
-* :setting:`PASSWORD` -- Your database password (not used for
- SQLite).
-
-* :setting:`HOST` -- The host your database is on. Leave this as
- an empty string (or possibly ``127.0.0.1``) if your database server is on the
- same physical machine (not used for SQLite). See :setting:`HOST` for details.
-
-If you're new to databases, we recommend simply using SQLite by setting
-:setting:`ENGINE` to ``'django.db.backends.sqlite3'`` and :setting:`NAME` to
-the place where you'd like to store the database. SQLite is included in Python,
-so you won't need to install anything else to support your database.
+If you are not using SQLite as your database, additional settings such as :setting:`USER`, :setting:`PASSWORD`, :setting:`HOST` must be added.
+For more details, see the reference documentation for :setting:`DATABASES`.
.. note::
@@ -220,17 +222,20 @@ so you won't need to install anything else to support your database.
If you're using SQLite, you don't need to create anything beforehand - the
database file will be created automatically when it is needed.
-While you're editing :file:`settings.py`, set :setting:`TIME_ZONE` to your
-time zone. The default value is the Central time zone in the U.S. (Chicago).
+While you're editing :file:`mysite/settings.py`, set :setting:`TIME_ZONE` to
+your time zone.
-Also, note the :setting:`INSTALLED_APPS` setting toward the bottom of
-the file. That holds the names of all Django applications that are
-activated in this Django instance. Apps can be used in multiple projects, and
-you can package and distribute them for use by others in their projects.
+Also, note the :setting:`INSTALLED_APPS` setting at the top of the file. That
+holds the names of all Django applications that are activated in this Django
+instance. Apps can be used in multiple projects, and you can package and
+distribute them for use by others in their projects.
By default, :setting:`INSTALLED_APPS` contains the following apps, all of which
come with Django:
+* :mod:`django.contrib.admin` -- The admin site. You'll use it in :doc:`part 2
+ of this tutorial </intro/tutorial02>`.
+
* :mod:`django.contrib.auth` -- An authentication system.
* :mod:`django.contrib.contenttypes` -- A framework for content types.
@@ -255,11 +260,12 @@ that, run the following command:
python manage.py syncdb
-The :djadmin:`syncdb` command looks at the :setting:`INSTALLED_APPS` setting and
-creates any necessary database tables according to the database settings in your
-:file:`settings.py` file. You'll see a message for each database table it
-creates, and you'll get a prompt asking you if you'd like to create a superuser
-account for the authentication system. Go ahead and do that.
+The :djadmin:`syncdb` command looks at the :setting:`INSTALLED_APPS` setting
+and creates any necessary database tables according to the database settings
+in your :file:`mysite/settings.py` file. You'll see a message for each
+database table it creates, and you'll get a prompt asking you if you'd like to
+create a superuser account for the authentication system. Go ahead and do
+that.
If you're interested, run the command-line client for your database and type
``\dt`` (PostgreSQL), ``SHOW TABLES;`` (MySQL), or ``.schema`` (SQLite) to
@@ -282,10 +288,10 @@ Creating models
Now that your environment -- a "project" -- is set up, you're set to start
doing work.
-Each application you write in Django consists of a Python package, somewhere
-on your `Python path`_, that follows a certain convention. Django comes with a
-utility that automatically generates the basic directory structure of an app,
-so you can focus on writing code rather than creating directories.
+Each application you write in Django consists of a Python package that follows
+a certain convention. Django comes with a utility that automatically generates
+the basic directory structure of an app, so you can focus on writing code
+rather than creating directories.
.. admonition:: Projects vs. apps
@@ -310,6 +316,7 @@ That'll create a directory :file:`polls`, which is laid out like this::
polls/
__init__.py
+ admin.py
models.py
tests.py
views.py
@@ -343,7 +350,7 @@ These concepts are represented by simple Python classes. Edit the
class Choice(models.Model):
poll = models.ForeignKey(Poll)
choice_text = models.CharField(max_length=200)
- votes = models.IntegerField()
+ votes = models.IntegerField(default=0)
The code is straightforward. Each model is represented by a class that
subclasses :class:`django.db.models.Model`. Each model has a number of class
@@ -355,7 +362,7 @@ class -- e.g., :class:`~django.db.models.CharField` for character fields and
type of data each field holds.
The name of each :class:`~django.db.models.Field` instance (e.g. ``question`` or
-``pub_date`` ) is the field's name, in machine-friendly format. You'll use this
+``pub_date``) is the field's name, in machine-friendly format. You'll use this
value in your Python code, and your database will use it as the column name.
You can use an optional first positional argument to a
@@ -366,10 +373,14 @@ example, we've only defined a human-readable name for ``Poll.pub_date``. For all
other fields in this model, the field's machine-readable name will suffice as
its human-readable name.
-Some :class:`~django.db.models.Field` classes have required elements.
+Some :class:`~django.db.models.Field` classes have required arguments.
:class:`~django.db.models.CharField`, for example, requires that you give it a
-:attr:`~django.db.models.Field.max_length`. That's used not only in the database
-schema, but in validation, as we'll soon see.
+:attr:`~django.db.models.CharField.max_length`. That's used not only in the
+database schema, but in validation, as we'll soon see.
+
+A :class:`~django.db.models.Field` can also have various optional arguments; in
+this case, we've set the :attr:`~django.db.models.Field.default` value of
+``votes`` to 0.
Finally, note a relationship is defined, using
:class:`~django.db.models.ForeignKey`. That tells Django each ``Choice`` is related
@@ -395,26 +406,21 @@ But first we need to tell our project that the ``polls`` app is installed.
you can distribute apps, because they don't have to be tied to a given
Django installation.
-Edit the :file:`settings.py` file again, and change the
-:setting:`INSTALLED_APPS` setting to include the string ``'polls'``. So
-it'll look like this::
+Edit the :file:`mysite/settings.py` file again, and change the
+:setting:`INSTALLED_APPS` setting to include the string ``'polls'``. So it'll
+look like this::
INSTALLED_APPS = (
+ 'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
- 'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
- # Uncomment the next line to enable the admin:
- # 'django.contrib.admin',
- # Uncomment the next line to enable admin documentation:
- # 'django.contrib.admindocs',
'polls',
)
-Now Django knows to include the ``polls`` app. Let's run another
-command:
+Now Django knows to include the ``polls`` app. Let's run another command:
.. code-block:: bash
@@ -427,13 +433,13 @@ statements for the polls app):
BEGIN;
CREATE TABLE "polls_poll" (
- "id" serial NOT NULL PRIMARY KEY,
+ "id" integer NOT NULL PRIMARY KEY,
"question" varchar(200) NOT NULL,
- "pub_date" timestamp with time zone NOT NULL
+ "pub_date" datetime NOT NULL
);
CREATE TABLE "polls_choice" (
- "id" serial NOT NULL PRIMARY KEY,
- "poll_id" integer NOT NULL REFERENCES "polls_poll" ("id") DEFERRABLE INITIALLY DEFERRED,
+ "id" integer NOT NULL PRIMARY KEY,
+ "poll_id" integer NOT NULL REFERENCES "polls_poll" ("id"),
"choice_text" varchar(200) NOT NULL,
"votes" integer NOT NULL
);
@@ -441,7 +447,8 @@ statements for the polls app):
Note the following:
-* The exact output will vary depending on the database you are using.
+* The exact output will vary depending on the database you are using. The
+ example above is generated for SQLite.
* Table names are automatically generated by combining the name of the app
(``polls``) and the lowercase name of the model -- ``poll`` and
@@ -459,8 +466,7 @@ Note the following:
types such as ``auto_increment`` (MySQL), ``serial`` (PostgreSQL), or
``integer primary key`` (SQLite) are handled for you automatically. Same
goes for quoting of field names -- e.g., using double quotes or single
- quotes. The author of this tutorial runs PostgreSQL, so the example
- output is in PostgreSQL syntax.
+ quotes.
* The :djadmin:`sql` command doesn't actually run the SQL in your database -
it just prints it to the screen so that you can see what SQL Django thinks
@@ -519,7 +525,7 @@ API Django gives you. To invoke the Python shell, use this command:
We're using this instead of simply typing "python", because :file:`manage.py`
sets the ``DJANGO_SETTINGS_MODULE`` environment variable, which gives Django
-the Python import path to your :file:`settings.py` file.
+the Python import path to your :file:`mysite/settings.py` file.
.. admonition:: Bypassing manage.py
@@ -645,8 +651,10 @@ Save these changes and start a new Python interactive shell by running
>>> Poll.objects.filter(question__startswith='What')
[<Poll: What's up?>]
- # Get the poll whose year is 2012.
- >>> Poll.objects.get(pub_date__year=2012)
+ # Get the poll that was published this year.
+ >>> from django.utils import timezone
+ >>> current_year = timezone.now().year
+ >>> Poll.objects.get(pub_date__year=current_year)
<Poll: What's up?>
# Request an ID that doesn't exist, this will raise an exception.
@@ -697,8 +705,9 @@ Save these changes and start a new Python interactive shell by running
# The API automatically follows relationships as far as you need.
# Use double underscores to separate relationships.
# This works as many levels deep as you want; there's no limit.
- # Find all Choices for any poll whose pub_date is in 2012.
- >>> Choice.objects.filter(poll__pub_date__year=2012)
+ # Find all Choices for any poll whose pub_date is in this year
+ # (reusing the 'current_year' variable we created above).
+ >>> Choice.objects.filter(poll__pub_date__year=current_year)
[<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]
# Let's delete one of the choices. Use delete() for that.
diff --git a/docs/intro/tutorial02.txt b/docs/intro/tutorial02.txt
index 2c8d25ae6f..1987c51a67 100644
--- a/docs/intro/tutorial02.txt
+++ b/docs/intro/tutorial02.txt
@@ -21,49 +21,11 @@ automatically-generated admin site.
The admin isn't intended to be used by site visitors. It's for site
managers.
-Activate the admin site
-=======================
-
-The Django admin site is not activated by default -- it's an opt-in thing. To
-activate the admin site for your installation, do these three things:
-
-* Uncomment ``"django.contrib.admin"`` in the :setting:`INSTALLED_APPS` setting.
-
-* Run ``python manage.py syncdb``. Since you have added a new application
- to :setting:`INSTALLED_APPS`, the database tables need to be updated.
-
-* Edit your ``mysite/urls.py`` file and uncomment the lines that reference
- the admin -- there are three lines in total to uncomment. This file is a
- URLconf; we'll dig into URLconfs in the next tutorial. For now, all you
- need to know is that it maps URL roots to applications. In the end, you
- should have a ``urls.py`` file that looks like this:
-
- .. parsed-literal::
-
- from django.conf.urls import patterns, include, url
-
- # Uncomment the next two lines to enable the admin:
- **from django.contrib import admin**
- **admin.autodiscover()**
-
- urlpatterns = patterns('',
- # Examples:
- # url(r'^$', '{{ project_name }}.views.home', name='home'),
- # url(r'^{{ project_name }}/', include('{{ project_name }}.foo.urls')),
-
- # Uncomment the admin/doc line below to enable admin documentation:
- # url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
-
- # Uncomment the next line to enable the admin:
- **url(r'^admin/', include(admin.site.urls)),**
- )
-
- (The bold lines are the ones that needed to be uncommented.)
-
Start the development server
============================
-Let's start the development server and explore the admin site.
+The Django admin site is activated by default. Let's start the development
+server and explore it.
Recall from Tutorial 1 that you start the development server like so:
@@ -77,6 +39,10 @@ http://127.0.0.1:8000/admin/. You should see the admin's login screen:
.. image:: _images/admin01.png
:alt: Django admin login screen
+Since :doc:`translation </topics/i18n/translation>` is turned on by default,
+the login screen may be displayed in your own language, depending on your
+browser's settings and on whether Django has a translation for this language.
+
.. admonition:: Doesn't match what you see?
If at this point, instead of the above login page, you get an error
@@ -93,35 +59,33 @@ http://127.0.0.1:8000/admin/. You should see the admin's login screen:
Enter the admin site
====================
-Now, try logging in. (You created a superuser account in the first part of this
+Now, try logging in. You created a superuser account in the first part of this
tutorial, remember? If you didn't create one or forgot the password you can
-:ref:`create another one <topics-auth-creating-superusers>`.) You should see
-the Django admin index page:
+:ref:`create another one <topics-auth-creating-superusers>`.
+
+You should see the Django admin index page:
.. image:: _images/admin02t.png
:alt: Django admin index page
-You should see a few types of editable content, including groups, users
-and sites. These are core features Django ships with by default.
+You should see a few types of editable content: groups and users. They are
+provided by :mod:`django.contrib.auth`, the authentication framework shipped
+by Django.
Make the poll app modifiable in the admin
=========================================
But where's our poll app? It's not displayed on the admin index page.
-Just one thing to do: We need to tell the admin that ``Poll``
-objects have an admin interface. To do this, create a file called
-``admin.py`` in your ``polls`` directory, and edit it to look like this::
+Just one thing to do: we need to tell the admin that ``Poll``
+objects have an admin interface. To do this, open the :file:`polls/admin.py`
+file, and edit it to look like this::
from django.contrib import admin
from polls.models import Poll
admin.site.register(Poll)
-You'll need to restart the development server to see your changes. Normally,
-the server auto-reloads code every time you modify a file, but the action of
-creating a new file doesn't trigger the auto-reloading logic.
-
Explore the free admin functionality
====================================
@@ -145,7 +109,7 @@ Click the "What's up?" poll to edit it:
Things to note here:
-* The form is automatically generated from the Poll model.
+* The form is automatically generated from the ``Poll`` model.
* The different model field types (:class:`~django.db.models.DateTimeField`,
:class:`~django.db.models.CharField`) correspond to the appropriate HTML
@@ -302,7 +266,7 @@ registration code to read::
This tells Django: "``Choice`` objects are edited on the ``Poll`` admin page. By
default, provide enough fields for 3 choices."
-Load the "Add poll" page to see how that looks, you may need to restart your development server:
+Load the "Add poll" page to see how that looks:
.. image:: _images/admin11t.png
:alt: Add poll page now has choices on it
@@ -375,7 +339,7 @@ of an arbitrary method is not supported. Also note that the column header for
underscores replaced with spaces), and that each line contains the string
representation of the output.
-You can improve that by giving that method (in ``models.py``) a few
+You can improve that by giving that method (in :file:`polls/models.py`) a few
attributes, as follows::
class Poll(models.Model):
@@ -386,8 +350,8 @@ attributes, as follows::
was_published_recently.boolean = True
was_published_recently.short_description = 'Published recently?'
-Edit your admin.py file again and add an improvement to the Poll change list page: Filters. Add the
-following line to ``PollAdmin``::
+Edit your :file:`polls/admin.py` file again and add an improvement to the Poll
+change list page: Filters. Add the following line to ``PollAdmin``::
list_filter = ['pub_date']
@@ -398,9 +362,9 @@ That adds a "Filter" sidebar that lets people filter the change list by the
:alt: Polls change list page, updated
The type of filter displayed depends on the type of field you're filtering on.
-Because ``pub_date`` is a :class:`~django.db.models.fields.DateTimeField`,
-Django knows to give appropriate filter options: "Any date," "Today," "Past 7
-days," "This month," "This year."
+Because ``pub_date`` is a :class:`~django.db.models.DateTimeField`, Django
+knows to give appropriate filter options: "Any date," "Today," "Past 7 days,"
+"This month," "This year."
This is shaping up well. Let's add some search capability::
@@ -435,31 +399,28 @@ That's easy to change, though, using Django's template system. The Django admin
is powered by Django itself, and its interfaces use Django's own template
system.
-Open your settings file (``mysite/settings.py``, remember) and look at the
-:setting:`TEMPLATE_DIRS` setting. :setting:`TEMPLATE_DIRS` is a tuple of
-filesystem directories to check when loading Django templates. It's a search
-path.
+.. _ref-customizing-your-projects-templates:
-Create a ``mytemplates`` directory in your project directory. Templates can
+Customizing your *project's* templates
+--------------------------------------
+
+Create a ``templates`` directory in your project directory. Templates can
live anywhere on your filesystem that Django can access. (Django runs as
whatever user your server runs.) However, keeping your templates within the
project is a good convention to follow.
-By default, :setting:`TEMPLATE_DIRS` is empty. So, let's add a line to it, to
-tell Django where our templates live::
+Open your settings file (:file:`mysite/settings.py`, remember) and add a
+:setting:`TEMPLATE_DIRS` setting::
+
+ TEMPLATE_DIRS = [os.path.join(BASE_DIR, 'templates')]
- TEMPLATE_DIRS = (
- '/path/to/mysite/mytemplates', # Change this to your own directory.
- )
+:setting:`TEMPLATE_DIRS` is an iterable of filesystem directories to check when
+loading Django templates; it's a search path.
-Now copy the template ``admin/base_site.html`` from within the default Django
-admin template directory in the source code of Django itself
-(``django/contrib/admin/templates``) into an ``admin`` subdirectory of
-whichever directory you're using in :setting:`TEMPLATE_DIRS`. For example, if
-your :setting:`TEMPLATE_DIRS` includes ``'/path/to/mysite/mytemplates'``, as
-above, then copy ``django/contrib/admin/templates/admin/base_site.html`` to
-``/path/to/mysite/mytemplates/admin/base_site.html``. Don't forget that
-``admin`` subdirectory.
+Now create a directory called ``admin`` inside ``templates``, and copy the
+template ``admin/base_site.html`` from within the default Django admin
+template directory in the source code of Django itself
+(``django/contrib/admin/templates``) into that directory.
.. admonition:: Where are the Django source files?
@@ -489,11 +450,24 @@ override a template, just do the same thing you did with ``base_site.html`` --
copy it from the default directory into your custom directory, and make
changes.
+Customizing your *application's* templates
+------------------------------------------
+
Astute readers will ask: But if :setting:`TEMPLATE_DIRS` was empty by default,
how was Django finding the default admin templates? The answer is that, by
default, Django automatically looks for a ``templates/`` subdirectory within
-each app package, for use as a fallback. See the :ref:`template loader
-documentation <template-loaders>` for full information.
+each application package, for use as a fallback (don't forget that
+``django.contrib.admin`` is an application).
+
+Our poll application is not very complex and doesn't need custom admin
+templates. But if it grew more sophisticated and required modification of
+Django's standard admin templates for some of its functionality, it would be
+more sensible to modify the *application's* templates, rather than those in the
+*project*. That way, you could include the polls application in any new project
+and be assured that it would find the custom templates it needed.
+
+See the :ref:`template loader documentation <template-loaders>` for more
+information about how Django finds its templates.
Customize the admin index page
==============================
diff --git a/docs/intro/tutorial03.txt b/docs/intro/tutorial03.txt
index 3159ee88c2..86cc5f97e6 100644
--- a/docs/intro/tutorial03.txt
+++ b/docs/intro/tutorial03.txt
@@ -39,7 +39,24 @@ In our poll application, we'll have the following four views:
* Vote action -- handles voting for a particular choice in a particular
poll.
-In Django, each view is represented by a simple Python function.
+In Django, web pages and other content are delivered by views. Each view is
+represented by a simple Python function (or method, in the case of class-based
+views). Django will choose a view by examining the URL that's requested (to be
+precise, the part of the URL after the domain name).
+
+Now in your time on the web you may have come across such beauties as
+"ME2/Sites/dirmod.asp?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B".
+You will be pleased to know that Django allows us much more elegant
+*URL patterns* than that.
+
+A URL pattern is simply the general form of a URL - for example:
+``/newsarchive/<year>/<month>/``.
+
+To get from a URL to a view, Django uses what are known as 'URLconfs'. A
+URLconf maps URL patterns (described as regular expressions) to views.
+
+This tutorial provides basic instruction in the use of URLconfs, and you can
+refer to :mod:`django.core.urlresolvers` for more information.
Write your first view
=====================
@@ -52,19 +69,8 @@ and put the following Python code in it::
def index(request):
return HttpResponse("Hello, world. You're at the poll index.")
-This is the simplest view possible in Django. Now we have a problem, how does
-this view get called? For that we need to map it to a URL, in Django this is
-done in a configuration file called a URLconf.
-
-.. admonition:: What is a URLconf?
-
- In Django, web pages and other content are delivered by views and
- determining which view is called is done by Python modules informally
- titled 'URLconfs'. These modules are pure Python code and are a simple
- mapping between URL patterns (as simple regular expressions) to Python
- callback functions (your views). This tutorial provides basic instruction
- in their use, and you can refer to :mod:`django.core.urlresolvers` for
- more information.
+This is the simplest view possible in Django. To call the view, we need to map
+it to a URL - and for this we need a URLconf.
To create a URLconf in the polls directory, create a file called ``urls.py``.
Your app directory should now look like::
@@ -101,7 +107,7 @@ with::
url(r'^admin/', include(admin.site.urls)),
)
-You have now wired an `index` view into the URLconf. Go to
+You have now wired an ``index`` view into the URLconf. Go to
http://localhost:8000/polls/ in your browser, and you should see the text
"*Hello, world. You're at the poll index.*", which you defined in the
``index`` view.
@@ -113,7 +119,7 @@ At this point, it's worth reviewing what these arguments are for.
:func:`~django.conf.urls.url` argument: regex
---------------------------------------------
-The term `regex` is a commonly used short form meaning `regular expression`,
+The term "regex" is a commonly used short form meaning "regular expression",
which is a syntax for matching patterns in strings, or in this case, url
patterns. Django starts at the first regular expression and makes its way down
the list, comparing the requested URL against each regular expression until it
@@ -274,10 +280,48 @@ commas, according to publication date::
There's a problem here, though: the page's design is hard-coded in the view. If
you want to change the way the page looks, you'll have to edit this Python code.
-So let's use Django's template system to separate the design from Python.
+So let's use Django's template system to separate the design from Python by
+creating a template that the view can use.
+
+First, create a directory called ``templates`` in your ``polls`` directory.
+Django will look for templates in there.
+
+Django's :setting:`TEMPLATE_LOADERS` setting contains a list of callables that
+know how to import templates from various sources. One of the defaults is
+:class:`django.template.loaders.app_directories.Loader` which looks for a
+"templates" subdirectory in each of the :setting:`INSTALLED_APPS` - this is how
+Django knows to find the polls templates even though we didn't modify
+:setting:`TEMPLATE_DIRS`, as we did in :ref:`Tutorial 2
+<ref-customizing-your-projects-templates>`.
+
+.. admonition:: Organizing templates
+
+ We *could* have all our templates together, in one big templates directory,
+ and it would work perfectly well. However, this template belongs to the
+ polls application, so unlike the admin template we created in the previous
+ tutorial, we'll put this one in the application's template directory
+ (``polls/templates``) rather than the project's (``templates``). We'll
+ discuss in more detail in the :doc:`reusable apps tutorial
+ </intro/reusable-apps>` *why* we do this.
+
+Within the ``templates`` directory you have just created, create another
+directory called ``polls``, and within that create a file called
+``index.html``. In other words, your template should be at
+``polls/templates/polls/index.html``. Because of how the ``app_directories``
+template loader works as described above, you can refer to this template within
+Django simply as ``polls/index.html``.
+
+.. admonition:: Template namespacing
+
+ Now we *might* be able to get away with putting our templates directly in
+ ``polls/templates`` (rather than creating another ``polls`` subdirectory),
+ but it would actually be a bad idea. Django will choose the first template
+ it finds whose name matches, and if you had a template with the same name
+ in a *different* application, Django would be unable to distinguish between
+ them. We need to be able to point Django at the right one, and the easiest
+ way to ensure this is by *namespacing* them. That is, by putting those
+ templates inside *another* directory named for the application itself.
-First, create a directory ``polls`` in your template directory you specified
-in setting:`TEMPLATE_DIRS`. Within that, create a file called ``index.html``.
Put the following code in that template:
.. code-block:: html+django
@@ -311,15 +355,9 @@ That code loads the template called ``polls/index.html`` and passes it a
context. The context is a dictionary mapping template variable names to Python
objects.
-Load the page in your Web browser, and you should see a bulleted-list
-containing the "What's up" poll from Tutorial 1. The link points to the poll's
-detail page.
-
-.. admonition:: Organizing Templates
-
- Rather than one big templates directory, you can also store templates
- within each app. We'll discuss this in more detail in the :doc:`reusable
- apps tutorial</intro/reusable-apps>`.
+Load the page by pointing your browser at "/polls/", and you should see a
+bulleted-list containing the "What's up" poll from Tutorial 1. The link points
+to the poll's detail page.
A shortcut: :func:`~django.shortcuts.render`
--------------------------------------------
@@ -367,7 +405,8 @@ The new concept here: The view raises the :exc:`~django.http.Http404` exception
if a poll with the requested ID doesn't exist.
We'll discuss what you could put in that ``polls/detail.html`` template a bit
-later, but if you'd like to quickly get the above example working, just::
+later, but if you'd like to quickly get the above example working, a file
+containing just::
{{ poll }}
@@ -536,8 +575,9 @@ view, and so might an app on the same project that is for a blog. How does one
make it so that Django knows which app view to create for a url when using the
``{% url %}`` template tag?
-The answer is to add namespaces to your root URLconf. In the
-``mysite/urls.py`` file, go ahead and change it to include namespacing::
+The answer is to add namespaces to your root URLconf. In the ``mysite/urls.py``
+file (the project's ``urls.py``, not the application's), go ahead and change
+it to include namespacing::
from django.conf.urls import patterns, include, url
diff --git a/docs/intro/tutorial04.txt b/docs/intro/tutorial04.txt
index 1619b599bb..87d8e584ad 100644
--- a/docs/intro/tutorial04.txt
+++ b/docs/intro/tutorial04.txt
@@ -33,7 +33,7 @@ A quick rundown:
``value`` of each radio button is the associated poll choice's ID. The
``name`` of each radio button is ``"choice"``. That means, when somebody
selects one of the radio buttons and submits the form, it'll send the
- POST data ``choice=3``. This is HTML Forms 101.
+ POST data ``choice=3``. This is the basic concept of HTML forms.
* We set the form's ``action`` to ``{% url 'polls:vote' poll.id %}``, and we
set ``method="post"``. Using ``method="post"`` (as opposed to
@@ -98,9 +98,10 @@ This code includes a few things we haven't covered yet in this tutorial:
<django.http.HttpRequest.POST>` in our code, to ensure that data is only
altered via a POST call.
-* ``request.POST['choice']`` will raise :exc:`KeyError` if ``choice`` wasn't
- provided in POST data. The above code checks for :exc:`KeyError` and
- redisplays the poll form with an error message if ``choice`` isn't given.
+* ``request.POST['choice']`` will raise :exc:`~exceptions.KeyError` if
+ ``choice`` wasn't provided in POST data. The above code checks for
+ :exc:`~exceptions.KeyError` and redisplays the poll form with an error
+ message if ``choice`` isn't given.
* After incrementing the choice count, the code returns an
:class:`~django.http.HttpResponseRedirect` rather than a normal
@@ -198,6 +199,9 @@ Read on for details.
You should know basic math before you start using a calculator.
+Amend URLconf
+-------------
+
First, open the ``polls/urls.py`` URLconf and change it like so::
from django.conf.urls import patterns, url
@@ -224,6 +228,9 @@ First, open the ``polls/urls.py`` URLconf and change it like so::
url(r'^(?P<poll_id>\d+)/vote/$', 'polls.views.vote', name='vote'),
)
+Amend views
+-----------
+
We're using two generic views here:
:class:`~django.views.generic.list.ListView` and
:class:`~django.views.generic.detail.DetailView`. Respectively, those
@@ -233,12 +240,12 @@ two views abstract the concepts of "display a list of objects" and
* Each generic view needs to know what model it will be acting
upon. This is provided using the ``model`` parameter.
-* The :class:`~django.views.generic.list.DetailView` generic view
+* The :class:`~django.views.generic.detail.DetailView` generic view
expects the primary key value captured from the URL to be called
``"pk"``, so we've changed ``poll_id`` to ``pk`` for the generic
views.
-By default, the :class:`~django.views.generic.list.DetailView` generic
+By default, the :class:`~django.views.generic.detail.DetailView` generic
view uses a template called ``<app name>/<model name>_detail.html``.
In our case, it'll use the template ``"polls/poll_detail.html"``. The
``template_name`` argument is used to tell Django to use a specific
@@ -246,7 +253,7 @@ template name instead of the autogenerated default template name. We
also specify the ``template_name`` for the ``results`` list view --
this ensures that the results view and the detail view have a
different appearance when rendered, even though they're both a
-:class:`~django.views.generic.list.DetailView` behind the scenes.
+:class:`~django.views.generic.detail.DetailView` behind the scenes.
Similarly, the :class:`~django.views.generic.list.ListView` generic
view uses a default template called ``<app name>/<model
@@ -256,7 +263,7 @@ name>_list.html``; we use ``template_name`` to tell
In previous parts of the tutorial, the templates have been provided
with a context that contains the ``poll`` and ``latest_poll_list``
-context variables. For DetailView the ``poll`` variable is provided
+context variables. For ``DetailView`` the ``poll`` variable is provided
automatically -- since we're using a Django model (``Poll``), Django
is able to determine an appropriate name for the context variable.
However, for ListView, the automatically generated context variable is
@@ -266,9 +273,10 @@ As an alternative approach, you could change your templates to match
the new default context variables -- but it's a lot easier to just
tell Django to use the variable you want.
-You can now delete the ``index()``, ``detail()`` and ``results()``
-views from ``polls/views.py``. We don't need them anymore -- they have
-been replaced by generic views.
+You can now delete the ``index()``, ``detail()`` and ``results()`` views from
+``polls/views.py``. We don't need them anymore -- they have been replaced by
+generic views. You can also delete the import for ``HttpResponse``, which is no
+longer required.
Run the server, and use your new polling app based on generic views.
diff --git a/docs/intro/tutorial05.txt b/docs/intro/tutorial05.txt
index 163b7cdd0f..3b0a95f253 100644
--- a/docs/intro/tutorial05.txt
+++ b/docs/intro/tutorial05.txt
@@ -121,7 +121,7 @@ the next time you make a change, either when you add a new feature or fix a bug.
So let's do that right away.
-.. _test-driven development: http://en.wikipedia.org/wiki/Test-driven_development/
+.. _test-driven development: http://en.wikipedia.org/wiki/Test-driven_development
Writing our first test
======================
@@ -159,8 +159,7 @@ can do in an automated test, so let's turn that into an automated test.
The best place for an application's tests is in the application's ``tests.py``
file - the testing system will look there for tests automatically.
-Put the following in the ``tests.py`` file in the ``polls`` application (you'll
-notice ``tests.py`` contains some dummy tests, you can remove those)::
+Put the following in the ``tests.py`` file in the ``polls`` application::
import datetime
@@ -632,7 +631,7 @@ a piece of code, it usually means that code should be refactored or removed.
Coverage will help to identify dead code. See
:ref:`topics-testing-code-coverage` for details.
-:doc:`Testing Django applications </topics/testing>` has comprehensive
+:doc:`Testing Django applications </topics/testing/index>` has comprehensive
information about testing.
.. _Selenium: http://seleniumhq.org/
@@ -641,10 +640,9 @@ information about testing.
What's next?
============
-The beginner tutorial ends here for the time being. In the meantime, you might
-want to check out some pointers on :doc:`where to go from here
-</intro/whatsnext>`.
+For full details on testing, see :doc:`Testing in Django
+</topics/testing/index>`.
-If you are familiar with Python packaging and interested in learning how to
-turn polls into a "reusable app", check out :doc:`Advanced tutorial: How to
-write reusable apps</intro/reusable-apps>`.
+When you're comfortable with testing Django views, read
+:doc:`part 6 of this tutorial</intro/tutorial06>` to learn about
+static files management.
diff --git a/docs/intro/tutorial06.txt b/docs/intro/tutorial06.txt
new file mode 100644
index 0000000000..6b3d0f35e2
--- /dev/null
+++ b/docs/intro/tutorial06.txt
@@ -0,0 +1,125 @@
+=====================================
+Writing your first Django app, part 6
+=====================================
+
+This tutorial begins where :doc:`Tutorial 5 </intro/tutorial05>` left off.
+We've built a tested Web-poll application, and we'll now add a stylesheet and
+an image.
+
+Aside from the HTML generated by the server, web applications generally need
+to serve additional files — such as images, JavaScript, or CSS — necessary to
+render the complete web page. In Django, we refer to these files as "static
+files".
+
+For small projects, this isn't a big deal, because you can just keep the
+static files somewhere your web server can find it. However, in bigger
+projects -- especially those comprised of multiple apps -- dealing with the
+multiple sets of static files provided by each application starts to get
+tricky.
+
+That's what ``django.contrib.staticfiles`` is for: it collects static files
+from each of your applications (and any other places you specify) into a
+single location that can easily be served in production.
+
+Customize your *app's* look and feel
+====================================
+
+First, create a directory called ``static`` in your ``polls`` directory. Django
+will look for static files there, similarly to how Django finds templates
+inside ``polls/templates/``.
+
+Django's :setting:`STATICFILES_FINDERS` setting contains a list
+of finders that know how to discover static files from various
+sources. One of the defaults is ``AppDirectoriesFinder`` which
+looks for a "static" subdirectory in each of the
+:setting:`INSTALLED_APPS`, like the one in ``polls`` we just created. The admin
+site uses the same directory structure for its static files.
+
+Within the ``static`` directory you have just created, create another directory
+called ``polls`` and within that create a file called ``style.css``. In other
+words, your stylesheet should be at ``polls/static/polls/style.css``. Because
+of how the ``AppDirectoriesFinder`` staticfile finder works, you can refer to
+this static file in Django simply as ``polls/style.css``, similar to how you
+reference the path for templates.
+
+.. admonition:: Static file namespacing
+
+ Just like templates, we *might* be able to get away with putting our static
+ files directly in ``polls/static`` (rather than creating another ``polls``
+ subdirectory), but it would actually be a bad idea. Django will choose the
+ first static file it finds whose name matches, and if you had a static file
+ with the same name in a *different* application, Django would be unable to
+ distinguish between them. We need to be able to point Django at the right
+ one, and the easiest way to ensure this is by *namespacing* them. That is,
+ by putting those static files inside *another* directory named for the
+ application itself.
+
+Put the following code in that stylesheet (``polls/static/polls/style.css``):
+
+.. code-block:: css
+
+ li a {
+ color: green;
+ }
+
+Next, add the following at the top of ``polls/templates/polls/index.html``:
+
+.. code-block:: html+django
+
+ {% load staticfiles %}
+
+ <link rel="stylesheet" type="text/css" href="{% static 'polls/style.css' %}" />
+
+``{% load staticfiles %}`` loads the :ttag:`{% static %} <staticfiles-static>`
+template tag from the ``staticfiles`` template library. The ``{% static %}``
+template tag generates the absolute URL of the static file.
+
+That's all you need to do for development. Reload
+``http://localhost:8000/polls/`` and you should see that the poll links are
+green (Django style!) which means that your stylesheet was properly loaded.
+
+Adding a background-image
+=========================
+
+Next, we'll create a subdirectory for images. Create an ``images`` subdirectory
+in the ``polls/static/polls/`` directory. Inside this directory, put an image
+called ``background.gif``. In other words, put your image in
+``polls/static/polls/images/background.gif``.
+
+Then, add to your stylesheet (``polls/static/polls/style.css``):
+
+.. code-block:: css
+
+ body {
+ background: white url("images/background.gif") no-repeat right bottom;
+ }
+
+Reload ``http://localhost:8000/polls/`` and you should see the background
+loaded in the bottom right of the screen.
+
+.. warning::
+
+ Of course the ``{% static %}`` template tag is not available for use in
+ static files like your stylesheet which aren't generated by Django. You
+ should always use **relative paths** to link your static files between each
+ other, because then you can change :setting:`STATIC_URL` (used by the
+ :ttag:`static` template tag to generate its URLs) without having to modify
+ a bunch of paths in your static files as well.
+
+These are the **basics**. For more details on settings and other bits included
+with the framework see
+:doc:`the static files howto </howto/static-files/index>` and the
+:doc:`the staticfiles reference </ref/contrib/staticfiles>`. :doc:`Deploying
+static files </howto/static-files/deployment>` discusses how to use static
+files on a real server.
+
+What's next?
+============
+
+The beginner tutorial ends here for the time being. In the meantime, you might
+want to check out some pointers on :doc:`where to go from here
+</intro/whatsnext>`.
+
+If you are familiar with Python packaging and interested in learning how to
+turn polls into a "reusable app", check out :doc:`Advanced tutorial: How to
+write reusable apps</intro/reusable-apps>`.
diff --git a/docs/intro/whatsnext.txt b/docs/intro/whatsnext.txt
index 500a858d47..a677bc9efd 100644
--- a/docs/intro/whatsnext.txt
+++ b/docs/intro/whatsnext.txt
@@ -4,8 +4,8 @@ What to read next
So you've read all the :doc:`introductory material </intro/index>` and have
decided you'd like to keep using Django. We've only just scratched the surface
-with this intro (in fact, if you've read every single word you've still read
-less than 10% of the overall documentation).
+with this intro (in fact, if you've read every single word, you've read about
+5% of the overall documentation).
So what's next?
@@ -23,9 +23,9 @@ to write a document about how to read the document about documentation.)
Finding documentation
=====================
-Django's got a *lot* of documentation -- almost 200,000 words -- so finding what
-you need can sometimes be tricky. A few good places to start are the :ref:`search`
-and the :ref:`genindex`.
+Django's got a *lot* of documentation -- almost 450,000 words and counting --
+so finding what you need can sometimes be tricky. A few good places to start
+are the :ref:`search` and the :ref:`genindex`.
Or you can just browse around!
diff --git a/docs/make.bat b/docs/make.bat
index d7f54b2059..65602aa160 100644
--- a/docs/make.bat
+++ b/docs/make.bat
@@ -6,7 +6,7 @@ if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set BUILDDIR=_build
-set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
+set ALLSPHINXOPTS=-n -d %BUILDDIR%/doctrees %SPHINXOPTS% .
if NOT "%PAPER%" == "" (
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
diff --git a/docs/misc/api-stability.txt b/docs/misc/api-stability.txt
index 4f232e795b..8517866769 100644
--- a/docs/misc/api-stability.txt
+++ b/docs/misc/api-stability.txt
@@ -4,17 +4,19 @@ API stability
:doc:`The release of Django 1.0 </releases/1.0>` comes with a promise of API
stability and forwards-compatibility. In a nutshell, this means that code you
-develop against Django 1.0 will continue to work against 1.1 unchanged, and you
-should need to make only minor changes for any 1.X release.
+develop against a 1.X version of Django will continue to work with future
+1.X releases. You may need to make minor changes when upgrading the version of
+Django your project uses: see the "Backwards incompatible changes" section of
+the :doc:`release note </releases/index>` for the version or versions to which
+you are upgrading.
What "stable" means
===================
In this context, stable means:
-- All the public APIs -- everything documented in the linked documents below,
- and all methods that don't begin with an underscore -- will not be moved or
- renamed without providing backwards-compatible aliases.
+- All the public APIs (everything in this documentation) will not be moved
+ or renamed without providing backwards-compatible aliases.
- If new features are added to these APIs -- which is quite possible --
they will not break or change the meaning of existing methods. In other
@@ -35,77 +37,7 @@ Stable APIs
===========
In general, everything covered in the documentation -- with the exception of
-anything in the :doc:`internals area </internals/index>` is considered stable as
-of 1.0. This includes these APIs:
-
-- :doc:`Authorization </topics/auth>`
-
-- :doc:`Caching </topics/cache>`.
-
-- :doc:`Model definition, managers, querying and transactions
- </topics/db/index>`
-
-- :doc:`Sending email </topics/email>`.
-
-- :doc:`File handling and storage </topics/files>`
-
-- :doc:`Forms </topics/forms/index>`
-
-- :doc:`HTTP request/response handling </topics/http/index>`, including file
- uploads, middleware, sessions, URL resolution, view, and shortcut APIs.
-
-- :doc:`Generic views </topics/class-based-views/index>`.
-
-- :doc:`Internationalization </topics/i18n/index>`.
-
-- :doc:`Pagination </topics/pagination>`
-
-- :doc:`Serialization </topics/serialization>`
-
-- :doc:`Signals </topics/signals>`
-
-- :doc:`Templates </topics/templates>`, including the language, Python-level
- :doc:`template APIs </ref/templates/index>`, and :doc:`custom template tags
- and libraries </howto/custom-template-tags>`. We may add new template
- tags in the future and the names may inadvertently clash with
- external template tags. Before adding any such tags, we'll ensure that
- Django raises an error if it tries to load tags with duplicate names.
-
-- :doc:`Testing </topics/testing>`
-
-- :doc:`django-admin utility </ref/django-admin>`.
-
-- :doc:`Built-in middleware </ref/middleware>`
-
-- :doc:`Request/response objects </ref/request-response>`.
-
-- :doc:`Settings </ref/settings>`. Note, though that while the :doc:`list of
- built-in settings </ref/settings>` can be considered complete we may -- and
- probably will -- add new settings in future versions. This is one of those
- places where "'stable' does not mean 'complete.'"
-
-- :doc:`Built-in signals </ref/signals>`. Like settings, we'll probably add
- new signals in the future, but the existing ones won't break.
-
-- :doc:`Unicode handling </ref/unicode>`.
-
-- Everything covered by the :doc:`HOWTO guides </howto/index>`.
-
-``django.utils``
-----------------
-
-Most of the modules in ``django.utils`` are designed for internal use. Only
-the following parts of :doc:`django.utils </ref/utils>` can be considered stable:
-
-- ``django.utils.cache``
-- ``django.utils.datastructures.SortedDict`` -- only this single class; the
- rest of the module is for internal use.
-- ``django.utils.encoding``
-- ``django.utils.feedgenerator``
-- ``django.utils.http``
-- ``django.utils.safestring``
-- ``django.utils.translation``
-- ``django.utils.tzinfo``
+anything in the :doc:`internals area </internals/index>` is considered stable.
Exceptions
==========
@@ -118,24 +50,8 @@ Security fixes
If we become aware of a security problem -- hopefully by someone following our
:ref:`security reporting policy <reporting-security-issues>` -- we'll do
-everything necessary to fix it. This might mean breaking backwards compatibility; security trumps the compatibility guarantee.
-
-Contributed applications (``django.contrib``)
----------------------------------------------
-
-While we'll make every effort to keep these APIs stable -- and have no plans to
-break any contrib apps -- this is an area that will have more flux between
-releases. As the Web evolves, Django must evolve with it.
-
-However, any changes to contrib apps will come with an important guarantee:
-we'll make sure it's always possible to use an older version of a contrib app if
-we need to make changes. Thus, if Django 1.5 ships with a backwards-incompatible
-``django.contrib.flatpages``, we'll make sure you can still use the Django 1.4
-version alongside Django 1.5. This will continue to allow for easy upgrades.
-
-Historically, apps in ``django.contrib`` have been more stable than the core, so
-in practice we probably won't have to ever make this exception. However, it's
-worth noting if you're building apps that depend on ``django.contrib``.
+everything necessary to fix it. This might mean breaking backwards
+compatibility; security trumps the compatibility guarantee.
APIs marked as internal
-----------------------
@@ -149,56 +65,3 @@ Certain APIs are explicitly marked as "internal" in a couple of ways:
- Functions, methods, and other objects prefixed by a leading underscore
(``_``). This is the standard Python way of indicating that something is
private; if any method starts with a single ``_``, it's an internal API.
-
-.. _misc-api-stability-localflavor:
-
-Local flavors
--------------
-
-:mod:`django.contrib.localflavor` contains assorted pieces of code
-that are useful for particular countries or cultures. This data is
-local in nature, and is subject to change on timelines that will
-almost never correlate with Django's own release schedules. For
-example, a common change is to split a province into two new
-provinces, or to rename an existing province.
-
-These changes present two competing compatibility issues. Moving
-forward, displaying the names of deprecated, renamed and dissolved
-provinces in a selection widget is bad from a user interface
-perspective. However, maintaining full backwards compatibility
-requires that we support historical values that may be stored in a
-database -- including values that may no longer be valid.
-
-Therefore, Django has the following policy with respect to changes in
-local flavor:
-
-* At the time of a Django release, the data and algorithms
- contained in :mod:`django.contrib.localflavor` will, to the best
- of our ability, reflect the officially gazetted policies of the
- appropriate local government authority. If a province has been
- added, altered, or removed, that change will be reflected in
- Django's localflavor.
-
-* These changes will *not* be backported to the previous stable
- release. Upgrading a minor version of Django should not require
- any data migration or audits for UI changes; therefore, if you
- want to get the latest province list, you will either need to
- upgrade your Django install, or backport the province list you
- need.
-
-* For one release, the affected localflavor module will raise a
- ``RuntimeWarning`` when it is imported.
-
-* The change will be announced in the release notes as a backwards
- incompatible change requiring attention. The change will also be
- annotated in the documentation for the localflavor module.
-
-* Where necessary and feasible, a migration script will be provided
- to aid the migration process.
-
-For example, Django 1.2 contains an Indonesian localflavor. It has a
-province list that includes "Nanggroe Aceh Darussalam (NAD)" as a
-province. The Indonesian government has changed the official name of
-the province to "Aceh (ACE)". As a result, Django 1.3 does *not*
-contain "Nanggroe Aceh Darussalam (NAD)" in the province list, but
-*does* contain "Aceh (ACE)".
diff --git a/docs/ref/authbackends.txt b/docs/ref/authbackends.txt
deleted file mode 100644
index 55a536e819..0000000000
--- a/docs/ref/authbackends.txt
+++ /dev/null
@@ -1,33 +0,0 @@
-=======================
-Authentication backends
-=======================
-
-.. module:: django.contrib.auth.backends
- :synopsis: Django's built-in authentication backend classes.
-
-This document details the authentication backends that come with Django. For
-information on how to use them and how to write your own authentication
-backends, see the :ref:`Other authentication sources section
-<authentication-backends>` of the :doc:`User authentication guide
-</topics/auth>`.
-
-
-Available authentication backends
-=================================
-
-The following backends are available in :mod:`django.contrib.auth.backends`:
-
-.. class:: ModelBackend
-
- This is the default authentication backend used by Django. It
- authenticates using usernames and passwords stored in the
- :class:`~django.contrib.auth.models.User` model.
-
-
-.. class:: RemoteUserBackend
-
- Use this backend to take advantage of external-to-Django-handled
- authentication. It authenticates using usernames passed in
- :attr:`request.META['REMOTE_USER'] <django.http.HttpRequest.META>`. See
- the :doc:`Authenticating against REMOTE_USER </howto/auth-remote-user>`
- documentation.
diff --git a/docs/ref/class-based-views/base.txt b/docs/ref/class-based-views/base.txt
index cc9aa852f1..2073458314 100644
--- a/docs/ref/class-based-views/base.txt
+++ b/docs/ref/class-based-views/base.txt
@@ -49,9 +49,13 @@ View
**Attributes**
- .. attribute:: http_method_names = ['get', 'post', 'put', 'delete', 'head', 'options', 'trace']
+ .. attribute:: http_method_names
- The default list of HTTP method names that this view will accept.
+ The list of HTTP method names that this view will accept.
+
+ Default::
+
+ ['get', 'post', 'put', 'delete', 'head', 'options', 'trace']
**Methods**
@@ -68,12 +72,11 @@ View
The default implementation will inspect the HTTP method and attempt to
delegate to a method that matches the HTTP method; a ``GET`` will be
- delegated to :meth:`~View.get()`, a ``POST`` to :meth:`~View.post()`,
- and so on.
+ delegated to ``get()``, a ``POST`` to ``post()``, and so on.
- By default, a ``HEAD`` request will be delegated to :meth:`~View.get()`.
+ By default, a ``HEAD`` request will be delegated to ``get()``.
If you need to handle ``HEAD`` requests in a different way than ``GET``,
- you can override the :meth:`~View.head()` method. See
+ you can override the ``head()`` method. See
:ref:`supporting-other-http-methods` for an example.
The default implementation also sets ``request``, ``args`` and
@@ -98,8 +101,13 @@ TemplateView
.. class:: django.views.generic.base.TemplateView
- Renders a given template, passing it a ``{{ params }}`` template variable,
- which is a dictionary of the parameters captured in the URL.
+ Renders a given template, with the context containing parameters captured
+ in the URL.
+
+ .. versionchanged:: 1.5
+ The context used to be populated with a ``{{ params }}`` dictionary of
+ the parameters captured in the URL. Now those parameters are first-level
+ context variables.
**Ancestors (MRO)**
@@ -111,9 +119,9 @@ TemplateView
**Method Flowchart**
- 1. :meth:`dispatch()`
- 2. :meth:`http_method_not_allowed()`
- 3. :meth:`get_context_data()`
+ 1. :meth:`~django.views.generic.base.View.dispatch()`
+ 2. :meth:`~django.views.generic.base.View.http_method_not_allowed()`
+ 3. :meth:`~django.views.generic.base.ContextMixin.get_context_data()`
**Example views.py**::
@@ -169,8 +177,8 @@ RedirectView
**Method Flowchart**
- 1. :meth:`dispatch()`
- 2. :meth:`http_method_not_allowed()`
+ 1. :meth:`~django.views.generic.base.View.dispatch()`
+ 2. :meth:`~django.views.generic.base.View.http_method_not_allowed()`
3. :meth:`get_redirect_url()`
**Example views.py**::
@@ -230,9 +238,8 @@ RedirectView
Constructs the target URL for redirection.
- The default implementation uses :attr:`~RedirectView.url` as a starting
+ The default implementation uses :attr:`url` as a starting
string, performs expansion of ``%`` parameters in that string, as well
- as the appending of query string if requested by
- :attr:`~RedirectView.query_string`. Subclasses may implement any
- behavior they wish, as long as the method returns a redirect-ready URL
- string.
+ as the appending of query string if requested by :attr:`query_string`.
+ Subclasses may implement any behavior they wish, as long as the method
+ returns a redirect-ready URL string.
diff --git a/docs/ref/class-based-views/flattened-index.txt b/docs/ref/class-based-views/flattened-index.txt
index aa2f51f156..df00f87aa0 100644
--- a/docs/ref/class-based-views/flattened-index.txt
+++ b/docs/ref/class-based-views/flattened-index.txt
@@ -23,7 +23,7 @@ View
* :meth:`~django.views.generic.base.View.as_view`
* :meth:`~django.views.generic.base.View.dispatch`
-* :meth:`~django.views.generic.base.View.head`
+* ``head()``
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
TemplateView
@@ -32,17 +32,18 @@ TemplateView
**Attributes** (with optional accessor):
+* :attr:`~django.views.generic.base.TemplateResponseMixin.content_type`
* :attr:`~django.views.generic.base.View.http_method_names`
-* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
+* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
**Methods**
* :meth:`~django.views.generic.base.View.as_view`
* :meth:`~django.views.generic.base.View.dispatch`
-* :meth:`~django.views.generic.base.TemplateView.get`
-* :meth:`~django.views.generic.base.TemplateView.get_context_data`
-* :meth:`~django.views.generic.base.View.head`
+* ``get()``
+* :meth:`~django.views.generic.base.ContextMixin.get_context_data`
+* ``head()``
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
@@ -60,15 +61,15 @@ RedirectView
**Methods**
* :meth:`~django.views.generic.base.View.as_view`
-* :meth:`~django.views.generic.base.RedirectView.delete`
+* ``delete()``
* :meth:`~django.views.generic.base.View.dispatch`
-* :meth:`~django.views.generic.base.RedirectView.get`
+* ``get()``
* :meth:`~django.views.generic.base.RedirectView.get_redirect_url`
-* :meth:`~django.views.generic.base.View.head`
+* ``head()``
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
-* :meth:`~django.views.generic.base.RedirectView.options`
-* :meth:`~django.views.generic.base.RedirectView.post`
-* :meth:`~django.views.generic.base.RedirectView.put`
+* ``options()``
+* ``post()``
+* ``put()``
Detail Views
------------
@@ -79,12 +80,13 @@ DetailView
**Attributes** (with optional accessor):
+* :attr:`~django.views.generic.base.TemplateResponseMixin.content_type`
* :attr:`~django.views.generic.detail.SingleObjectMixin.context_object_name` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_context_object_name`]
* :attr:`~django.views.generic.base.View.http_method_names`
* :attr:`~django.views.generic.detail.SingleObjectMixin.model`
* :attr:`~django.views.generic.detail.SingleObjectMixin.pk_url_kwarg`
* :attr:`~django.views.generic.detail.SingleObjectMixin.queryset` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_queryset`]
-* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
+* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_field` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_slug_field`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_url_kwarg`
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
@@ -95,10 +97,10 @@ DetailView
* :meth:`~django.views.generic.base.View.as_view`
* :meth:`~django.views.generic.base.View.dispatch`
-* :meth:`~django.views.generic.detail.BaseDetailView.get`
+* ``get()``
* :meth:`~django.views.generic.detail.SingleObjectMixin.get_context_data`
* :meth:`~django.views.generic.detail.SingleObjectMixin.get_object`
-* :meth:`~django.views.generic.base.View.head`
+* ``head()``
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
@@ -112,6 +114,7 @@ ListView
**Attributes** (with optional accessor):
* :attr:`~django.views.generic.list.MultipleObjectMixin.allow_empty` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_allow_empty`]
+* :attr:`~django.views.generic.base.TemplateResponseMixin.content_type`
* :attr:`~django.views.generic.list.MultipleObjectMixin.context_object_name` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_context_object_name`]
* :attr:`~django.views.generic.base.View.http_method_names`
* :attr:`~django.views.generic.list.MultipleObjectMixin.model`
@@ -119,7 +122,7 @@ ListView
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_orphans` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_orphans`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
-* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
+* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix`
@@ -130,7 +133,7 @@ ListView
* :meth:`~django.views.generic.list.BaseListView.get`
* :meth:`~django.views.generic.list.MultipleObjectMixin.get_context_data`
* :meth:`~django.views.generic.list.MultipleObjectMixin.get_paginator`
-* :meth:`~django.views.generic.base.View.head`
+* ``head()``
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
* :meth:`~django.views.generic.list.MultipleObjectMixin.paginate_queryset`
* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
@@ -144,10 +147,11 @@ FormView
**Attributes** (with optional accessor):
+* :attr:`~django.views.generic.base.TemplateResponseMixin.content_type`
* :attr:`~django.views.generic.edit.FormMixin.form_class` [:meth:`~django.views.generic.edit.FormMixin.get_form_class`]
* :attr:`~django.views.generic.base.View.http_method_names`
* :attr:`~django.views.generic.edit.FormMixin.initial` [:meth:`~django.views.generic.edit.FormMixin.get_initial`]
-* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
+* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.edit.FormMixin.success_url` [:meth:`~django.views.generic.edit.FormMixin.get_success_url`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
@@ -161,11 +165,9 @@ FormView
* :meth:`~django.views.generic.edit.FormMixin.get_context_data`
* :meth:`~django.views.generic.edit.FormMixin.get_form`
* :meth:`~django.views.generic.edit.FormMixin.get_form_kwargs`
-* :meth:`~django.views.generic.base.View.head`
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
* :meth:`~django.views.generic.edit.ProcessFormView.post`
* :meth:`~django.views.generic.edit.ProcessFormView.put`
-* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
CreateView
~~~~~~~~~~
@@ -173,6 +175,7 @@ CreateView
**Attributes** (with optional accessor):
+* :attr:`~django.views.generic.base.TemplateResponseMixin.content_type`
* :attr:`~django.views.generic.detail.SingleObjectMixin.context_object_name` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_context_object_name`]
* :attr:`~django.views.generic.edit.FormMixin.form_class` [:meth:`~django.views.generic.edit.FormMixin.get_form_class`]
* :attr:`~django.views.generic.base.View.http_method_names`
@@ -180,7 +183,7 @@ CreateView
* :attr:`~django.views.generic.detail.SingleObjectMixin.model`
* :attr:`~django.views.generic.detail.SingleObjectMixin.pk_url_kwarg`
* :attr:`~django.views.generic.detail.SingleObjectMixin.queryset` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_queryset`]
-* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
+* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_field` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_slug_field`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_url_kwarg`
* :attr:`~django.views.generic.edit.FormMixin.success_url` [:meth:`~django.views.generic.edit.FormMixin.get_success_url`]
@@ -199,10 +202,10 @@ CreateView
* :meth:`~django.views.generic.edit.FormMixin.get_form`
* :meth:`~django.views.generic.edit.FormMixin.get_form_kwargs`
* :meth:`~django.views.generic.detail.SingleObjectMixin.get_object`
-* :meth:`~django.views.generic.base.View.head`
+* ``head()``
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
* :meth:`~django.views.generic.edit.ProcessFormView.post`
-* :meth:`~django.views.generic.edit.ProcessFormView.put`
+* ``put()``
* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
UpdateView
@@ -211,6 +214,7 @@ UpdateView
**Attributes** (with optional accessor):
+* :attr:`~django.views.generic.base.TemplateResponseMixin.content_type`
* :attr:`~django.views.generic.detail.SingleObjectMixin.context_object_name` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_context_object_name`]
* :attr:`~django.views.generic.edit.FormMixin.form_class` [:meth:`~django.views.generic.edit.FormMixin.get_form_class`]
* :attr:`~django.views.generic.base.View.http_method_names`
@@ -218,7 +222,7 @@ UpdateView
* :attr:`~django.views.generic.detail.SingleObjectMixin.model`
* :attr:`~django.views.generic.detail.SingleObjectMixin.pk_url_kwarg`
* :attr:`~django.views.generic.detail.SingleObjectMixin.queryset` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_queryset`]
-* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
+* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_field` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_slug_field`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_url_kwarg`
* :attr:`~django.views.generic.edit.FormMixin.success_url` [:meth:`~django.views.generic.edit.FormMixin.get_success_url`]
@@ -237,10 +241,10 @@ UpdateView
* :meth:`~django.views.generic.edit.FormMixin.get_form`
* :meth:`~django.views.generic.edit.FormMixin.get_form_kwargs`
* :meth:`~django.views.generic.detail.SingleObjectMixin.get_object`
-* :meth:`~django.views.generic.base.View.head`
+* ``head()``
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
* :meth:`~django.views.generic.edit.ProcessFormView.post`
-* :meth:`~django.views.generic.edit.ProcessFormView.put`
+* ``put()``
* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
DeleteView
@@ -249,12 +253,13 @@ DeleteView
**Attributes** (with optional accessor):
+* :attr:`~django.views.generic.base.TemplateResponseMixin.content_type`
* :attr:`~django.views.generic.detail.SingleObjectMixin.context_object_name` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_context_object_name`]
* :attr:`~django.views.generic.base.View.http_method_names`
* :attr:`~django.views.generic.detail.SingleObjectMixin.model`
* :attr:`~django.views.generic.detail.SingleObjectMixin.pk_url_kwarg`
* :attr:`~django.views.generic.detail.SingleObjectMixin.queryset` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_queryset`]
-* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
+* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_field` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_slug_field`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_url_kwarg`
* :attr:`~django.views.generic.edit.DeletionMixin.success_url` [:meth:`~django.views.generic.edit.DeletionMixin.get_success_url`]
@@ -265,14 +270,14 @@ DeleteView
**Methods**
* :meth:`~django.views.generic.base.View.as_view`
-* :meth:`~django.views.generic.edit.DeletionMixin.delete`
+* ``delete()``
* :meth:`~django.views.generic.base.View.dispatch`
-* :meth:`~django.views.generic.detail.BaseDetailView.get`
+* ``get()``
* :meth:`~django.views.generic.detail.SingleObjectMixin.get_context_data`
* :meth:`~django.views.generic.detail.SingleObjectMixin.get_object`
-* :meth:`~django.views.generic.base.View.head`
+* ``head()``
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
-* :meth:`~django.views.generic.edit.DeletionMixin.post`
+* ``post()``
* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
Date-based views
@@ -286,6 +291,7 @@ ArchiveIndexView
* :attr:`~django.views.generic.list.MultipleObjectMixin.allow_empty` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_allow_empty`]
* :attr:`~django.views.generic.dates.DateMixin.allow_future` [:meth:`~django.views.generic.dates.DateMixin.get_allow_future`]
+* :attr:`~django.views.generic.base.TemplateResponseMixin.content_type`
* :attr:`~django.views.generic.list.MultipleObjectMixin.context_object_name` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_context_object_name`]
* :attr:`~django.views.generic.dates.DateMixin.date_field` [:meth:`~django.views.generic.dates.DateMixin.get_date_field`]
* :attr:`~django.views.generic.base.View.http_method_names`
@@ -294,7 +300,7 @@ ArchiveIndexView
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_orphans` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_orphans`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
-* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
+* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix`
@@ -302,13 +308,13 @@ ArchiveIndexView
* :meth:`~django.views.generic.base.View.as_view`
* :meth:`~django.views.generic.base.View.dispatch`
-* :meth:`~django.views.generic.dates.BaseDateListView.get`
+* ``get()``
* :meth:`~django.views.generic.list.MultipleObjectMixin.get_context_data`
* :meth:`~django.views.generic.dates.BaseDateListView.get_date_list`
* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_items`
* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_queryset`
* :meth:`~django.views.generic.list.MultipleObjectMixin.get_paginator`
-* :meth:`~django.views.generic.base.View.head`
+* ``head()``
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
* :meth:`~django.views.generic.list.MultipleObjectMixin.paginate_queryset`
* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
@@ -321,16 +327,17 @@ YearArchiveView
* :attr:`~django.views.generic.list.MultipleObjectMixin.allow_empty` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_allow_empty`]
* :attr:`~django.views.generic.dates.DateMixin.allow_future` [:meth:`~django.views.generic.dates.DateMixin.get_allow_future`]
+* :attr:`~django.views.generic.base.TemplateResponseMixin.content_type`
* :attr:`~django.views.generic.list.MultipleObjectMixin.context_object_name` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_context_object_name`]
* :attr:`~django.views.generic.dates.DateMixin.date_field` [:meth:`~django.views.generic.dates.DateMixin.get_date_field`]
* :attr:`~django.views.generic.base.View.http_method_names`
-* :attr:`~django.views.generic.dates.BaseYearArchiveView.make_object_list` [:meth:`~django.views.generic.dates.BaseYearArchiveView.get_make_object_list`]
+* :attr:`~django.views.generic.dates.YearArchiveView.make_object_list` [:meth:`~django.views.generic.dates.YearArchiveView.get_make_object_list`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.model`
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_orphans` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_orphans`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
-* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
+* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix`
* :attr:`~django.views.generic.dates.YearMixin.year` [:meth:`~django.views.generic.dates.YearMixin.get_year`]
@@ -340,13 +347,13 @@ YearArchiveView
* :meth:`~django.views.generic.base.View.as_view`
* :meth:`~django.views.generic.base.View.dispatch`
-* :meth:`~django.views.generic.dates.BaseDateListView.get`
+* ``get()``
* :meth:`~django.views.generic.list.MultipleObjectMixin.get_context_data`
* :meth:`~django.views.generic.dates.BaseDateListView.get_date_list`
* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_items`
* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_queryset`
* :meth:`~django.views.generic.list.MultipleObjectMixin.get_paginator`
-* :meth:`~django.views.generic.base.View.head`
+* ``head()``
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
* :meth:`~django.views.generic.list.MultipleObjectMixin.paginate_queryset`
* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
@@ -359,6 +366,7 @@ MonthArchiveView
* :attr:`~django.views.generic.list.MultipleObjectMixin.allow_empty` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_allow_empty`]
* :attr:`~django.views.generic.dates.DateMixin.allow_future` [:meth:`~django.views.generic.dates.DateMixin.get_allow_future`]
+* :attr:`~django.views.generic.base.TemplateResponseMixin.content_type`
* :attr:`~django.views.generic.list.MultipleObjectMixin.context_object_name` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_context_object_name`]
* :attr:`~django.views.generic.dates.DateMixin.date_field` [:meth:`~django.views.generic.dates.DateMixin.get_date_field`]
* :attr:`~django.views.generic.base.View.http_method_names`
@@ -369,7 +377,7 @@ MonthArchiveView
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_orphans` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_orphans`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
-* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
+* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix`
* :attr:`~django.views.generic.dates.YearMixin.year` [:meth:`~django.views.generic.dates.YearMixin.get_year`]
@@ -379,7 +387,7 @@ MonthArchiveView
* :meth:`~django.views.generic.base.View.as_view`
* :meth:`~django.views.generic.base.View.dispatch`
-* :meth:`~django.views.generic.dates.BaseDateListView.get`
+* ``get()``
* :meth:`~django.views.generic.list.MultipleObjectMixin.get_context_data`
* :meth:`~django.views.generic.dates.BaseDateListView.get_date_list`
* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_items`
@@ -387,7 +395,7 @@ MonthArchiveView
* :meth:`~django.views.generic.dates.MonthMixin.get_next_month`
* :meth:`~django.views.generic.list.MultipleObjectMixin.get_paginator`
* :meth:`~django.views.generic.dates.MonthMixin.get_previous_month`
-* :meth:`~django.views.generic.base.View.head`
+* ``head()``
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
* :meth:`~django.views.generic.list.MultipleObjectMixin.paginate_queryset`
* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
@@ -400,6 +408,7 @@ WeekArchiveView
* :attr:`~django.views.generic.list.MultipleObjectMixin.allow_empty` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_allow_empty`]
* :attr:`~django.views.generic.dates.DateMixin.allow_future` [:meth:`~django.views.generic.dates.DateMixin.get_allow_future`]
+* :attr:`~django.views.generic.base.TemplateResponseMixin.content_type`
* :attr:`~django.views.generic.list.MultipleObjectMixin.context_object_name` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_context_object_name`]
* :attr:`~django.views.generic.dates.DateMixin.date_field` [:meth:`~django.views.generic.dates.DateMixin.get_date_field`]
* :attr:`~django.views.generic.base.View.http_method_names`
@@ -408,7 +417,7 @@ WeekArchiveView
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_orphans` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_orphans`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
-* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
+* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix`
* :attr:`~django.views.generic.dates.WeekMixin.week` [:meth:`~django.views.generic.dates.WeekMixin.get_week`]
@@ -420,13 +429,13 @@ WeekArchiveView
* :meth:`~django.views.generic.base.View.as_view`
* :meth:`~django.views.generic.base.View.dispatch`
-* :meth:`~django.views.generic.dates.BaseDateListView.get`
+* ``get()``
* :meth:`~django.views.generic.list.MultipleObjectMixin.get_context_data`
* :meth:`~django.views.generic.dates.BaseDateListView.get_date_list`
* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_items`
* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_queryset`
* :meth:`~django.views.generic.list.MultipleObjectMixin.get_paginator`
-* :meth:`~django.views.generic.base.View.head`
+* ``head()``
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
* :meth:`~django.views.generic.list.MultipleObjectMixin.paginate_queryset`
* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
@@ -439,6 +448,7 @@ DayArchiveView
* :attr:`~django.views.generic.list.MultipleObjectMixin.allow_empty` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_allow_empty`]
* :attr:`~django.views.generic.dates.DateMixin.allow_future` [:meth:`~django.views.generic.dates.DateMixin.get_allow_future`]
+* :attr:`~django.views.generic.base.TemplateResponseMixin.content_type`
* :attr:`~django.views.generic.list.MultipleObjectMixin.context_object_name` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_context_object_name`]
* :attr:`~django.views.generic.dates.DateMixin.date_field` [:meth:`~django.views.generic.dates.DateMixin.get_date_field`]
* :attr:`~django.views.generic.dates.DayMixin.day` [:meth:`~django.views.generic.dates.DayMixin.get_day`]
@@ -451,7 +461,7 @@ DayArchiveView
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_orphans` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_orphans`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
-* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
+* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix`
* :attr:`~django.views.generic.dates.YearMixin.year` [:meth:`~django.views.generic.dates.YearMixin.get_year`]
@@ -461,7 +471,7 @@ DayArchiveView
* :meth:`~django.views.generic.base.View.as_view`
* :meth:`~django.views.generic.base.View.dispatch`
-* :meth:`~django.views.generic.dates.BaseDateListView.get`
+* ``get()``
* :meth:`~django.views.generic.list.MultipleObjectMixin.get_context_data`
* :meth:`~django.views.generic.dates.BaseDateListView.get_date_list`
* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_items`
@@ -471,7 +481,7 @@ DayArchiveView
* :meth:`~django.views.generic.list.MultipleObjectMixin.get_paginator`
* :meth:`~django.views.generic.dates.DayMixin.get_previous_day`
* :meth:`~django.views.generic.dates.MonthMixin.get_previous_month`
-* :meth:`~django.views.generic.base.View.head`
+* ``head()``
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
* :meth:`~django.views.generic.list.MultipleObjectMixin.paginate_queryset`
* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
@@ -484,6 +494,7 @@ TodayArchiveView
* :attr:`~django.views.generic.list.MultipleObjectMixin.allow_empty` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_allow_empty`]
* :attr:`~django.views.generic.dates.DateMixin.allow_future` [:meth:`~django.views.generic.dates.DateMixin.get_allow_future`]
+* :attr:`~django.views.generic.base.TemplateResponseMixin.content_type`
* :attr:`~django.views.generic.list.MultipleObjectMixin.context_object_name` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_context_object_name`]
* :attr:`~django.views.generic.dates.DateMixin.date_field` [:meth:`~django.views.generic.dates.DateMixin.get_date_field`]
* :attr:`~django.views.generic.dates.DayMixin.day` [:meth:`~django.views.generic.dates.DayMixin.get_day`]
@@ -496,7 +507,7 @@ TodayArchiveView
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_orphans` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_orphans`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
-* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
+* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
* :attr:`~django.views.generic.list.MultipleObjectTemplateResponseMixin.template_name_suffix`
* :attr:`~django.views.generic.dates.YearMixin.year` [:meth:`~django.views.generic.dates.YearMixin.get_year`]
@@ -506,7 +517,7 @@ TodayArchiveView
* :meth:`~django.views.generic.base.View.as_view`
* :meth:`~django.views.generic.base.View.dispatch`
-* :meth:`~django.views.generic.dates.BaseDateListView.get`
+* ``get()``
* :meth:`~django.views.generic.list.MultipleObjectMixin.get_context_data`
* :meth:`~django.views.generic.dates.BaseDateListView.get_date_list`
* :meth:`~django.views.generic.dates.BaseDateListView.get_dated_items`
@@ -516,7 +527,7 @@ TodayArchiveView
* :meth:`~django.views.generic.list.MultipleObjectMixin.get_paginator`
* :meth:`~django.views.generic.dates.DayMixin.get_previous_day`
* :meth:`~django.views.generic.dates.MonthMixin.get_previous_month`
-* :meth:`~django.views.generic.base.View.head`
+* ``head()``
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
* :meth:`~django.views.generic.list.MultipleObjectMixin.paginate_queryset`
* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
@@ -528,6 +539,7 @@ DateDetailView
**Attributes** (with optional accessor):
* :attr:`~django.views.generic.dates.DateMixin.allow_future` [:meth:`~django.views.generic.dates.DateMixin.get_allow_future`]
+* :attr:`~django.views.generic.base.TemplateResponseMixin.content_type`
* :attr:`~django.views.generic.detail.SingleObjectMixin.context_object_name` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_context_object_name`]
* :attr:`~django.views.generic.dates.DateMixin.date_field` [:meth:`~django.views.generic.dates.DateMixin.get_date_field`]
* :attr:`~django.views.generic.dates.DayMixin.day` [:meth:`~django.views.generic.dates.DayMixin.get_day`]
@@ -538,7 +550,7 @@ DateDetailView
* :attr:`~django.views.generic.dates.MonthMixin.month_format` [:meth:`~django.views.generic.dates.MonthMixin.get_month_format`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.pk_url_kwarg`
* :attr:`~django.views.generic.detail.SingleObjectMixin.queryset` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_queryset`]
-* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
+* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_field` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_slug_field`]
* :attr:`~django.views.generic.detail.SingleObjectMixin.slug_url_kwarg`
* :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`]
@@ -551,13 +563,13 @@ DateDetailView
* :meth:`~django.views.generic.base.View.as_view`
* :meth:`~django.views.generic.base.View.dispatch`
-* :meth:`~django.views.generic.detail.BaseDetailView.get`
+* ``get()``
* :meth:`~django.views.generic.detail.SingleObjectMixin.get_context_data`
* :meth:`~django.views.generic.dates.DayMixin.get_next_day`
* :meth:`~django.views.generic.dates.MonthMixin.get_next_month`
* :meth:`~django.views.generic.detail.SingleObjectMixin.get_object`
* :meth:`~django.views.generic.dates.DayMixin.get_previous_day`
* :meth:`~django.views.generic.dates.MonthMixin.get_previous_month`
-* :meth:`~django.views.generic.base.View.head`
+* ``head()``
* :meth:`~django.views.generic.base.View.http_method_not_allowed`
* :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
diff --git a/docs/ref/class-based-views/generic-date-based.txt b/docs/ref/class-based-views/generic-date-based.txt
index 0ae0bcdf42..4144c382f8 100644
--- a/docs/ref/class-based-views/generic-date-based.txt
+++ b/docs/ref/class-based-views/generic-date-based.txt
@@ -42,6 +42,20 @@ ArchiveIndexView
* :class:`django.views.generic.dates.DateMixin`
* :class:`django.views.generic.base.View`
+ **Context**
+
+ In addition to the context provided by
+ :class:`django.views.generic.list.MultipleObjectMixin` (via
+ :class:`django.views.generic.dates.BaseDateListView`), the template's
+ context will be:
+
+ * ``date_list``: A
+ :meth:`DateQuerySet<django.db.models.query.QuerySet.dates>` object
+ containing all years that have objects available according to
+ ``queryset``, represented as
+ :class:`datetime.datetime<python:datetime.datetime>` objects, in
+ descending order.
+
**Notes**
* Uses a default ``context_object_name`` of ``latest``.
@@ -109,7 +123,6 @@ YearArchiveView
Determine if an object list will be returned as part of the context.
Returns :attr:`~YearArchiveView.make_object_list` by default.
-
**Context**
In addition to the context provided by
@@ -118,7 +131,7 @@ YearArchiveView
context will be:
* ``date_list``: A
- :meth:`DateQuerySet<django.db.models.query.QuerySet.dates>` object object
+ :meth:`DateQuerySet<django.db.models.query.QuerySet.dates>` object
containing all months that have objects available according to
``queryset``, represented as
:class:`datetime.datetime<python:datetime.datetime>` objects, in
@@ -580,7 +593,7 @@ DateDetailView
* :class:`django.views.generic.dates.MonthMixin`
* :class:`django.views.generic.dates.DayMixin`
* :class:`django.views.generic.dates.DateMixin`
- * :class:`django.views.generic.detail.BaseDetailView`
+ * ``django.views.generic.detail.BaseDetailView``
* :class:`django.views.generic.detail.SingleObjectMixin`
* :class:`django.views.generic.base.View`
diff --git a/docs/ref/class-based-views/generic-display.txt b/docs/ref/class-based-views/generic-display.txt
index 12603ff0df..b827c0005c 100644
--- a/docs/ref/class-based-views/generic-display.txt
+++ b/docs/ref/class-based-views/generic-display.txt
@@ -19,22 +19,22 @@ DetailView
* :class:`django.views.generic.detail.SingleObjectTemplateResponseMixin`
* :class:`django.views.generic.base.TemplateResponseMixin`
- * :class:`django.views.generic.detail.BaseDetailView`
+ * ``django.views.generic.detail.BaseDetailView``
* :class:`django.views.generic.detail.SingleObjectMixin`
* :class:`django.views.generic.base.View`
**Method Flowchart**
- 1. :meth:`dispatch()`
- 2. :meth:`http_method_not_allowed()`
- 3. :meth:`get_template_names()`
- 4. :meth:`get_slug_field()`
- 5. :meth:`get_queryset()`
- 6. :meth:`get_object()`
- 7. :meth:`get_context_object_name()`
- 8. :meth:`get_context_data()`
- 9. :meth:`get()`
- 10. :meth:`render_to_response()`
+ 1. :meth:`~django.views.generic.base.View.dispatch()`
+ 2. :meth:`~django.views.generic.base.View.http_method_not_allowed()`
+ 3. :meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names()`
+ 4. :meth:`~django.views.generic.detail.SingleObjectMixin.get_slug_field()`
+ 5. :meth:`~django.views.generic.detail.SingleObjectMixin.get_queryset()`
+ 6. :meth:`~django.views.generic.detail.SingleObjectMixin.get_object()`
+ 7. :meth:`~django.views.generic.detail.SingleObjectMixin.get_context_object_name()`
+ 8. :meth:`~django.views.generic.detail.SingleObjectMixin.get_context_data()`
+ 9. ``get()``
+ 10. :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response()`
**Example views.py**::
@@ -86,14 +86,14 @@ ListView
**Method Flowchart**
- 1. :meth:`dispatch()`
- 2. :meth:`http_method_not_allowed()`
- 3. :meth:`get_template_names()`
- 4. :meth:`get_queryset()`
- 5. :meth:`get_objects()`
- 6. :meth:`get_context_data()`
- 7. :meth:`get()`
- 8. :meth:`render_to_response()`
+ 1. :meth:`~django.views.generic.base.View.dispatch()`
+ 2. :meth:`~django.views.generic.base.View.http_method_not_allowed()`
+ 3. :meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names()`
+ 4. :meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset()`
+ 5. :meth:`~django.views.generic.list.MultipleObjectMixin.get_context_object_name()`
+ 6. :meth:`~django.views.generic.list.MultipleObjectMixin.get_context_data()`
+ 7. ``get()``
+ 8. :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response()`
**Example views.py**::
@@ -140,7 +140,7 @@ ListView
.. method:: get(request, *args, **kwargs)
- Adds :attr:`object_list` to the context. If
+ Adds ``object_list`` to the context. If
:attr:`~django.views.generic.list.MultipleObjectMixin.allow_empty`
is True then display an empty list. If
:attr:`~django.views.generic.list.MultipleObjectMixin.allow_empty` is
diff --git a/docs/ref/class-based-views/generic-editing.txt b/docs/ref/class-based-views/generic-editing.txt
index 7ce5c1d1be..1dbb427036 100644
--- a/docs/ref/class-based-views/generic-editing.txt
+++ b/docs/ref/class-based-views/generic-editing.txt
@@ -12,7 +12,7 @@ editing content:
.. note::
- Some of the examples on this page assume that an ``Article`` model has been
+ Some of the examples on this page assume that an ``Author`` model has been
defined as follows in ``myapp/models.py``::
from django.core.urlresolvers import reverse
@@ -38,7 +38,7 @@ FormView
* :class:`django.views.generic.edit.FormView`
* :class:`django.views.generic.base.TemplateResponseMixin`
- * :class:`django.views.generic.edit.BaseFormView`
+ * ``django.views.generic.edit.BaseFormView``
* :class:`django.views.generic.edit.FormMixin`
* :class:`django.views.generic.edit.ProcessFormView`
* :class:`django.views.generic.base.View`
@@ -86,7 +86,7 @@ CreateView
* :class:`django.views.generic.edit.CreateView`
* :class:`django.views.generic.detail.SingleObjectTemplateResponseMixin`
* :class:`django.views.generic.base.TemplateResponseMixin`
- * :class:`django.views.generic.edit.BaseCreateView`
+ * ``django.views.generic.edit.BaseCreateView``
* :class:`django.views.generic.edit.ModelFormMixin`
* :class:`django.views.generic.edit.FormMixin`
* :class:`django.views.generic.detail.SingleObjectMixin`
@@ -97,11 +97,11 @@ CreateView
.. attribute:: template_name_suffix
- The CreateView page displayed to a GET request uses a
- ``template_name_suffix`` of ``'_form.html'``. For
- example, changing this attribute to ``'_create_form.html'`` for a view
- creating objects for the the example `Author` model would cause the the
- default `template_name` to be ``'myapp/author_create_form.html'``.
+ The ``CreateView`` page displayed to a ``GET`` request uses a
+ ``template_name_suffix`` of ``'_form'``. For
+ example, changing this attribute to ``'_create_form'`` for a view
+ creating objects for the example ``Author`` model would cause the
+ default ``template_name`` to be ``'myapp/author_create_form.html'``.
**Example views.py**::
@@ -128,7 +128,7 @@ UpdateView
* :class:`django.views.generic.edit.UpdateView`
* :class:`django.views.generic.detail.SingleObjectTemplateResponseMixin`
* :class:`django.views.generic.base.TemplateResponseMixin`
- * :class:`django.views.generic.edit.BaseUpdateView`
+ * ``django.views.generic.edit.BaseUpdateView``
* :class:`django.views.generic.edit.ModelFormMixin`
* :class:`django.views.generic.edit.FormMixin`
* :class:`django.views.generic.detail.SingleObjectMixin`
@@ -139,11 +139,11 @@ UpdateView
.. attribute:: template_name_suffix
- The UpdateView page displayed to a GET request uses a
- ``template_name_suffix`` of ``'_form.html'``. For
- example, changing this attribute to ``'_update_form.html'`` for a view
- updating objects for the the example `Author` model would cause the the
- default `template_name` to be ``'myapp/author_update_form.html'``.
+ The ``UpdateView`` page displayed to a ``GET`` request uses a
+ ``template_name_suffix`` of ``'_form'``. For
+ example, changing this attribute to ``'_update_form'`` for a view
+ updating objects for the example ``Author`` model would cause the
+ default ``template_name`` to be ``'myapp/author_update_form.html'``.
**Example views.py**::
@@ -170,9 +170,9 @@ DeleteView
* :class:`django.views.generic.edit.DeleteView`
* :class:`django.views.generic.detail.SingleObjectTemplateResponseMixin`
* :class:`django.views.generic.base.TemplateResponseMixin`
- * :class:`django.views.generic.edit.BaseDeleteView`
+ * ``django.views.generic.edit.BaseDeleteView``
* :class:`django.views.generic.edit.DeletionMixin`
- * :class:`django.views.generic.detail.BaseDetailView`
+ * ``django.views.generic.detail.BaseDetailView``
* :class:`django.views.generic.detail.SingleObjectMixin`
* :class:`django.views.generic.base.View`
@@ -180,11 +180,11 @@ DeleteView
.. attribute:: template_name_suffix
- The DeleteView page displayed to a GET request uses a
- ``template_name_suffix`` of ``'_confirm_delete.html'``. For
- example, changing this attribute to ``'_check_delete.html'`` for a view
- deleting objects for the the example `Author` model would cause the the
- default `template_name` to be ``'myapp/author_check_delete.html'``.
+ The ``DeleteView`` page displayed to a ``GET`` request uses a
+ ``template_name_suffix`` of ``'_confirm_delete'``. For
+ example, changing this attribute to ``'_check_delete'`` for a view
+ deleting objects for the example ``Author`` model would cause the
+ default ``template_name`` to be ``'myapp/author_check_delete.html'``.
**Example views.py**::
diff --git a/docs/ref/class-based-views/mixins-date-based.txt b/docs/ref/class-based-views/mixins-date-based.txt
index 561e525e70..75f2a77615 100644
--- a/docs/ref/class-based-views/mixins-date-based.txt
+++ b/docs/ref/class-based-views/mixins-date-based.txt
@@ -35,8 +35,8 @@ YearMixin
Tries the following sources, in order:
* The value of the :attr:`YearMixin.year` attribute.
- * The value of the `year` argument captured in the URL pattern.
- * The value of the `year` GET query argument.
+ * The value of the ``year`` argument captured in the URL pattern.
+ * The value of the ``year`` ``GET`` query argument.
Raises a 404 if no valid year specification can be found.
@@ -87,8 +87,8 @@ MonthMixin
Tries the following sources, in order:
* The value of the :attr:`MonthMixin.month` attribute.
- * The value of the `month` argument captured in the URL pattern.
- * The value of the `month` GET query argument.
+ * The value of the ``month`` argument captured in the URL pattern.
+ * The value of the ``month`` ``GET`` query argument.
Raises a 404 if no valid month specification can be found.
@@ -100,7 +100,7 @@ MonthMixin
:attr:`~BaseDateListView.allow_empty` and
:attr:`~DateMixin.allow_future`.
- .. method:: get_prev_month(date)
+ .. method:: get_previous_month(date)
Returns a date object containing the first day of the month before the
date provided. This function can also return ``None`` or raise an
@@ -139,8 +139,8 @@ DayMixin
Tries the following sources, in order:
* The value of the :attr:`DayMixin.day` attribute.
- * The value of the `day` argument captured in the URL pattern.
- * The value of the `day` GET query argument.
+ * The value of the ``day`` argument captured in the URL pattern.
+ * The value of the ``day`` ``GET`` query argument.
Raises a 404 if no valid day specification can be found.
@@ -152,7 +152,7 @@ DayMixin
:attr:`~BaseDateListView.allow_empty` and
:attr:`~DateMixin.allow_future`.
- .. method:: get_prev_day(date)
+ .. method:: get_previous_day(date)
Returns a date object containing the previous valid day. This function
can also return ``None`` or raise an :class:`~django.http.Http404`
@@ -192,8 +192,8 @@ WeekMixin
Tries the following sources, in order:
* The value of the :attr:`WeekMixin.week` attribute.
- * The value of the `week` argument captured in the URL pattern
- * The value of the `week` GET query argument.
+ * The value of the ``week`` argument captured in the URL pattern
+ * The value of the ``week`` ``GET`` query argument.
Raises a 404 if no valid week specification can be found.
@@ -287,8 +287,9 @@ BaseDateListView
available. If this is ``True`` and no objects are available, the view
will display an empty page instead of raising a 404.
- This is identical to :attr:`MultipleObjectMixin.allow_empty`, except
- for the default value, which is ``False``.
+ This is identical to
+ :attr:`django.views.generic.list.MultipleObjectMixin.allow_empty`,
+ except for the default value, which is ``False``.
.. attribute:: date_list_period
diff --git a/docs/ref/class-based-views/mixins-editing.txt b/docs/ref/class-based-views/mixins-editing.txt
index 95dd24f442..a4175369aa 100644
--- a/docs/ref/class-based-views/mixins-editing.txt
+++ b/docs/ref/class-based-views/mixins-editing.txt
@@ -40,11 +40,6 @@ FormMixin
Retrieve initial data for the form. By default, returns a copy of
:attr:`~django.views.generic.edit.FormMixin.initial`.
- .. versionchanged:: 1.4
- In Django 1.3, this method was returning the
- :attr:`~django.views.generic.edit.FormMixin.initial` class variable
- itself.
-
.. method:: get_form_class()
Retrieve the form class to instantiate. By default
@@ -88,9 +83,8 @@ FormMixin
.. note::
- Views mixing :class:`FormMixin` must provide an implementation of
- :meth:`~django.views.generic.FormMixin.form_valid` and
- :meth:`~django.views.generic.FormMixin.form_invalid`.
+ Views mixing ``FormMixin`` must provide an implementation of
+ :meth:`form_valid` and :meth:`form_invalid`.
ModelFormMixin
@@ -98,15 +92,16 @@ ModelFormMixin
.. class:: django.views.generic.edit.ModelFormMixin
- A form mixin that works on ModelForms, rather than a standalone form.
+ A form mixin that works on ``ModelForms``, rather than a standalone form.
Since this is a subclass of
:class:`~django.views.generic.detail.SingleObjectMixin`, instances of this
- mixin have access to the :attr:`~SingleObjectMixin.model` and
- :attr:`~SingleObjectMixin.queryset` attributes, describing the type of
- object that the ModelForm is manipulating. The view also provides
- ``self.object``, the instance being manipulated. If the instance is being
- created, ``self.object`` will be ``None``.
+ mixin have access to the
+ :attr:`~django.views.generic.detail.SingleObjectMixin.model` and
+ :attr:`~django.views.generic.detail.SingleObjectMixin.queryset` attributes,
+ describing the type of object that the ``ModelForm`` is manipulating. The
+ view also provides ``self.object``, the instance being manipulated. If the
+ instance is being created, ``self.object`` will be ``None``.
**Mixins**
@@ -115,6 +110,12 @@ ModelFormMixin
**Methods and Attributes**
+ .. attribute:: model
+
+ A model class. Can be explicitly provided, otherwise will be determined
+ by examining ``self.object`` or
+ :attr:`~django.views.generic.detail.SingleObjectMixin.queryset`.
+
.. attribute:: success_url
The URL to redirect to when the form is successfully processed.
@@ -127,22 +128,25 @@ ModelFormMixin
.. method:: get_form_class()
Retrieve the form class to instantiate. If
- :attr:`FormMixin.form_class` is provided, that class will be used.
- Otherwise, a ModelForm will be instantiated using the model associated
- with the :attr:`~SingleObjectMixin.queryset`, or with the
- :attr:`~SingleObjectMixin.model`, depending on which attribute is
- provided.
+ :attr:`~django.views.generic.edit.FormMixin.form_class` is provided,
+ that class will be used. Otherwise, a ``ModelForm`` will be
+ instantiated using the model associated with the
+ :attr:`~django.views.generic.detail.SingleObjectMixin.queryset`, or
+ with the :attr:`~django.views.generic.detail.SingleObjectMixin.model`,
+ depending on which attribute is provided.
.. method:: get_form_kwargs()
Add the current instance (``self.object``) to the standard
- :meth:`FormMixin.get_form_kwargs`.
+ :meth:`~django.views.generic.edit.FormMixin.get_form_kwargs`.
.. method:: get_success_url()
Determine the URL to redirect to when the form is successfully
- validated. Returns :attr:`ModelFormMixin.success_url` if it is provided;
- otherwise, attempts to use the ``get_absolute_url()`` of the object.
+ validated. Returns
+ :attr:`django.views.generic.edit.ModelFormMixin.success_url` if it is
+ provided; otherwise, attempts to use the ``get_absolute_url()`` of the
+ object.
.. method:: form_valid(form)
@@ -184,7 +188,10 @@ ProcessFormView
Constructs a form, checks the form for validity, and handles it
accordingly.
- The PUT action is also handled, as an analog of POST.
+ .. method:: put(*args, **kwargs)
+
+ The ``PUT`` action is also handled and just passes all parameters
+ through to :meth:`post`.
.. class:: django.views.generic.edit.DeletionMixin
@@ -197,7 +204,14 @@ ProcessFormView
The url to redirect to when the nominated object has been
successfully deleted.
- .. method:: get_success_url(obj)
+ .. versionadded:: 1.6
+
+ ``success_url`` may contain dictionary string formatting, which
+ will be interpolated against the object's field attributes. For
+ example, you could use ``success_url="/parent/%(parent_id)s/"`` to
+ redirect to a URL composed out of the ``parent_id`` field on a model.
+
+ .. method:: get_success_url()
Returns the url to redirect to when the nominated object has been
successfully deleted. Returns
diff --git a/docs/ref/class-based-views/mixins-multiple-object.txt b/docs/ref/class-based-views/mixins-multiple-object.txt
index c85c962bce..b28bd11a71 100644
--- a/docs/ref/class-based-views/mixins-multiple-object.txt
+++ b/docs/ref/class-based-views/mixins-multiple-object.txt
@@ -61,14 +61,13 @@ MultipleObjectMixin
.. attribute:: queryset
A ``QuerySet`` that represents the objects. If provided, the value of
- :attr:`MultipleObjectMixin.queryset` supersedes the value provided for
- :attr:`MultipleObjectMixin.model`.
+ ``queryset`` supersedes the value provided for :attr:`model`.
.. attribute:: paginate_by
An integer specifying how many objects should be displayed per page. If
this is given, the view will paginate objects with
- :attr:`MultipleObjectMixin.paginate_by` objects per page. The view will
+ ``paginate_by`` objects per page. The view will
expect either a ``page`` query string parameter (via ``request.GET``)
or a ``page`` variable specified in the URLconf.
@@ -77,10 +76,9 @@ MultipleObjectMixin
.. versionadded:: 1.6
An integer specifying the number of "overflow" objects the last page
- can contain. This extends the :attr:`MultipleObjectMixin.paginate_by`
- limit on the last page by up to
- :attr:`MultipleObjectMixin.paginate_orphans`, in order to keep the last
- page from having a very small number of objects.
+ can contain. This extends the :attr:`paginate_by` limit on the last
+ page by up to ``paginate_orphans``, in order to keep the last page from
+ having a very small number of objects.
.. attribute:: page_kwarg
@@ -97,7 +95,7 @@ MultipleObjectMixin
:class:`django.core.paginator.Paginator` is used. If the custom paginator
class doesn't have the same constructor interface as
:class:`django.core.paginator.Paginator`, you will also need to
- provide an implementation for :meth:`MultipleObjectMixin.get_paginator`.
+ provide an implementation for :meth:`get_paginator`.
.. attribute:: context_object_name
@@ -122,20 +120,20 @@ MultipleObjectMixin
Returns the number of items to paginate by, or ``None`` for no
pagination. By default this simply returns the value of
- :attr:`MultipleObjectMixin.paginate_by`.
+ :attr:`paginate_by`.
.. method:: get_paginator(queryset, per_page, orphans=0, allow_empty_first_page=True)
Returns an instance of the paginator to use for this view. By default,
instantiates an instance of :attr:`paginator_class`.
- .. method:: get_paginate_by()
+ .. method:: get_paginate_orphans()
.. versionadded:: 1.6
An integer specifying the number of "overflow" objects the last page
can contain. By default this simply returns the value of
- :attr:`MultipleObjectMixin.paginate_orphans`.
+ :attr:`paginate_orphans`.
.. method:: get_allow_empty()
@@ -149,7 +147,7 @@ MultipleObjectMixin
Return the context variable name that will be used to contain
the list of data that this view is manipulating. If
``object_list`` is a queryset of Django objects and
- :attr:`~MultipleObjectMixin.context_object_name` is not set,
+ :attr:`context_object_name` is not set,
the context name will be the ``object_name`` of the model that
the queryset is composed from, with postfix ``'_list'``
appended. For example, the model ``Article`` would have a
diff --git a/docs/ref/class-based-views/mixins-simple.txt b/docs/ref/class-based-views/mixins-simple.txt
index d2f0df241e..51b0386654 100644
--- a/docs/ref/class-based-views/mixins-simple.txt
+++ b/docs/ref/class-based-views/mixins-simple.txt
@@ -48,7 +48,7 @@ TemplateResponseMixin
.. attribute:: template_name
The full name of a template to use as defined by a string. Not defining
- a template_name will raise a
+ a ``template_name`` will raise a
:class:`django.core.exceptions.ImproperlyConfigured` exception.
.. attribute:: response_class
@@ -64,6 +64,15 @@ TemplateResponseMixin
instantiation, create a ``TemplateResponse`` subclass and assign it to
``response_class``.
+ .. attribute:: content_type
+
+ .. versionadded:: 1.5
+ The ``content_type`` attribute was added.
+
+ The content type to use for the response. ``content_type`` is passed
+ as a keyword argument to ``response_class``. Default is ``None`` --
+ meaning that Django uses :setting:`DEFAULT_CONTENT_TYPE`.
+
**Methods**
.. method:: render_to_response(context, **response_kwargs)
@@ -73,15 +82,13 @@ TemplateResponseMixin
If any keyword arguments are provided, they will be passed to the
constructor of the response class.
- Calls :meth:`~TemplateResponseMixin.get_template_names()` to obtain the
- list of template names that will be searched looking for an existent
- template.
+ Calls :meth:`get_template_names()` to obtain the list of template names
+ that will be searched looking for an existent template.
.. method:: get_template_names()
Returns a list of template names to search for when rendering the
template.
- If :attr:`TemplateResponseMixin.template_name` is specified, the
- default implementation will return a list containing
- :attr:`TemplateResponseMixin.template_name` (if it is specified).
+ If :attr:`template_name` is specified, the default implementation will
+ return a list containing :attr:`template_name` (if it is specified).
diff --git a/docs/ref/class-based-views/mixins-single-object.txt b/docs/ref/class-based-views/mixins-single-object.txt
index 77f52b96c6..bbe930d79e 100644
--- a/docs/ref/class-based-views/mixins-single-object.txt
+++ b/docs/ref/class-based-views/mixins-single-object.txt
@@ -21,8 +21,7 @@ SingleObjectMixin
.. attribute:: queryset
A ``QuerySet`` that represents the objects. If provided, the value of
- :attr:`SingleObjectMixin.queryset` supersedes the value provided for
- :attr:`SingleObjectMixin.model`.
+ ``queryset`` supersedes the value provided for :attr:`model`.
.. attribute:: slug_field
@@ -31,15 +30,11 @@ SingleObjectMixin
.. attribute:: slug_url_kwarg
- .. versionadded:: 1.4
-
The name of the URLConf keyword argument that contains the slug. By
default, ``slug_url_kwarg`` is ``'slug'``.
.. attribute:: pk_url_kwarg
- .. versionadded:: 1.4
-
The name of the URLConf keyword argument that contains the primary key.
By default, ``pk_url_kwarg`` is ``'pk'``.
@@ -51,38 +46,38 @@ SingleObjectMixin
Returns the single object that this view will display. If
``queryset`` is provided, that queryset will be used as the
- source of objects; otherwise,
- :meth:`~SingleObjectMixin.get_queryset` will be used.
- ``get_object()`` looks for a
- :attr:`SingleObjectMixin.pk_url_kwarg` argument in the arguments
- to the view; if this argument is found, this method performs a
- primary-key based lookup using that value. If this argument is not
- found, it looks for a :attr:`SingleObjectMixin.slug_url_kwarg`
- argument, and performs a slug lookup using the
- :attr:`SingleObjectMixin.slug_field`.
+ source of objects; otherwise, :meth:`get_queryset` will be used.
+ ``get_object()`` looks for a :attr:`pk_url_kwarg` argument in the
+ arguments to the view; if this argument is found, this method performs
+ a primary-key based lookup using that value. If this argument is not
+ found, it looks for a :attr:`slug_url_kwarg` argument, and performs a
+ slug lookup using the :attr:`slug_field`.
.. method:: get_queryset()
Returns the queryset that will be used to retrieve the object that
- this view will display. By default,
- :meth:`~SingleObjectMixin.get_queryset` returns the value of the
- :attr:`~SingleObjectMixin.queryset` attribute if it is set, otherwise
- it constructs a :class:`QuerySet` by calling the `all()` method on the
- :attr:`~SingleObjectMixin.model` attribute's default manager.
+ this view will display. By default, :meth:`get_queryset` returns the
+ value of the :attr:`queryset` attribute if it is set, otherwise
+ it constructs a :class:`~django.db.models.query.QuerySet` by calling
+ the ``all()`` method on the :attr:`model` attribute's default manager.
.. method:: get_context_object_name(obj)
Return the context variable name that will be used to contain the
- data that this view is manipulating. If
- :attr:`~SingleObjectMixin.context_object_name` is not set, the context
- name will be constructed from the ``object_name`` of the model that
- the queryset is composed from. For example, the model ``Article``
- would have context object named ``'article'``.
+ data that this view is manipulating. If :attr:`context_object_name` is
+ not set, the context name will be constructed from the ``object_name``
+ of the model that the queryset is composed from. For example, the model
+ ``Article`` would have context object named ``'article'``.
.. method:: get_context_data(**kwargs)
Returns context data for displaying the list of objects.
+ .. method:: get_slug_field()
+
+ Returns the name of a slug field to be used to look up by slug. By
+ default this simply returns the value of :attr:`slug_field`.
+
**Context**
* ``object``: The object that this view is displaying. If
diff --git a/docs/ref/clickjacking.txt b/docs/ref/clickjacking.txt
index b70fe9f90d..ce27148ad3 100644
--- a/docs/ref/clickjacking.txt
+++ b/docs/ref/clickjacking.txt
@@ -10,9 +10,6 @@ against `clickjacking`_. This type of attack occurs when a malicious site
tricks a user into clicking on a concealed element of another site which they
have loaded in a hidden frame or iframe.
-.. versionadded:: 1.4
- The clickjacking middleware and decorators were added.
-
.. _clickjacking: http://en.wikipedia.org/wiki/Clickjacking
An example of clickjacking
@@ -33,10 +30,10 @@ Preventing clickjacking
Modern browsers honor the `X-Frame-Options`_ HTTP header that indicates whether
or not a resource is allowed to load within a frame or iframe. If the response
-contains the header with a value of SAMEORIGIN then the browser will only load
-the resource in a frame if the request originated from the same site. If the
-header is set to DENY then the browser will block the resource from loading in a
-frame no matter which site made the request.
+contains the header with a value of ``SAMEORIGIN`` then the browser will only
+load the resource in a frame if the request originated from the same site. If
+the header is set to ``DENY`` then the browser will block the resource from
+loading in a frame no matter which site made the request.
.. _X-Frame-Options: https://developer.mozilla.org/en/The_X-FRAME-OPTIONS_response_header
@@ -54,7 +51,7 @@ How to use it
Setting X-Frame-Options for all responses
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-To set the same X-Frame-Options value for all responses in your site, add
+To set the same ``X-Frame-Options`` value for all responses in your site, put
``'django.middleware.clickjacking.XFrameOptionsMiddleware'`` to
:setting:`MIDDLEWARE_CLASSES`::
@@ -64,15 +61,19 @@ To set the same X-Frame-Options value for all responses in your site, add
...
)
-By default, the middleware will set the X-Frame-Options header to SAMEORIGIN for
-every outgoing ``HttpResponse``. If you want DENY instead, set the
-:setting:`X_FRAME_OPTIONS` setting::
+.. versionchanged:: 1.6
+ This middleware is enabled in the settings file generated by
+ :djadmin:`startproject`.
+
+By default, the middleware will set the ``X-Frame-Options`` header to
+``SAMEORIGIN`` for every outgoing ``HttpResponse``. If you want ``DENY``
+instead, set the :setting:`X_FRAME_OPTIONS` setting::
X_FRAME_OPTIONS = 'DENY'
When using the middleware there may be some views where you do **not** want the
-X-Frame-Options header set. For those cases, you can use a view decorator that
-tells the middleware not to set the header::
+``X-Frame-Options`` header set. For those cases, you can use a view decorator
+that tells the middleware not to set the header::
from django.http import HttpResponse
from django.views.decorators.clickjacking import xframe_options_exempt
@@ -85,7 +86,7 @@ tells the middleware not to set the header::
Setting X-Frame-Options per view
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-To set the X-Frame-Options header on a per view basis, Django provides these
+To set the ``X-Frame-Options`` header on a per view basis, Django provides these
decorators::
from django.http import HttpResponse
@@ -106,23 +107,23 @@ a decorator overrides the middleware.
Limitations
===========
-The `X-Frame-Options` header will only protect against clickjacking in a modern
-browser. Older browsers will quietly ignore the header and need `other
+The ``X-Frame-Options`` header will only protect against clickjacking in a
+modern browser. Older browsers will quietly ignore the header and need `other
clickjacking prevention techniques`_.
Browsers that support X-Frame-Options
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Internet Explorer 8+
-* Firefox 3.6.9+
-* Opera 10.5+
-* Safari 4+
-* Chrome 4.1+
+* Firefox 3.6.9+
+* Opera 10.5+
+* Safari 4+
+* Chrome 4.1+
See also
~~~~~~~~
-A `complete list`_ of browsers supporting X-Frame-Options.
+A `complete list`_ of browsers supporting ``X-Frame-Options``.
.. _complete list: https://developer.mozilla.org/en/The_X-FRAME-OPTIONS_response_header#Browser_compatibility
.. _other clickjacking prevention techniques: http://en.wikipedia.org/wiki/Clickjacking#Prevention
diff --git a/docs/ref/contrib/admin/actions.txt b/docs/ref/contrib/admin/actions.txt
index d7eef623d5..c79f978850 100644
--- a/docs/ref/contrib/admin/actions.txt
+++ b/docs/ref/contrib/admin/actions.txt
@@ -175,7 +175,7 @@ That's easy enough to do::
make_published.short_description = "Mark selected stories as published"
Notice first that we've moved ``make_published`` into a method and renamed the
-`modeladmin` parameter to `self`, and second that we've now put the string
+``modeladmin`` parameter to ``self``, and second that we've now put the string
``'make_published'`` in ``actions`` instead of a direct function reference. This
tells the :class:`ModelAdmin` to look up the action as a method.
@@ -223,7 +223,7 @@ objects as JSON::
from django.core import serializers
def export_as_json(modeladmin, request, queryset):
- response = HttpResponse(mimetype="text/javascript")
+ response = HttpResponse(content_type="application/json")
serializers.serialize("json", queryset, stream=response)
return response
@@ -356,5 +356,3 @@ Conditionally enabling or disabling actions
if 'delete_selected' in actions:
del actions['delete_selected']
return actions
-
-
diff --git a/docs/ref/contrib/admin/admindocs.txt b/docs/ref/contrib/admin/admindocs.txt
index 4a50856f3d..b3e26eca48 100644
--- a/docs/ref/contrib/admin/admindocs.txt
+++ b/docs/ref/contrib/admin/admindocs.txt
@@ -24,7 +24,7 @@ the following:
* Add :mod:`django.contrib.admindocs` to your :setting:`INSTALLED_APPS`.
* Add ``(r'^admin/doc/', include('django.contrib.admindocs.urls'))`` to
- your :data:`urlpatterns`. Make sure it's included *before* the
+ your ``urlpatterns``. Make sure it's included *before* the
``r'^admin/'`` entry, so that requests to ``/admin/doc/`` don't get
handled by the latter entry.
* Install the docutils Python module (http://docutils.sf.net/).
diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt
index 6f79e97a3c..c567bc1db4 100644
--- a/docs/ref/contrib/admin/index.txt
+++ b/docs/ref/contrib/admin/index.txt
@@ -14,7 +14,13 @@ Django's admin interface.
Overview
========
-There are seven steps in activating the Django admin site:
+The admin is enabled in the default project template used by
+:djadmin:`startproject`.
+
+.. versionchanged:: 1.6
+ In previous versions, the admin wasn't enabled by default.
+
+For reference, here are the requirements:
1. Add ``'django.contrib.admin'`` to your :setting:`INSTALLED_APPS`
setting.
@@ -136,6 +142,13 @@ subclass::
e.g. if all the dates are in one month, it'll show the day-level
drill-down only.
+ .. note::
+
+ ``date_hierarchy`` uses :meth:`QuerySet.datetimes()
+ <django.db.models.query.QuerySet.datetimes>` internally. Please refer
+ to its documentation for some caveats when time zone support is
+ enabled (:setting:`USE_TZ = True <USE_TZ>`).
+
.. attribute:: ModelAdmin.exclude
This attribute, if given, should be a list of field names to exclude from
@@ -170,7 +183,7 @@ subclass::
``fields`` option (for more complex layout needs see the
:attr:`~ModelAdmin.fieldsets` option described in the next section). For
example, you could define a simpler version of the admin form for the
- ``django.contrib.flatpages.FlatPage`` model as follows::
+ :class:`django.contrib.flatpages.models.FlatPage` model as follows::
class FlatPageAdmin(admin.ModelAdmin):
fields = ('url', 'title', 'content')
@@ -180,7 +193,10 @@ subclass::
values defined in :attr:`ModelAdmin.readonly_fields` to be displayed as
read-only.
- .. versionadded:: 1.4
+ The ``fields`` option, unlike :attr:`~ModelAdmin.list_display`, may only
+ contain names of fields on the model or the form specified by
+ :attr:`~ModelAdmin.form`. It may contain callables only if they are listed
+ in :attr:`~ModelAdmin.readonly_fields`.
To display multiple fields on the same line, wrap those fields in their own
tuple. In this example, the ``url`` and ``title`` fields will display on the
@@ -214,8 +230,8 @@ subclass::
a dictionary of information about the fieldset, including a list of fields
to be displayed in it.
- A full example, taken from the :class:`django.contrib.flatpages.FlatPage`
- model::
+ A full example, taken from the
+ :class:`django.contrib.flatpages.models.FlatPage` model::
class FlatPageAdmin(admin.ModelAdmin):
fieldsets = (
@@ -249,10 +265,10 @@ subclass::
'fields': ('first_name', 'last_name', 'address', 'city', 'state'),
}
- Just like with the :attr:`~ModelAdmin.fields` option, to display
- multiple fields on the same line, wrap those fields in their own
- tuple. In this example, the ``first_name`` and ``last_name`` fields
- will display on the same line::
+ As with the :attr:`~ModelAdmin.fields` option, to display multiple
+ fields on the same line, wrap those fields in their own tuple. In this
+ example, the ``first_name`` and ``last_name`` fields will display on
+ the same line::
{
'fields': (('first_name', 'last_name'), 'address', 'city', 'state'),
@@ -261,13 +277,17 @@ subclass::
``fields`` can contain values defined in
:attr:`~ModelAdmin.readonly_fields` to be displayed as read-only.
+ If you add the name of a callable to ``fields``, the same rule applies
+ as with the :attr:`~ModelAdmin.fields` option: the callable must be
+ listed in :attr:`~ModelAdmin.readonly_fields`.
+
* ``classes``
A list containing extra CSS classes to apply to the fieldset.
Example::
{
- 'classes': ['wide', 'extrapretty'],
+ 'classes': ('wide', 'extrapretty'),
}
Two useful classes defined by the default admin site stylesheet are
@@ -359,7 +379,7 @@ subclass::
Note that the key in the dictionary is the actual field class, *not* a
string. The value is another dictionary; these arguments will be passed to
- :meth:`~django.forms.Field.__init__`. See :doc:`/ref/forms/api` for
+ the form field's ``__init__()`` method. See :doc:`/ref/forms/api` for
details.
.. warning::
@@ -451,17 +471,25 @@ subclass::
* If the string given is a method of the model, ``ModelAdmin`` or a
callable, Django will HTML-escape the output by default. If you'd
rather not escape the output of the method, give the method an
- ``allow_tags`` attribute whose value is ``True``.
+ ``allow_tags`` attribute whose value is ``True``. However, to avoid an
+ XSS vulnerability, you should use :func:`~django.utils.html.format_html`
+ to escape user-provided inputs.
Here's a full example model::
+ from django.utils.html import format_html
+
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
color_code = models.CharField(max_length=6)
def colored_name(self):
- return '<span style="color: #%s;">%s %s</span>' % (self.color_code, self.first_name, self.last_name)
+ return format_html('<span style="color: #{0};">{1} {2}</span>',
+ self.color_code,
+ self.first_name,
+ self.last_name)
+
colored_name.allow_tags = True
class PersonAdmin(admin.ModelAdmin):
@@ -502,12 +530,17 @@ subclass::
For example::
+ from django.utils.html import format_html
+
class Person(models.Model):
first_name = models.CharField(max_length=50)
color_code = models.CharField(max_length=6)
def colored_first_name(self):
- return '<span style="color: #%s;">%s</span>' % (self.color_code, self.first_name)
+ return format_html('<span style="color: #{0};">{1}</span>',
+ self.color_code,
+ self.first_name)
+
colored_first_name.allow_tags = True
colored_first_name.admin_order_field = 'first_name'
@@ -517,6 +550,13 @@ subclass::
The above will tell Django to order by the ``first_name`` field when
trying to sort by ``colored_first_name`` in the admin.
+ * .. versionadded:: 1.6
+
+ The field names in ``list_display`` will also appear as CSS classes in
+ the HTML output, in the form of ``column-<field_name>`` on each ``<th>``
+ element. This can be used to set column widths in a CSS file for example.
+
+
.. attribute:: ModelAdmin.list_display_links
Set ``list_display_links`` to control which fields in ``list_display``
@@ -586,9 +626,7 @@ subclass::
class PersonAdmin(UserAdmin):
list_filter = ('company__name',)
- .. versionadded:: 1.4
-
- * a class inheriting from :mod:`django.contrib.admin.SimpleListFilter`,
+ * a class inheriting from ``django.contrib.admin.SimpleListFilter``,
which you need to provide the ``title`` and ``parameter_name``
attributes to and override the ``lookups`` and ``queryset`` methods,
e.g.::
@@ -665,7 +703,7 @@ subclass::
Only show the lookups if there actually is
anyone born in the corresponding decades.
"""
- qs = model_admin.queryset(request)
+ qs = model_admin.get_queryset(request)
if qs.filter(birthday__gte=date(1980, 1, 1),
birthday__lte=date(1989, 12, 31)).exists():
yield ('80s', _('in the eighties'))
@@ -673,11 +711,9 @@ subclass::
birthday__lte=date(1999, 12, 31)).exists():
yield ('90s', _('in the nineties'))
- .. versionadded:: 1.4
-
* a tuple, where the first element is a field name and the second
element is a class inheriting from
- :mod:`django.contrib.admin.FieldListFilter`, for example::
+ ``django.contrib.admin.FieldListFilter``, for example::
from django.contrib.admin import BooleanFieldListFilter
@@ -691,8 +727,6 @@ subclass::
The ``FieldListFilter`` API is considered internal and might be
changed.
- .. versionadded:: 1.4
-
It is possible to specify a custom template for rendering a list filter::
class FilterWithCustomTemplate(SimpleListFilter):
@@ -703,8 +737,6 @@ subclass::
.. attribute:: ModelAdmin.list_max_show_all
- .. versionadded:: 1.4
-
Set ``list_max_show_all`` to control how many items can appear on a "Show
all" admin change list page. The admin will display a "Show all" link on the
change list only if the total result count is less than or equal to this
@@ -738,15 +770,9 @@ subclass::
If this isn't provided, the Django admin will use the model's default
ordering.
- .. versionadded:: 1.4
-
If you need to specify a dynamic order (for example depending on user or
language) you can implement a :meth:`~ModelAdmin.get_ordering` method.
- .. versionchanged:: 1.4
-
- Django honors all elements in the list/tuple; before 1.4, only the first
- was respected.
.. attribute:: ModelAdmin.paginator
@@ -827,25 +853,33 @@ subclass::
added last after all editable fields.
A read-only field can not only display data from a model's field, it can
- also display the output of a a model's method or a method of the
+ also display the output of a model's method or a method of the
``ModelAdmin`` class itself. This is very similar to the way
:attr:`ModelAdmin.list_display` behaves. This provides an easy way to use
the admin interface to provide feedback on the status of the objects being
edited, for example::
+ from django.utils.html import format_html_join
+ from django.utils.safestring import mark_safe
+
class PersonAdmin(ModelAdmin):
readonly_fields = ('address_report',)
def address_report(self, instance):
- return ", ".join(instance.get_full_address()) or \
- "<span class='errors'>I can't determine this address.</span>"
+ # assuming get_full_address() returns a list of strings
+ # for each line of the address and you want to separate each
+ # line by a linebreak
+ return format_html_join(
+ mark_safe('<br/>'),
+ '{0}',
+ ((line,) for line in instance.get_full_address()),
+ ) or "<span class='errors'>I can't determine this address.</span>"
# short_description functions like a model field's verbose_name
address_report.short_description = "Address"
# in this example, we have used HTML tags in the output
address_report.allow_tags = True
-
.. attribute:: ModelAdmin.save_as
Set ``save_as`` to enable a "save as" feature on admin change forms.
@@ -959,10 +993,9 @@ templates used by the :class:`ModelAdmin` views:
.. attribute:: ModelAdmin.delete_selected_confirmation_template
- Path to a custom template, used by the :meth:`delete_selected`
- action method for displaying a confirmation page when deleting one
- or more objects. See the :doc:`actions
- documentation</ref/contrib/admin/actions>`.
+ Path to a custom template, used by the ``delete_selected`` action method
+ for displaying a confirmation page when deleting one or more objects. See
+ the :doc:`actions documentation</ref/contrib/admin/actions>`.
.. attribute:: ModelAdmin.object_history_template
@@ -1017,8 +1050,6 @@ templates used by the :class:`ModelAdmin` views:
.. method:: ModelAdmin.get_ordering(self, request)
- .. versionadded:: 1.4
-
The ``get_ordering`` method takes a``request`` as parameter and
is expected to return a ``list`` or ``tuple`` for ordering similar
to the :attr:`ordering` attribute. For example::
@@ -1033,8 +1064,6 @@ templates used by the :class:`ModelAdmin` views:
.. method:: ModelAdmin.save_related(self, request, form, formsets, change)
- .. versionadded:: 1.4
-
The ``save_related`` method is given the ``HttpRequest``, the parent
``ModelForm`` instance, the list of inline formsets and a boolean value
based on whether the parent is being added or changed. Here you can do any
@@ -1050,8 +1079,6 @@ templates used by the :class:`ModelAdmin` views:
.. method:: ModelAdmin.get_prepopulated_fields(self, request, obj=None)
- .. versionadded:: 1.4
-
The ``get_prepopulated_fields`` method is given the ``HttpRequest`` and the
``obj`` being edited (or ``None`` on an add form) and is expected to return
a ``dictionary``, as described above in the :attr:`ModelAdmin.prepopulated_fields`
@@ -1059,8 +1086,6 @@ templates used by the :class:`ModelAdmin` views:
.. method:: ModelAdmin.get_list_display(self, request)
- .. versionadded:: 1.4
-
The ``get_list_display`` method is given the ``HttpRequest`` and is
expected to return a ``list`` or ``tuple`` of field names that will be
displayed on the changelist view as described above in the
@@ -1068,14 +1093,19 @@ templates used by the :class:`ModelAdmin` views:
.. method:: ModelAdmin.get_list_display_links(self, request, list_display)
- .. versionadded:: 1.4
-
The ``get_list_display_links`` method is given the ``HttpRequest`` and
the ``list`` or ``tuple`` returned by :meth:`ModelAdmin.get_list_display`.
It is expected to return a ``list`` or ``tuple`` of field names on the
changelist that will be linked to the change view, as described in the
:attr:`ModelAdmin.list_display_links` section.
+.. method:: ModelAdmin.get_fieldsets(self, request, obj=None)
+
+ The ``get_fieldsets`` method is given the ``HttpRequest`` and the ``obj``
+ being edited (or ``None`` on an add form) and is expected to return a list
+ of two-tuples, in which each two-tuple represents a ``<fieldset>`` on the
+ admin form page, as described above in the :attr:`ModelAdmin.fieldsets` section.
+
.. method:: ModelAdmin.get_list_filter(self, request)
.. versionadded:: 1.5
@@ -1134,9 +1164,8 @@ templates used by the :class:`ModelAdmin` views:
Since this is usually not what you want, Django provides a convenience
wrapper to check permissions and mark the view as non-cacheable. This
- wrapper is :meth:`AdminSite.admin_view` (i.e.
- ``self.admin_site.admin_view`` inside a ``ModelAdmin`` instance); use it
- like so::
+ wrapper is ``AdminSite.admin_view()`` (i.e. ``self.admin_site.admin_view``
+ inside a ``ModelAdmin`` instance); use it like so::
class MyModelAdmin(admin.ModelAdmin):
def get_urls(self):
@@ -1156,7 +1185,7 @@ templates used by the :class:`ModelAdmin` views:
If the page is cacheable, but you still want the permission check to be
performed, you can pass a ``cacheable=True`` argument to
- :meth:`AdminSite.admin_view`::
+ ``AdminSite.admin_view()``::
(r'^my_view/$', self.admin_site.admin_view(self.my_view, cacheable=True))
@@ -1297,20 +1326,23 @@ templates used by the :class:`ModelAdmin` views:
be interpreted as meaning that the current user is not permitted to delete
any object of this type).
-.. method:: ModelAdmin.queryset(self, request)
+.. method:: ModelAdmin.get_queryset(self, request)
- The ``queryset`` method on a ``ModelAdmin`` returns a
+ The ``get_queryset`` method on a ``ModelAdmin`` returns a
:class:`~django.db.models.query.QuerySet` of all model instances that
can be edited by the admin site. One use case for overriding this method
is to show objects owned by the logged-in user::
class MyModelAdmin(admin.ModelAdmin):
- def queryset(self, request):
- qs = super(MyModelAdmin, self).queryset(request)
+ def get_queryset(self, request):
+ qs = super(MyModelAdmin, self).get_queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(author=request.user)
+ .. versionchanged:: 1.6
+ The ``get_queryset`` method was previously named ``queryset``.
+
.. method:: ModelAdmin.message_user(request, message, level=messages.INFO, extra_tags='', fail_silently=False)
Sends a message to the user using the :mod:`django.contrib.messages`
@@ -1341,10 +1373,6 @@ Other methods
Django view for the model instance edition page. See note below.
- .. versionchanged:: 1.4
-
- The ``form_url`` parameter was added.
-
.. method:: ModelAdmin.changelist_view(self, request, extra_context=None)
Django view for the model instances change list/actions page. See note
@@ -1386,12 +1414,10 @@ provided some extra mapping data that would not otherwise be available::
return super(MyModelAdmin, self).change_view(request, object_id,
form_url, extra_context=extra_context)
-.. versionadded:: 1.4
-
-These views now return :class:`~django.template.response.TemplateResponse`
+These views return :class:`~django.template.response.TemplateResponse`
instances which allow you to easily customize the response data before
-rendering. For more details, see the
-:doc:`TemplateResponse documentation </ref/template-response>`.
+rendering. For more details, see the :doc:`TemplateResponse documentation
+</ref/template-response>`.
.. _modeladmin-media-definitions:
@@ -1414,15 +1440,33 @@ The :doc:`staticfiles app </ref/contrib/staticfiles>` prepends
``None``) to any media paths. The same rules apply as :ref:`regular media
definitions on forms <form-media-paths>`.
-Django admin Javascript makes use of the `jQuery`_ library. To avoid
-conflicts with user-supplied scripts or libraries, Django's jQuery is
-namespaced as ``django.jQuery``. If you want to use jQuery in your own admin
-JavaScript without including a second copy, you can use the ``django.jQuery``
-object on changelist and add/edit views.
+jQuery
+~~~~~~
+
+Django admin Javascript makes use of the `jQuery`_ library.
+
+To avoid conflicts with user-supplied scripts or libraries, Django's jQuery
+(version 1.9.1) is namespaced as ``django.jQuery``. If you want to use jQuery
+in your own admin JavaScript without including a second copy, you can use the
+``django.jQuery`` object on changelist and add/edit views.
+
+.. versionchanged:: 1.6
+ The embedded jQuery has been upgraded from 1.4.2 to 1.9.1.
+
+The :class:`ModelAdmin` class requires jQuery by default, so there is no need
+to add jQuery to your ``ModelAdmin``'s list of media resources unless you have
+a specifc need. For example, if you require the jQuery library to be in the
+global namespace (for example when using third-party jQuery plugins) or if you
+need a newer version of jQuery, you will have to include your own copy.
-If you require the jQuery library to be in the global namespace, for example
-when using third-party jQuery plugins, or need a newer version of jQuery, you
-will have to include your own copy of jQuery.
+Django provides both uncompressed and 'minified' versions of jQuery, as
+``jquery.js`` and ``jquery.min.js`` respectively.
+
+:class:`ModelAdmin` and :class:`InlineModelAdmin` have a ``media`` property
+that returns a list of ``Media`` objects which store paths to the JavaScript
+files for the forms and/or formsets. If :setting:`DEBUG` is ``True`` it will
+return the uncompressed versions of the various JavaScript files, including
+``jquery.js``; if not, it will return the 'minified' versions.
.. _jQuery: http://jquery.com
@@ -1508,15 +1552,12 @@ adds some of its own (the shared features are actually defined in the
- :attr:`~ModelAdmin.filter_vertical`
- :attr:`~ModelAdmin.ordering`
- :attr:`~ModelAdmin.prepopulated_fields`
-- :meth:`~ModelAdmin.queryset`
+- :meth:`~ModelAdmin.get_queryset`
- :attr:`~ModelAdmin.radio_fields`
- :attr:`~ModelAdmin.readonly_fields`
- :attr:`~InlineModelAdmin.raw_id_fields`
- :meth:`~ModelAdmin.formfield_for_foreignkey`
- :meth:`~ModelAdmin.formfield_for_manytomany`
-
-.. versionadded:: 1.4
-
- :meth:`~ModelAdmin.has_add_permission`
- :meth:`~ModelAdmin.has_change_permission`
- :meth:`~ModelAdmin.has_delete_permission`
@@ -1542,8 +1583,8 @@ The ``InlineModelAdmin`` class adds:
.. attribute:: InlineModelAdmin.form
The value for ``form`` defaults to ``ModelForm``. This is what is passed
- through to ``inlineformset_factory`` when creating the formset for this
- inline.
+ through to :func:`~django.forms.models.inlineformset_factory` when
+ creating the formset for this inline.
.. attribute:: InlineModelAdmin.extra
@@ -1825,31 +1866,32 @@ Because of the modular design of the admin templates, it is usually neither
necessary nor advisable to replace an entire template. It is almost always
better to override only the section of the template which you need to change.
-To continue the example above, we want to add a new link next to the ``History``
-tool for the ``Page`` model. After looking at ``change_form.html`` we determine
-that we only need to override the ``object-tools`` block. Therefore here is our
-new ``change_form.html`` :
+To continue the example above, we want to add a new link next to the
+``History`` tool for the ``Page`` model. After looking at ``change_form.html``
+we determine that we only need to override the ``object-tools-items`` block.
+Therefore here is our new ``change_form.html`` :
.. code-block:: html+django
{% extends "admin/change_form.html" %}
- {% load i18n %}
- {% block object-tools %}
- {% if change %}{% if not is_popup %}
- <ul class="object-tools">
- <li><a href="history/" class="historylink">{% trans "History" %}</a></li>
- <li><a href="mylink/" class="historylink">My Link</a></li>
+ {% load i18n admin_urls %}
+ {% block object-tools-items %}
+ <li>
+ <a href="{% url opts|admin_urlname:'history' original.pk|admin_urlquote %}" class="historylink">{% trans "History" %}</a>
+ </li>
+ <li>
+ <a href="mylink/" class="historylink">My Link</a>
+ </li>
{% if has_absolute_url %}
- <li><a href="../../../r/{{ content_type_id }}/{{ object_id }}/" class="viewsitelink">
- {% trans "View on site" %}</a>
+ <li>
+ <a href="{% url 'admin:view_on_site' content_type_id original.pk %}" class="viewsitelink">{% trans "View on site" %}</a>
</li>
{% endif%}
- </ul>
- {% endif %}{% endif %}
{% endblock %}
And that's it! If we placed this file in the ``templates/admin/my_app``
-directory, our link would appear on every model's change form.
+directory, our link would appear on the change form for all models within
+my_app.
Templates which may be overridden per app or model
--------------------------------------------------
@@ -1870,7 +1912,7 @@ and 500 pages.
.. note::
- Some of the admin templates, such as ``change_list_request.html`` are used
+ Some of the admin templates, such as ``change_list_results.html`` are used
to render custom inclusion tags. These may be overridden, but in such cases
you are probably better off creating your own version of the tag in
question and giving it a different name. That way you can use it
@@ -1952,7 +1994,7 @@ In this example, we register the default ``AdminSite`` instance
``django.contrib.admin.site`` at the URL ``/admin/`` ::
# urls.py
- from django.conf.urls import patterns, url, include
+ from django.conf.urls import patterns, include
from django.contrib import admin
admin.autodiscover()
@@ -1968,7 +2010,7 @@ In this example, we register the ``AdminSite`` instance
``myproject.admin.admin_site`` at the URL ``/myadmin/`` ::
# urls.py
- from django.conf.urls import patterns, url, include
+ from django.conf.urls import patterns, include
from myproject.admin import admin_site
urlpatterns = patterns('',
@@ -1992,7 +2034,7 @@ separate versions of the admin site -- using the ``AdminSite`` instances
respectively::
# urls.py
- from django.conf.urls import patterns, url, include
+ from django.conf.urls import patterns, include
from myproject.admin import basic_site, advanced_site
urlpatterns = patterns('',
@@ -2043,8 +2085,6 @@ your URLconf. Specifically, add these four patterns:
the URLs starting with ``^admin/`` before the line that includes the admin app
itself).
-.. versionchanged:: 1.4
-
The presence of the ``admin_password_reset`` named URL will cause a "forgotten
your password?" link to appear on the default admin log-in page under the
password box.
@@ -2108,8 +2148,6 @@ if you specifically wanted the admin view from the admin instance named
For more details, see the documentation on :ref:`reversing namespaced URLs
<topics-http-reversing-url-namespaces>`.
-.. versionadded:: 1.4
-
To allow easier reversing of the admin urls in templates, Django provides an
``admin_urlname`` filter which takes an action as argument:
@@ -2121,5 +2159,5 @@ To allow easier reversing of the admin urls in templates, Django provides an
The action in the examples above match the last part of the URL names for
:class:`ModelAdmin` instances described above. The ``opts`` variable can be any
-object which has an ``app_label`` and ``module_name`` and is usually supplied
-by the admin views for the current model.
+object which has an ``app_label`` and ``model_name`` attributes and is usually
+supplied by the admin views for the current model.
diff --git a/docs/ref/contrib/auth.txt b/docs/ref/contrib/auth.txt
index 619b38e5ac..40b3629f63 100644
--- a/docs/ref/contrib/auth.txt
+++ b/docs/ref/contrib/auth.txt
@@ -1,4 +1,435 @@
``django.contrib.auth``
=======================
-See :doc:`/topics/auth`.
+This document provides API reference material for the components of Django's
+authentication system. For more details on the usage of these components or
+how to customize authentication and authorization see the :doc:`authentication
+topic guide </topics/auth/index>`.
+
+.. currentmodule:: django.contrib.auth
+
+User
+====
+
+Fields
+------
+
+.. class:: models.User
+
+ :class:`~django.contrib.auth.models.User` objects have the following
+ fields:
+
+ .. attribute:: username
+
+ Required. 30 characters or fewer. Usernames may contain alphanumeric,
+ ``_``, ``@``, ``+``, ``.`` and ``-`` characters.
+
+ .. attribute:: first_name
+
+ Optional. 30 characters or fewer.
+
+ .. attribute:: last_name
+
+ Optional. 30 characters or fewer.
+
+ .. attribute:: email
+
+ Optional. Email address.
+
+ .. attribute:: password
+
+ Required. A hash of, and metadata about, the password. (Django doesn't
+ store the raw password.) Raw passwords can be arbitrarily long and can
+ contain any character. See the :doc:`password documentation
+ </topics/auth/passwords>`.
+
+ .. attribute:: groups
+
+ Many-to-many relationship to :class:`~django.contrib.auth.models.Group`
+
+ .. attribute:: user_permissions
+
+ Many-to-many relationship to :class:`~django.contrib.auth.models.Permission`
+
+ .. attribute:: is_staff
+
+ Boolean. Designates whether this user can access the admin site.
+
+ .. attribute:: is_active
+
+ Boolean. Designates whether this user account should be considered
+ active. We recommend that you set this flag to ``False`` instead of
+ deleting accounts; that way, if your applications have any foreign keys
+ to users, the foreign keys won't break.
+
+ 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, 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:`~django.contrib.auth.models.User.has_perm` and the
+ authentication in the Django admin. All of those functions/methods will
+ return ``False`` for inactive users.
+
+ .. attribute:: is_superuser
+
+ Boolean. Designates that this user has all permissions without
+ explicitly assigning them.
+
+ .. attribute:: last_login
+
+ A datetime of the user's last login. Is set to the current date/time by
+ default.
+
+ .. attribute:: date_joined
+
+ A datetime designating when the account was created. Is set to the
+ current date/time by default when the account is created.
+
+Methods
+-------
+
+.. class:: models.User
+
+ .. method:: get_username()
+
+ Returns the username for the user. Since the User model can be swapped
+ out, you should use this method instead of referencing the username
+ attribute directly.
+
+ .. method:: is_anonymous()
+
+ Always returns ``False``. This is a way of differentiating
+ :class:`~django.contrib.auth.models.User` and
+ :class:`~django.contrib.auth.models.AnonymousUser` objects.
+ Generally, you should prefer using
+ :meth:`~django.contrib.auth.models.User.is_authenticated()` to this
+ method.
+
+ .. method:: is_authenticated()
+
+ Always returns ``True`` (as opposed to
+ ``AnonymousUser.is_authenticated()`` which always returns ``False``).
+ This is a way to tell if the user has been authenticated. This does not
+ imply any permissions, and doesn't check if the user is active - it
+ only indicates that ``request.user`` has been populated by the
+ :class:`~django.contrib.auth.middleware.AuthenticationMiddleware` with
+ a :class:`~django.contrib.auth.models.User` object representing the
+ currently logged-in user.
+
+ .. method:: get_full_name()
+
+ Returns the :attr:`~django.contrib.auth.models.User.first_name` plus
+ the :attr:`~django.contrib.auth.models.User.last_name`, with a space in
+ between.
+
+ .. method:: set_password(raw_password)
+
+ Sets the user's password to the given raw string, taking care of the
+ password hashing. Doesn't save the
+ :class:`~django.contrib.auth.models.User` object.
+
+ .. method:: check_password(raw_password)
+
+ Returns ``True`` if the given raw string is the correct password for
+ the user. (This takes care of the password hashing in making the
+ comparison.)
+
+ .. method:: set_unusable_password()
+
+ Marks the user as having no password set. This isn't the same as
+ having a blank string for a password.
+ :meth:`~django.contrib.auth.models.User.check_password()` for this user
+ will never return ``True``. Doesn't save the
+ :class:`~django.contrib.auth.models.User` object.
+
+ You may need this if authentication for your application takes place
+ against an existing external source such as an LDAP directory.
+
+ .. method:: has_usable_password()
+
+ Returns ``False`` if
+ :meth:`~django.contrib.auth.models.User.set_unusable_password()` has
+ been called for this user.
+
+ .. method:: get_group_permissions(obj=None)
+
+ Returns a set of permission strings that the user has, through his/her
+ groups.
+
+ If ``obj`` is passed in, only returns the group permissions for
+ this specific object.
+
+ .. method:: get_all_permissions(obj=None)
+
+ Returns a set of permission strings that the user has, both through
+ group and user permissions.
+
+ If ``obj`` is passed in, only returns the permissions for this
+ specific object.
+
+ .. method:: has_perm(perm, obj=None)
+
+ Returns ``True`` if the user has the specified permission, where perm
+ is in the format ``"<app label>.<permission codename>"``. (see
+ documentation on :ref:`permissions <topic-authorization>`). If the user is
+ inactive, this method will always return ``False``.
+
+ If ``obj`` is passed in, this method won't check for a permission for
+ the model, but for this specific object.
+
+ .. method:: has_perms(perm_list, obj=None)
+
+ Returns ``True`` if the user has each of the specified permissions,
+ where each perm is in the format
+ ``"<app label>.<permission codename>"``. If the user is inactive,
+ this method will always return ``False``.
+
+ If ``obj`` is passed in, this method won't check for permissions for
+ the model, but for the specific object.
+
+ .. method:: has_module_perms(package_name)
+
+ Returns ``True`` if the user has any permissions in the given package
+ (the Django app label). If the user is inactive, this method will
+ always return ``False``.
+
+ .. method:: email_user(subject, message, from_email=None)
+
+ Sends an email to the user. If ``from_email`` is ``None``, Django uses
+ the :setting:`DEFAULT_FROM_EMAIL`.
+
+ .. method:: get_profile()
+
+ .. deprecated:: 1.5
+ With the introduction of :ref:`custom User models <auth-custom-user>`,
+ the use of :setting:`AUTH_PROFILE_MODULE` to define a single profile
+ model is no longer supported. See the
+ :doc:`Django 1.5 release notes</releases/1.5>` for more information.
+
+ Returns a site-specific profile for this user. Raises
+ ``django.contrib.auth.models.SiteProfileNotAvailable`` if the
+ current site doesn't allow profiles, or
+ :exc:`django.core.exceptions.ObjectDoesNotExist` if the user does not
+ have a profile.
+
+Manager methods
+---------------
+
+.. class:: models.UserManager
+
+ The :class:`~django.contrib.auth.models.User` model has a custom manager
+ that has the following helper methods (in addition to the methods provided
+ by :class:`~django.contrib.auth.models.BaseUserManager`):
+
+ .. method:: create_user(username, email=None, password=None, **extra_fields)
+
+ Creates, saves and returns a :class:`~django.contrib.auth.models.User`.
+
+ The :attr:`~django.contrib.auth.models.User.username` and
+ :attr:`~django.contrib.auth.models.User.password` are set as given. The
+ domain portion of :attr:`~django.contrib.auth.models.User.email` is
+ automatically converted to lowercase, and the returned
+ :class:`~django.contrib.auth.models.User` object will have
+ :attr:`~django.contrib.auth.models.User.is_active` set to ``True``.
+
+ If no password is provided,
+ :meth:`~django.contrib.auth.models.User.set_unusable_password()` will
+ be called.
+
+ The ``extra_fields`` keyword arguments are passed through to the
+ :class:`~django.contrib.auth.models.User`'s ``__init__`` method to
+ allow setting arbitrary fields on a :ref:`custom User model
+ <auth-custom-user>`.
+
+ See :ref:`Creating users <topics-auth-creating-users>` for example usage.
+
+ .. method:: create_superuser(self, username, email, password, **extra_fields)
+
+ Same as :meth:`create_user`, but sets :attr:`~models.User.is_staff` and
+ :attr:`~models.User.is_superuser` to ``True``.
+
+
+Anonymous users
+===============
+
+.. class:: models.AnonymousUser
+
+ :class:`django.contrib.auth.models.AnonymousUser` is a class that
+ implements the :class:`django.contrib.auth.models.User` interface, with
+ these differences:
+
+ * :ref:`id <automatic-primary-key-fields>` is always ``None``.
+ * :attr:`~django.contrib.auth.models.User.is_staff` and
+ :attr:`~django.contrib.auth.models.User.is_superuser` are always
+ ``False``.
+ * :attr:`~django.contrib.auth.models.User.is_active` is always ``False``.
+ * :attr:`~django.contrib.auth.models.User.groups` and
+ :attr:`~django.contrib.auth.models.User.user_permissions` are always
+ empty.
+ * :meth:`~django.contrib.auth.models.User.is_anonymous()` returns ``True``
+ instead of ``False``.
+ * :meth:`~django.contrib.auth.models.User.is_authenticated()` returns
+ ``False`` instead of ``True``.
+ * :meth:`~django.contrib.auth.models.User.set_password()`,
+ :meth:`~django.contrib.auth.models.User.check_password()`,
+ :meth:`~django.db.models.Model.save` and
+ :meth:`~django.db.models.Model.delete()` raise
+ :exc:`~exceptions.NotImplementedError`.
+
+In practice, you probably won't need to use
+:class:`~django.contrib.auth.models.AnonymousUser` objects on your own, but
+they're used by Web requests, as explained in the next section.
+
+Permission
+==========
+
+.. class:: models.Permission
+
+Fields
+------
+
+:class:`~django.contrib.auth.models.Permission` objects have the following
+fields:
+
+.. attribute:: name
+
+ Required. 50 characters or fewer. Example: ``'Can vote'``.
+
+.. attribute:: content_type
+
+ Required. A reference to the ``django_content_type`` database table, which
+ contains a record for each installed Django model.
+
+.. attribute:: codename
+
+ Required. 100 characters or fewer. Example: ``'can_vote'``.
+
+Methods
+-------
+
+:class:`~django.contrib.auth.models.Permission` objects have the standard
+data-access methods like any other :doc:`Django model </ref/models/instances>`.
+
+Group
+=====
+
+.. class:: models.Group
+
+Fields
+------
+
+:class:`~django.contrib.auth.models.Group` objects have the following fields:
+
+.. attribute:: name
+
+ Required. 80 characters or fewer. Any characters are permitted. Example:
+ ``'Awesome Users'``.
+
+.. attribute:: permissions
+
+ Many-to-many field to :class:`~django.contrib.auth.models.Permission`::
+
+ group.permissions = [permission_list]
+ group.permissions.add(permission, permission, ...)
+ group.permissions.remove(permission, permission, ...)
+ group.permissions.clear()
+
+.. _topics-auth-signals:
+
+Login and logout signals
+========================
+
+.. module:: django.contrib.auth.signals
+
+The auth framework uses the following :doc:`signals </topics/signals>` that
+can be used for notification when a user logs in or out.
+
+.. function:: user_logged_in
+
+ Sent when a user logs in successfully.
+
+ Arguments sent with this signal:
+
+ ``sender``
+ The class of the user that just logged in.
+
+ ``request``
+ The current :class:`~django.http.HttpRequest` instance.
+
+ ``user``
+ The user instance that just logged in.
+
+.. function:: user_logged_out
+
+ Sent when the logout method is called.
+
+ ``sender``
+ As above: the class of the user that just logged out or ``None``
+ if the user was not authenticated.
+
+ ``request``
+ The current :class:`~django.http.HttpRequest` instance.
+
+ ``user``
+ The user instance that just logged out or ``None`` if the
+ user was not authenticated.
+
+.. function:: user_login_failed
+
+ .. versionadded:: 1.5
+
+ Sent when the user failed to login successfully
+
+ ``sender``
+ The name of the module used for authentication.
+
+ ``credentials``
+ A dictionary of keyword arguments containing the user credentials that were
+ passed to :func:`~django.contrib.auth.authenticate()` or your own custom
+ authentication backend. Credentials matching a set of 'sensitive' patterns,
+ (including password) will not be sent in the clear as part of the signal.
+
+.. _authentication-backends-reference:
+
+Authentication backends
+=======================
+
+.. module:: django.contrib.auth.backends
+ :synopsis: Django's built-in authentication backend classes.
+
+This section details the authentication backends that come with Django. For
+information on how to use them and how to write your own authentication
+backends, see the :ref:`Other authentication sources section
+<authentication-backends>` of the :doc:`User authentication guide
+</topics/auth/index>`.
+
+
+Available authentication backends
+---------------------------------
+
+The following backends are available in :mod:`django.contrib.auth.backends`:
+
+.. class:: ModelBackend
+
+ This is the default authentication backend used by Django. It
+ authenticates using credentials consisting of a user identifier and
+ password. For Django's default user model, the user identifier is the
+ username, for custom user models it is the field specified by
+ USERNAME_FIELD (see :doc:`Customizing Users and authentication
+ </topics/auth/customizing>`).
+
+ It also handles the default permissions model as defined for
+ :class:`~django.contrib.auth.models.User` and
+ :class:`~django.contrib.auth.models.PermissionsMixin`.
+
+.. class:: RemoteUserBackend
+
+ Use this backend to take advantage of external-to-Django-handled
+ authentication. It authenticates using usernames passed in
+ :attr:`request.META['REMOTE_USER'] <django.http.HttpRequest.META>`. See
+ the :doc:`Authenticating against REMOTE_USER </howto/auth-remote-user>`
+ documentation.
diff --git a/docs/ref/contrib/comments/custom.txt b/docs/ref/contrib/comments/custom.txt
index 0ef37a9a0b..fd70a6a224 100644
--- a/docs/ref/contrib/comments/custom.txt
+++ b/docs/ref/contrib/comments/custom.txt
@@ -4,6 +4,18 @@ Customizing the comments framework
.. currentmodule:: django.contrib.comments
+.. warning::
+
+ Django's comment framework has been deprecated and is no longer supported.
+ Most users will be better served with a custom solution, or a hosted
+ product like Disqus__.
+
+ The code formerly known as ``django.contrib.comments`` is `still available
+ in an external repository`__.
+
+ __ https://disqus.com/
+ __ https://github.com/django/django-contrib-comments
+
If the built-in comment framework doesn't quite fit your needs, you can extend
the comment app's behavior to add custom data and logic. The comments framework
lets you extend the built-in comment model, the built-in comment form, and the
@@ -66,15 +78,17 @@ In the ``models.py`` we'll define a ``CommentWithTitle`` model::
class CommentWithTitle(Comment):
title = models.CharField(max_length=300)
-Most custom comment models will subclass the :class:`Comment` model. However,
+Most custom comment models will subclass the
+:class:`~django.contrib.comments.models.Comment` model. However,
if you want to substantially remove or change the fields available in the
-:class:`Comment` model, but don't want to rewrite the templates, you could
-try subclassing from :class:`BaseCommentAbstractModel`.
+:class:`~django.contrib.comments.models.Comment` model, but don't want to
+rewrite the templates, you could try subclassing from
+``BaseCommentAbstractModel``.
Next, we'll define a custom comment form in ``forms.py``. This is a little more
tricky: we have to both create a form and override
-:meth:`CommentForm.get_comment_model` and
-:meth:`CommentForm.get_comment_create_data` to return deal with our custom title
+``CommentForm.get_comment_model()`` and
+``CommentForm.get_comment_create_data()`` to return deal with our custom title
field::
from django import forms
@@ -139,7 +153,7 @@ however.
Return the :class:`~django.db.models.Model` class to use for comments. This
model should inherit from
- :class:`django.contrib.comments.models.BaseCommentAbstractModel`, which
+ ``django.contrib.comments.models.BaseCommentAbstractModel``, which
defines necessary core fields.
The default implementation returns
@@ -170,33 +184,33 @@ however.
attribute when rendering your comment form.
The default implementation returns a reverse-resolved URL pointing
- to the :func:`post_comment` view.
+ to the ``post_comment()`` view.
.. note::
If you provide a custom comment model and/or form, but you
- want to use the default :func:`post_comment` view, you will
+ want to use the default ``post_comment()`` view, you will
need to be aware that it requires the model and form to have
certain additional attributes and methods: see the
- :func:`post_comment` view documentation for details.
+ ``django.contrib.comments.views.post_comment()`` view for details.
.. function:: get_flag_url()
Return the URL for the "flag this comment" view.
The default implementation returns a reverse-resolved URL pointing
- to the :func:`django.contrib.comments.views.moderation.flag` view.
+ to the ``django.contrib.comments.views.moderation.flag()`` view.
.. function:: get_delete_url()
Return the URL for the "delete this comment" view.
The default implementation returns a reverse-resolved URL pointing
- to the :func:`django.contrib.comments.views.moderation.delete` view.
+ to the ``django.contrib.comments.views.moderation.delete()`` view.
.. function:: get_approve_url()
Return the URL for the "approve this comment from moderation" view.
The default implementation returns a reverse-resolved URL pointing
- to the :func:`django.contrib.comments.views.moderation.approve` view.
+ to the ``django.contrib.comments.views.moderation.approve()`` view.
diff --git a/docs/ref/contrib/comments/example.txt b/docs/ref/contrib/comments/example.txt
index 2bff778c2f..abf79c5f14 100644
--- a/docs/ref/contrib/comments/example.txt
+++ b/docs/ref/contrib/comments/example.txt
@@ -4,6 +4,18 @@
Example of using the built-in comments app
===========================================
+.. warning::
+
+ Django's comment framework has been deprecated and is no longer supported.
+ Most users will be better served with a custom solution, or a hosted
+ product like Disqus__.
+
+ The code formerly known as ``django.contrib.comments`` is `still available
+ in an external repository`__.
+
+ __ https://disqus.com/
+ __ https://github.com/django/django-contrib-comments
+
Follow the first three steps of the quick start guide in the
:doc:`documentation </ref/contrib/comments/index>`.
@@ -136,12 +148,12 @@ Feeds
=====
Suppose you want to export a :doc:`feed </ref/contrib/syndication>` of the
-latest comments, you can use the built-in :class:`LatestCommentFeed`. Just
+latest comments, you can use the built-in ``LatestCommentFeed``. Just
enable it in your project's ``urls.py``:
.. code-block:: python
- from django.conf.urls import patterns, url, include
+ from django.conf.urls import patterns
from django.contrib.comments.feeds import LatestCommentFeed
urlpatterns = patterns('',
@@ -166,7 +178,7 @@ features (all of which or only certain can be enabled):
* Close comments after a particular (user-defined) number of days.
* Email new comments to the site-staff.
-To enable comment moderation, we subclass the :class:`CommentModerator` and
+To enable comment moderation, we subclass the ``CommentModerator`` and
register it with the moderation features we want. Let's suppose we want to
close comments after 7 days of posting and also send out an email to the
site staff. In ``blog/models.py``, we register a comment moderator in the
diff --git a/docs/ref/contrib/comments/forms.txt b/docs/ref/contrib/comments/forms.txt
index c21a27bb9e..f2624ca870 100644
--- a/docs/ref/contrib/comments/forms.txt
+++ b/docs/ref/contrib/comments/forms.txt
@@ -5,6 +5,18 @@ Comment form classes
.. module:: django.contrib.comments.forms
:synopsis: Forms for dealing with the built-in comment model.
+.. warning::
+
+ Django's comment framework has been deprecated and is no longer supported.
+ Most users will be better served with a custom solution, or a hosted
+ product like Disqus__.
+
+ The code formerly known as ``django.contrib.comments`` is `still available
+ in an external repository`__.
+
+ __ https://disqus.com/
+ __ https://github.com/django/django-contrib-comments
+
The ``django.contrib.comments.forms`` module contains a handful of forms
you'll use when writing custom views dealing with comments, or when writing
:doc:`custom comment apps </ref/contrib/comments/custom>`.
@@ -43,4 +55,4 @@ forms that you can subclass to reuse pieces of the form handling logic:
Handles the details of the comment itself.
This class contains the ``name``, ``email``, ``url``, and the ``comment``
- field itself, along with the associated validation logic. \ No newline at end of file
+ field itself, along with the associated validation logic.
diff --git a/docs/ref/contrib/comments/index.txt b/docs/ref/contrib/comments/index.txt
index 8275092d2f..6db69d8168 100644
--- a/docs/ref/contrib/comments/index.txt
+++ b/docs/ref/contrib/comments/index.txt
@@ -7,6 +7,18 @@ Django's comments framework
.. highlightlang:: html+django
+.. warning::
+
+ Django's comment framework has been deprecated and is no longer supported.
+ Most users will be better served with a custom solution, or a hosted
+ product like Disqus__.
+
+ The code formerly known as ``django.contrib.comments`` is `still available
+ in an external repository`__.
+
+ __ https://disqus.com/
+ __ https://github.com/django/django-contrib-comments
+
Django includes a simple, yet customizable comments framework. The built-in
comments framework can be used to attach comments to any model, so you can use
it for comments on blog entries, photos, book chapters, or anything else.
@@ -34,7 +46,8 @@ To get started using the ``comments`` app, follow these steps:
#. Use the `comment template tags`_ below to embed comments in your
templates.
-You might also want to examine :doc:`/ref/contrib/comments/settings`.
+You might also want to examine :ref:`the available settings
+<settings-comments>`.
Comment template tags
=====================
@@ -335,6 +348,13 @@ output the CSRF token and cookie.
.. _honeypot: http://en.wikipedia.org/wiki/Honeypot_(computing)
+
+Configuration
+=============
+
+See :ref:`comment settings <settings-comments>`.
+
+
More information
================
@@ -342,7 +362,6 @@ More information
:maxdepth: 1
models
- settings
signals
custom
forms
diff --git a/docs/ref/contrib/comments/models.txt b/docs/ref/contrib/comments/models.txt
index e773790d65..cae9c11971 100644
--- a/docs/ref/contrib/comments/models.txt
+++ b/docs/ref/contrib/comments/models.txt
@@ -5,18 +5,30 @@ The built-in comment models
.. module:: django.contrib.comments.models
:synopsis: The built-in comment models
+.. warning::
+
+ Django's comment framework has been deprecated and is no longer supported.
+ Most users will be better served with a custom solution, or a hosted
+ product like Disqus__.
+
+ The code formerly known as ``django.contrib.comments`` is `still available
+ in an external repository`__.
+
+ __ https://disqus.com/
+ __ https://github.com/django/django-contrib-comments
+
.. class:: Comment
Django's built-in comment model. Has the following fields:
.. attribute:: content_object
- A :class:`~django.contrib.contettypes.generic.GenericForeignKey`
+ A :class:`~django.contrib.contenttypes.generic.GenericForeignKey`
attribute pointing to the object the comment is attached to. You can use
this to get at the related object (i.e. ``my_comment.content_object``).
Since this field is a
- :class:`~django.contrib.contettypes.generic.GenericForeignKey`, it's
+ :class:`~django.contrib.contenttypes.generic.GenericForeignKey`, it's
actually syntactic sugar on top of two underlying attributes, described
below.
@@ -77,4 +89,3 @@ The built-in comment models
``True`` if the comment was removed. Used to keep track of removed
comments instead of just deleting them.
-
diff --git a/docs/ref/contrib/comments/moderation.txt b/docs/ref/contrib/comments/moderation.txt
index 39b3ea7913..796e257200 100644
--- a/docs/ref/contrib/comments/moderation.txt
+++ b/docs/ref/contrib/comments/moderation.txt
@@ -5,6 +5,18 @@ Generic comment moderation
.. module:: django.contrib.comments.moderation
:synopsis: Support for automatic comment moderation.
+.. warning::
+
+ Django's comment framework has been deprecated and is no longer supported.
+ Most users will be better served with a custom solution, or a hosted
+ product like Disqus__.
+
+ The code formerly known as ``django.contrib.comments`` is `still available
+ in an external repository`__.
+
+ __ https://disqus.com/
+ __ https://github.com/django/django-contrib-comments
+
Django's bundled comments application is extremely useful on its own,
but the amount of comment spam circulating on the Web today
essentially makes it necessary to have some sort of automatic
@@ -81,8 +93,8 @@ Built-in moderation options
.. attribute:: auto_close_field
If this is set to the name of a
- :class:`~django.db.models.fields.DateField` or
- :class:`~django.db.models.fields.DateTimeField` on the model for which
+ :class:`~django.db.models.DateField` or
+ :class:`~django.db.models.DateTimeField` on the model for which
comments are being moderated, new comments for objects of that model
will be disallowed (immediately deleted) when a certain number of days
have passed after the date specified in that field. Must be
@@ -117,7 +129,7 @@ Built-in moderation options
.. attribute:: enable_field
If this is set to the name of a
- :class:`~django.db.models.fields.BooleanField` on the model
+ :class:`~django.db.models.BooleanField` on the model
for which comments are being moderated, new comments on
objects of that model will be disallowed (immediately deleted)
whenever the value of that field is ``False`` on the object
@@ -185,15 +197,14 @@ via two methods:
be moderated using the options defined in the
``CommentModerator`` subclass. If any of the models are
already registered for moderation, the exception
- :exc:`AlreadyModerated` will be raised.
+ ``AlreadyModerated`` will be raised.
.. function:: moderator.unregister(model_or_iterable)
Takes one argument: a model class or list of model classes,
and removes the model or models from the set of models which
are being moderated. If any of the models are not currently
- being moderated, the exception
- :exc:`NotModerated` will be raised.
+ being moderated, the exception ``NotModerated`` will be raised.
Customizing the moderation system
@@ -207,8 +218,8 @@ models with an instance of the subclass.
.. class:: Moderator
- In addition to the :meth:`Moderator.register` and
- :meth:`Moderator.unregister` methods detailed above, the following methods
+ In addition to the :func:`moderator.register` and
+ :func:`moderator.unregister` methods detailed above, the following methods
on :class:`Moderator` can be overridden to achieve customized behavior:
.. method:: connect
diff --git a/docs/ref/contrib/comments/settings.txt b/docs/ref/contrib/comments/settings.txt
deleted file mode 100644
index 1f1aecafd4..0000000000
--- a/docs/ref/contrib/comments/settings.txt
+++ /dev/null
@@ -1,33 +0,0 @@
-================
-Comment settings
-================
-
-These settings configure the behavior of the comments framework:
-
-.. setting:: COMMENTS_HIDE_REMOVED
-
-COMMENTS_HIDE_REMOVED
----------------------
-
-If ``True`` (default), removed comments will be excluded from comment
-lists/counts (as taken from template tags). Otherwise, the template author is
-responsible for some sort of a "this comment has been removed by the site staff"
-message.
-
-.. setting:: COMMENT_MAX_LENGTH
-
-COMMENT_MAX_LENGTH
-------------------
-
-The maximum length of the comment field, in characters. Comments longer than
-this will be rejected. Defaults to 3000.
-
-.. setting:: COMMENTS_APP
-
-COMMENTS_APP
-------------
-
-An app which provides :doc:`customization of the comments framework
-</ref/contrib/comments/custom>`. Use the same dotted-string notation
-as in :setting:`INSTALLED_APPS`. Your custom :setting:`COMMENTS_APP`
-must also be listed in :setting:`INSTALLED_APPS`.
diff --git a/docs/ref/contrib/comments/signals.txt b/docs/ref/contrib/comments/signals.txt
index 8274539ed7..f9df8980d7 100644
--- a/docs/ref/contrib/comments/signals.txt
+++ b/docs/ref/contrib/comments/signals.txt
@@ -5,6 +5,18 @@ Signals sent by the comments app
.. module:: django.contrib.comments.signals
:synopsis: Signals sent by the comment module.
+.. warning::
+
+ Django's comment framework has been deprecated and is no longer supported.
+ Most users will be better served with a custom solution, or a hosted
+ product like Disqus__.
+
+ The code formerly known as ``django.contrib.comments`` is `still available
+ in an external repository`__.
+
+ __ https://disqus.com/
+ __ https://github.com/django/django-contrib-comments
+
The comment app sends a series of :doc:`signals </topics/signals>` to allow for
comment moderation and similar activities. See :doc:`the introduction to signals
</topics/signals>` for information about how to register for and receive these
@@ -81,8 +93,8 @@ Arguments sent with this signal:
:meth:`~django.db.models.Model.save` again.
``flag``
- The :class:`~django.contrib.comments.models.CommentFlag` that's been
- attached to the comment.
+ The ``django.contrib.comments.models.CommentFlag`` that's been attached to
+ the comment.
``created``
``True`` if this is a new flag; ``False`` if it's a duplicate flag.
diff --git a/docs/ref/contrib/contenttypes.txt b/docs/ref/contrib/contenttypes.txt
index dfbeabc302..388172c43e 100644
--- a/docs/ref/contrib/contenttypes.txt
+++ b/docs/ref/contrib/contenttypes.txt
@@ -182,7 +182,7 @@ The ``ContentTypeManager``
Clears an internal cache used by
:class:`~django.contrib.contenttypes.models.ContentType` to keep track
- of which models for which it has created
+ of models for which it has created
:class:`~django.contrib.contenttypes.models.ContentType` instances. You
probably won't ever need to call this method yourself; Django will call
it automatically when it's needed.
@@ -234,14 +234,16 @@ lookup::
.. versionadded:: 1.5
-Prior to Django 1.5 :meth:`~ContentTypeManager.get_for_model()` and
-:meth:`~ContentTypeManager.get_for_models()` always returned the
-:class:`~django.contrib.contenttypes.models.ContentType` associated with the
-concrete model of the specified one(s). That means there was no way to retreive
-the :class:`~django.contrib.contenttypes.models.ContentType` of a proxy model
+Prior to Django 1.5,
+:meth:`~django.contrib.contenttypes.models.ContentTypeManager.get_for_model` and
+:meth:`~django.contrib.contenttypes.models.ContentTypeManager.get_for_models`
+always returned the :class:`~django.contrib.contenttypes.models.ContentType`
+associated with the concrete model of the specified one(s). That means there
+was no way to retrieve the
+:class:`~django.contrib.contenttypes.models.ContentType` of a proxy model
using those methods. As of Django 1.5 you can now pass a boolean flag –
-respectively ``for_concrete_model`` and ``for_concrete_models`` – to specify
-wether or not you want to retreive the
+``for_concrete_model`` and ``for_concrete_models`` respectively – to specify
+wether or not you want to retrieve the
:class:`~django.contrib.contenttypes.models.ContentType` for the concrete or
direct model.
@@ -275,7 +277,7 @@ A normal :class:`~django.db.models.ForeignKey` can only "point
to" one other model, which means that if the ``TaggedItem`` model used a
:class:`~django.db.models.ForeignKey` it would have to
choose one and only one model to store tags for. The contenttypes
-application provides a special field type which
+application provides a special field type (``GenericForeignKey``) which
works around this and allows the relationship to be with any
model:
@@ -285,7 +287,8 @@ model:
:class:`~django.contrib.contenttypes.generic.GenericForeignKey`:
1. Give your model a :class:`~django.db.models.ForeignKey`
- to :class:`~django.contrib.contenttypes.models.ContentType`.
+ to :class:`~django.contrib.contenttypes.models.ContentType`. The usual
+ name for this field is "content_type".
2. Give your model a field that can store primary key values from the
models you'll be relating to. For most models, this means a
@@ -450,14 +453,18 @@ need to calculate them without using the aggregation API.
Generic relations in forms and admin
------------------------------------
-The :mod:`django.contrib.contenttypes.generic` module provides
-:class:`~django.contrib.contenttypes.generic.BaseGenericInlineFormSet`,
-:class:`~django.contrib.contenttypes.generic.GenericTabularInline`
-and :class:`~django.contrib.contenttypes.generic.GenericStackedInline`
-(the last two are subclasses of
-:class:`~django.contrib.contenttypes.generic.GenericInlineModelAdmin`).
-This enables the use of generic relations in forms and the admin. See the
-:doc:`model formset </topics/forms/modelforms>` and
+The :mod:`django.contrib.contenttypes.generic` module provides:
+
+* ``BaseGenericInlineFormSet``
+* :class:`~django.contrib.contenttypes.generic.GenericTabularInline`
+ and :class:`~django.contrib.contenttypes.generic.GenericStackedInline`
+ (subclasses of
+ :class:`~django.contrib.contenttypes.generic.GenericInlineModelAdmin`)
+* A formset factory, :func:`generic_inlineformset_factory`, for use with
+ :class:`GenericForeignKey`
+
+These classes and functions enable the use of generic relations in forms
+and the admin. See the :doc:`model formset </topics/forms/modelforms>` and
:ref:`admin <using-generic-relations-as-an-inline>` documentation for more
information.
@@ -478,3 +485,20 @@ information.
The name of the integer field that represents the ID of the related
object. Defaults to ``object_id``.
+
+.. class:: GenericTabularInline
+.. class:: GenericStackedInline
+
+ Subclasses of :class:`GenericInlineModelAdmin` with stacked and tabular
+ layouts, respectively.
+
+.. function:: generic_inlineformset_factory(model, form=ModelForm, formset=BaseGenericInlineFormSet, ct_field="content_type", fk_field="object_id", fields=None, exclude=None, extra=3, can_order=False, can_delete=True, max_num=None, formfield_callback=None, validate_max=False)
+
+ Returns a ``GenericInlineFormSet`` using
+ :func:`~django.forms.models.modelformset_factory`.
+
+ You must provide ``ct_field`` and ``object_id`` if they different from the
+ defaults, ``content_type`` and ``object_id`` respectively. Other parameters
+ are similar to those documented in
+ :func:`~django.forms.models.modelformset_factory` and
+ :func:`~django.forms.models.inlineformset_factory`.
diff --git a/docs/ref/contrib/csrf.txt b/docs/ref/contrib/csrf.txt
index 32d8a705bc..968ef0b07b 100644
--- a/docs/ref/contrib/csrf.txt
+++ b/docs/ref/contrib/csrf.txt
@@ -181,7 +181,7 @@ protecting the CSRF token from being sent to other domains.
correctly on that version. Make sure you are running at least jQuery 1.5.1.
You can use `settings.crossDomain <http://api.jquery.com/jQuery.ajax>`_ in
-jQuery 1.5 and newer in order to replace the `sameOrigin` logic above:
+jQuery 1.5 and newer in order to replace the ``sameOrigin`` logic above:
.. code-block:: javascript
@@ -410,8 +410,6 @@ Utilities
.. function:: ensure_csrf_cookie(view)
- .. versionadded:: 1.4
-
This decorator forces a view to send the CSRF cookie.
Scenarios
@@ -490,64 +488,11 @@ developers of other reusable apps that want the same guarantees also use the
Settings
========
-A number of settings can be used to control Django's CSRF behavior.
-
-CSRF_COOKIE_DOMAIN
-------------------
-
-Default: ``None``
-
-The domain to be used when setting the CSRF cookie. This can be useful for
-easily allowing cross-subdomain requests to be excluded from the normal cross
-site request forgery protection. It should be set to a string such as
-``".example.com"`` to allow a POST request from a form on one subdomain to be
-accepted by a view served from another subdomain.
-
-Please note that, with or without use of this setting, this CSRF protection
-mechanism is not safe against cross-subdomain attacks -- see `Limitations`_.
-
-CSRF_COOKIE_NAME
-----------------
-
-Default: ``'csrftoken'``
-
-The name of the cookie to use for the CSRF authentication token. This can be
-whatever you want.
-
-CSRF_COOKIE_PATH
-----------------
-
-.. versionadded:: 1.4
-
-Default: ``'/'``
-
-The path set on the CSRF cookie. This should either match the URL path of your
-Django installation or be a parent of that path.
-
-This is useful if you have multiple Django instances running under the same
-hostname. They can use different cookie paths, and each instance will only see
-its own CSRF cookie.
-
-CSRF_COOKIE_SECURE
-------------------
-
-.. versionadded:: 1.4
-
-Default: ``False``
-
-Whether to use a secure cookie for the CSRF cookie. If this is set to ``True``,
-the cookie will be marked as "secure," which means browsers may ensure that the
-cookie is only sent under an HTTPS connection.
-
-CSRF_FAILURE_VIEW
------------------
-
-Default: ``'django.views.csrf.csrf_failure'``
-
-A dotted path to the view function to be used when an incoming request
-is rejected by the CSRF protection. The function should have this signature::
-
- def csrf_failure(request, reason="")
+A number of settings can be used to control Django's CSRF behavior:
-where ``reason`` is a short message (intended for developers or logging, not for
-end users) indicating the reason the request was rejected.
+* :setting:`CSRF_COOKIE_DOMAIN`
+* :setting:`CSRF_COOKIE_HTTPONLY`
+* :setting:`CSRF_COOKIE_NAME`
+* :setting:`CSRF_COOKIE_PATH`
+* :setting:`CSRF_COOKIE_SECURE`
+* :setting:`CSRF_FAILURE_VIEW`
diff --git a/docs/ref/contrib/databrowse.txt b/docs/ref/contrib/databrowse.txt
deleted file mode 100644
index 3d411bb7b4..0000000000
--- a/docs/ref/contrib/databrowse.txt
+++ /dev/null
@@ -1,89 +0,0 @@
-==========
-Databrowse
-==========
-
-.. module:: django.contrib.databrowse
- :synopsis: Databrowse is a Django application that lets you browse your data.
-
-.. deprecated:: 1.4
- This module has been deprecated.
-
-Databrowse is a Django application that lets you browse your data.
-
-As the Django admin dynamically creates an admin interface by introspecting
-your models, Databrowse dynamically creates a rich, browsable Web site by
-introspecting your models.
-
-How to use Databrowse
-=====================
-
-1. Point Django at the default Databrowse templates. There are two ways to
- do this:
-
- * Add ``'django.contrib.databrowse'`` to your :setting:`INSTALLED_APPS`
- setting. This will work if your :setting:`TEMPLATE_LOADERS` setting
- includes the ``app_directories`` template loader (which is the case by
- default). See the :ref:`template loader docs <template-loaders>` for
- more.
-
- * Otherwise, determine the full filesystem path to the
- :file:`django/contrib/databrowse/templates` directory, and add that
- directory to your :setting:`TEMPLATE_DIRS` setting.
-
-2. Register a number of models with the Databrowse site::
-
- from django.contrib import databrowse
- from myapp.models import SomeModel, SomeOtherModel, YetAnotherModel
-
- databrowse.site.register(SomeModel)
- databrowse.site.register(SomeOtherModel, YetAnotherModel)
-
- Note that you should register the model *classes*, not instances.
-
- .. versionchanged:: 1.4
-
- Since Django 1.4, it is possible to register several models in the same
- call to :func:`~databrowse.site.register`.
-
- It doesn't matter where you put this, as long as it gets executed at some
- point. A good place for it is in your :doc:`URLconf file
- </topics/http/urls>` (``urls.py``).
-
-3. Change your URLconf to import the :mod:`~django.contrib.databrowse` module::
-
- from django.contrib import databrowse
-
- ...and add the following line to your URLconf::
-
- (r'^databrowse/(.*)', databrowse.site.root),
-
- The prefix doesn't matter -- you can use ``databrowse/`` or ``db/`` or
- whatever you'd like.
-
-4. Run the Django server and visit ``/databrowse/`` in your browser.
-
-Requiring user login
-====================
-
-You can restrict access to logged-in users with only a few extra lines of
-code. Simply add the following import to your URLconf::
-
- from django.contrib.auth.decorators import login_required
-
-Then modify the :doc:`URLconf </topics/http/urls>` so that the
-:func:`databrowse.site.root` view is decorated with
-:func:`django.contrib.auth.decorators.login_required`::
-
- (r'^databrowse/(.*)', login_required(databrowse.site.root)),
-
-If you haven't already added support for user logins to your :doc:`URLconf
-</topics/http/urls>`, as described in the :doc:`user authentication docs
-</ref/contrib/auth>`, then you will need to do so now with the following
-mapping::
-
- (r'^accounts/login/$', 'django.contrib.auth.views.login'),
-
-The final step is to create the login form required by
-:func:`django.contrib.auth.views.login`. The
-:doc:`user authentication docs </ref/contrib/auth>` provide full details and a
-sample template that can be used for this purpose.
diff --git a/docs/ref/contrib/flatpages.txt b/docs/ref/contrib/flatpages.txt
index 7ff9165642..292b304acb 100644
--- a/docs/ref/contrib/flatpages.txt
+++ b/docs/ref/contrib/flatpages.txt
@@ -117,17 +117,9 @@ can do all of the work.
:class:`~django.template.RequestContext` in rendering the
template.
- .. versionchanged:: 1.4
- The middleware will only add a trailing slash and redirect (by looking
- at the :setting:`APPEND_SLASH` setting) if the resulting URL refers to
- a valid flatpage. Previously requesting a non-existent flatpage
- would redirect to the same URL with an apppended slash first and
- subsequently raise a 404.
-
- .. versionchanged:: 1.4
- Redirects by the middleware are permanent (301 status code) instead of
- temporary (302) to match behavior of the
- :class:`~django.middleware.common.CommonMiddleware`.
+ The middleware will only add a trailing slash and redirect (by looking
+ at the :setting:`APPEND_SLASH` setting) if the resulting URL refers to
+ a valid flatpage. Redirects are permanent (301 status code).
If it doesn't find a match, the request continues to be processed as usual.
@@ -194,7 +186,7 @@ Via the Python API
If you add or modify flatpages via your own code, you will likely want to
check for duplicate flatpage URLs within the same site. The flatpage form
used in the admin performs this validation check, and can be imported from
- :class:`django.contrib.flatpages.forms.FlatPageForm` and used in your own
+ ``django.contrib.flatpages.forms.FlatPageForm`` and used in your own
views.
Flatpage templates
@@ -264,7 +256,7 @@ Displaying ``registration_required`` flatpages
By default, the :ttag:`get_flatpages` templatetag will only show
flatpages that are marked ``registration_required = False``. If you
want to display registration-protected flatpages, you need to specify
-an authenticated user using a``for`` clause.
+an authenticated user using a ``for`` clause.
For example:
diff --git a/docs/ref/contrib/formtools/form-preview.txt b/docs/ref/contrib/formtools/form-preview.txt
index 784213ecba..011e72c2e0 100644
--- a/docs/ref/contrib/formtools/form-preview.txt
+++ b/docs/ref/contrib/formtools/form-preview.txt
@@ -25,9 +25,8 @@ application takes care of the following workflow:
a. If it's valid, displays a preview page.
b. If it's not valid, redisplays the form with error messages.
3. When the "confirmation" form is submitted from the preview page, calls
- a hook that you define -- a
- :meth:`~django.contrib.formtools.preview.FormPreview.done()` method that gets
- passed the valid data.
+ a hook that you define -- a ``done()`` method that gets passed the valid
+ data.
The framework enforces the required preview by passing a shared-secret hash to
the preview page via hidden form fields. If somebody tweaks the form parameters
@@ -51,8 +50,7 @@ How to use ``FormPreview``
directory to your :setting:`TEMPLATE_DIRS` setting.
2. Create a :class:`~django.contrib.formtools.preview.FormPreview` subclass that
- overrides the :meth:`~django.contrib.formtools.preview.FormPreview.done()`
- method::
+ overrides the ``done()`` method::
from django.contrib.formtools.preview import FormPreview
from myapp.models import SomeModel
@@ -92,13 +90,15 @@ How to use ``FormPreview``
A :class:`~django.contrib.formtools.preview.FormPreview` class is a simple Python class
that represents the preview workflow.
:class:`~django.contrib.formtools.preview.FormPreview` classes must subclass
-``django.contrib.formtools.preview.FormPreview`` and override the
-:meth:`~django.contrib.formtools.preview.FormPreview.done()` method. They can live
-anywhere in your codebase.
+``django.contrib.formtools.preview.FormPreview`` and override the ``done()``
+method. They can live anywhere in your codebase.
``FormPreview`` templates
=========================
+.. attribute:: FormPreview.form_template
+.. attribute:: FormPreview.preview_template
+
By default, the form is rendered via the template :file:`formtools/form.html`,
and the preview page is rendered via the template :file:`formtools/preview.html`.
These values can be overridden for a particular form preview by setting
diff --git a/docs/ref/contrib/formtools/form-wizard.txt b/docs/ref/contrib/formtools/form-wizard.txt
index 3edc019d05..f85ae8356d 100644
--- a/docs/ref/contrib/formtools/form-wizard.txt
+++ b/docs/ref/contrib/formtools/form-wizard.txt
@@ -54,7 +54,8 @@ you just have to do these things:
4. Add ``django.contrib.formtools`` to your
:setting:`INSTALLED_APPS` list in your settings file.
-5. Point your URLconf at your :class:`WizardView` :meth:`~WizardView.as_view` method.
+5. Point your URLconf at your :class:`WizardView` :meth:`~WizardView.as_view`
+ method.
Defining ``Form`` classes
-------------------------
@@ -89,6 +90,9 @@ the message itself. Here's what the :file:`forms.py` might look like::
Creating a ``WizardView`` subclass
----------------------------------
+.. class:: SessionWizardView
+.. class:: CookieWizardView
+
The next step is to create a
:class:`django.contrib.formtools.wizard.views.WizardView` subclass. You can
also use the :class:`SessionWizardView` or :class:`CookieWizardView` classes
@@ -225,9 +229,11 @@ Here's a full example template:
Hooking the wizard into a URLconf
---------------------------------
+.. method:: WizardView.as_view
+
Finally, we need to specify which forms to use in the wizard, and then
deploy the new :class:`WizardView` object at a URL in the ``urls.py``. The
-wizard's :meth:`as_view` method takes a list of your
+wizard's ``as_view()`` method takes a list of your
:class:`~django.forms.Form` classes as an argument during instantiation::
from django.conf.urls import patterns
@@ -239,6 +245,13 @@ wizard's :meth:`as_view` method takes a list of your
(r'^contact/$', ContactWizard.as_view([ContactForm1, ContactForm2])),
)
+.. versionchanged:: 1.6
+
+You can also pass the form list as a class attribute named ``form_list``::
+
+ class ContactWizard(WizardView):
+ form_list = [ContactForm1, ContactForm2]
+
.. _wizard-template-for-each-form:
Using a different template for each form
@@ -289,6 +302,14 @@ The ``urls.py`` file would contain something like::
(r'^checkout/$', OrderWizard.as_view(FORMS, condition_dict={'cc': pay_by_credit_card})),
)
+.. versionchanged:: 1.6
+
+The ``condiction_dict`` can be passed as attribute for the ``as_view()`
+method or as a class attribute named ``condition_dict``::
+
+ class OrderWizard(WizardView):
+ condition_dict = {'cc': pay_by_credit_card}
+
Note that the ``OrderWizard`` object is initialized with a list of pairs.
The first element in the pair is a string that corresponds to the name of the
step and the second is the form class.
@@ -312,11 +333,16 @@ Advanced ``WizardView`` methods
counter as string representing the current step of the wizard. (E.g., the
first form is ``'0'`` and the second form is ``'1'``)
-.. method:: WizardView.get_form_prefix(step)
+.. method:: WizardView.get_form_prefix(step=None, form=None)
+
+ Returns the prefix which will be used when calling the form for the given
+ step. ``step`` contains the step name, ``form`` the form class which will
+ be called with the returned prefix.
- Given the step, returns a form prefix to use. By default, this simply uses
- the step itself. For more, see the :ref:`form prefix documentation
- <form-prefix>`.
+ If no ``step`` is given, it will be determined automatically. By default,
+ this simply uses the step itself and the ``form`` parameter is not used.
+
+ For more, see the :ref:`form prefix documentation <form-prefix>`.
.. method:: WizardView.get_form_initial(step)
@@ -346,9 +372,9 @@ Advanced ``WizardView`` methods
used as the form for step ``step``.
Returns an :class:`~django.db.models.Model` object which will be passed as
- the :attr:`~django.forms.ModelForm.instance` argument when instantiating the
- ModelForm for step ``step``. If no instance object was provided while
- initializing the form wizard, ``None`` will be returned.
+ the ``instance`` argument when instantiating the ``ModelForm`` for step
+ ``step``. If no instance object was provided while initializing the form
+ wizard, ``None`` will be returned.
The default implementation::
@@ -443,6 +469,17 @@ Advanced ``WizardView`` methods
def process_step_files(self, form):
return self.get_form_step_files(form)
+.. method:: WizardView.render_goto_step(step, goto_step, **kwargs)
+
+ .. versionadded:: 1.6
+
+ This method is called when the step should be changed to something else
+ than the next step. By default, this method just stores the requested
+ step ``goto_step`` in the storage and then renders the new step.
+
+ If you want to store the entered data of the current step before rendering
+ the next step, you can overwrite this method.
+
.. method:: WizardView.render_revalidation_failure(step, form, **kwargs)
When the wizard thinks all steps have passed it revalidates all forms with
@@ -514,10 +551,10 @@ Providing initial data for the forms
.. attribute:: WizardView.initial_dict
Initial data for a wizard's :class:`~django.forms.Form` objects can be
- provided using the optional :attr:`~Wizard.initial_dict` keyword argument.
- This argument should be a dictionary mapping the steps to dictionaries
- containing the initial data for each step. The dictionary of initial data
- will be passed along to the constructor of the step's
+ provided using the optional :attr:`~WizardView.initial_dict` keyword
+ argument. This argument should be a dictionary mapping the steps to
+ dictionaries containing the initial data for each step. The dictionary of
+ initial data will be passed along to the constructor of the step's
:class:`~django.forms.Form`::
>>> from myapp.forms import ContactForm1, ContactForm2
@@ -526,7 +563,9 @@ Providing initial data for the forms
... '0': {'subject': 'Hello', 'sender': 'user@example.com'},
... '1': {'message': 'Hi there!'}
... }
- >>> wiz = ContactWizard.as_view([ContactForm1, ContactForm2], initial_dict=initial)
+ >>> # This example is illustrative only and isn't meant to be run in
+ >>> # the shell since it requires an HttpRequest to pass to the view.
+ >>> wiz = ContactWizard.as_view([ContactForm1, ContactForm2], initial_dict=initial)(request)
>>> form1 = wiz.get_form('0')
>>> form2 = wiz.get_form('1')
>>> form1.initial
@@ -537,16 +576,23 @@ Providing initial data for the forms
The ``initial_dict`` can also take a list of dictionaries for a specific
step if the step is a ``FormSet``.
+ .. versionchanged:: 1.6
+
+ The ``initial_dict`` can also be added as a class attribute named
+ ``initial_dict`` to avoid having the initial data in the ``urls.py``.
+
.. _wizard-files:
Handling files
==============
+.. attribute:: WizardView.file_storage
+
To handle :class:`~django.forms.FileField` within any step form of the wizard,
-you have to add a :attr:`file_storage` to your :class:`WizardView` subclass.
+you have to add a ``file_storage`` to your :class:`WizardView` subclass.
This storage will temporarily store the uploaded files for the wizard. The
-:attr:`file_storage` attribute should be a
+``file_storage`` attribute should be a
:class:`~django.core.files.storage.Storage` subclass.
Django provides a built-in storage class (see :ref:`the built-in filesystem
@@ -646,6 +692,8 @@ Usage of ``NamedUrlWizardView``
===============================
.. class:: NamedUrlWizardView
+.. class:: NamedUrlSessionWizardView
+.. class:: NamedUrlCookieWizardView
There is a :class:`WizardView` subclass which adds named-urls support to the
wizard. By doing this, you can have single urls for every step. You can also
diff --git a/docs/ref/contrib/formtools/index.txt b/docs/ref/contrib/formtools/index.txt
index f36470654a..e768c0e655 100644
--- a/docs/ref/contrib/formtools/index.txt
+++ b/docs/ref/contrib/formtools/index.txt
@@ -1,6 +1,8 @@
django.contrib.formtools
========================
+.. module:: django.contrib.formtools
+
A set of high-level abstractions for Django forms (:mod:`django.forms`).
.. toctree::
diff --git a/docs/ref/contrib/gis/commands.txt b/docs/ref/contrib/gis/commands.txt
index 3dd161ce1d..015a1f9741 100644
--- a/docs/ref/contrib/gis/commands.txt
+++ b/docs/ref/contrib/gis/commands.txt
@@ -47,7 +47,8 @@ of using ``ogrinspect`` :ref:`in the tutorial <ogrinspect-intro>`.
The key for specifying which layer in the OGR
:class:`~django.contrib.gis.gdal.DataSource` source to use.
Defaults to 0 (the first layer). May be an integer or a string identifier
- for the :class:`~django.contrib.gis.gdal.Layer`.
+ for the :class:`~django.contrib.gis.gdal.Layer`. When inspecting databases,
+ ``layer`` is generally the table name you want to inspect.
.. django-admin-option:: --mapping
diff --git a/docs/ref/contrib/gis/db-api.txt b/docs/ref/contrib/gis/db-api.txt
index 519f79f0d4..be413c9df8 100644
--- a/docs/ref/contrib/gis/db-api.txt
+++ b/docs/ref/contrib/gis/db-api.txt
@@ -4,20 +4,23 @@
GeoDjango Database API
======================
-.. module:: django.contrib.gis.db.models
- :synopsis: GeoDjango's database API.
-
.. _spatial-backends:
Spatial Backends
================
+.. module:: django.contrib.gis.db.backends
+ :synopsis: GeoDjango's spatial database backends.
+
GeoDjango currently provides the following spatial database backends:
-* :mod:`django.contrib.gis.db.backends.postgis`
-* :mod:`django.contrib.gis.db.backends.mysql`
-* :mod:`django.contrib.gis.db.backends.oracle`
-* :mod:`django.contrib.gis.db.backends.spatialite`
+* ``django.contrib.gis.db.backends.postgis``
+* ``django.contrib.gis.db.backends.mysql``
+* ``django.contrib.gis.db.backends.oracle``
+* ``django.contrib.gis.db.backends.spatialite``
+
+.. module:: django.contrib.gis.db.models
+ :synopsis: GeoDjango's database API.
.. _mysql-spatial-limitations:
diff --git a/docs/ref/contrib/gis/feeds.txt b/docs/ref/contrib/gis/feeds.txt
index 7c3a2d011c..7b1b6ebccf 100644
--- a/docs/ref/contrib/gis/feeds.txt
+++ b/docs/ref/contrib/gis/feeds.txt
@@ -27,7 +27,7 @@ API Reference
.. class:: Feed
In addition to methods provided by
- the :class:`django.contrib.syndication.feeds.Feed`
+ the :class:`django.contrib.syndication.views.Feed`
base class, GeoDjango's ``Feed`` class provides
the following overrides. Note that these overrides may be done in multiple ways::
@@ -71,11 +71,11 @@ API Reference
can be a ``GEOSGeometry`` instance, or a tuple that represents a
point coordinate or bounding box. For example::
- class ZipcodeFeed(Feed):
+ class ZipcodeFeed(Feed):
- def item_geometry(self, obj):
- # Returns the polygon.
- return obj.poly
+ def item_geometry(self, obj):
+ # Returns the polygon.
+ return obj.poly
``SyndicationFeed`` Subclasses
------------------------------
diff --git a/docs/ref/contrib/gis/gdal.txt b/docs/ref/contrib/gis/gdal.txt
index c4b29bead7..c68030673b 100644
--- a/docs/ref/contrib/gis/gdal.txt
+++ b/docs/ref/contrib/gis/gdal.txt
@@ -13,7 +13,7 @@ of GDAL is the `OGR`__ Simple Features Library, which specializes
in reading and writing vector geographic data in a variety of standard
formats.
-GeoDjango provides a high-level Python interface for some of the
+GeoDjango provides a high-level Python interface for some of the
capabilities of OGR, including the reading and coordinate transformation
of vector spatial data.
@@ -22,7 +22,7 @@ of vector spatial data.
Although the module is named ``gdal``, GeoDjango only supports
some of the capabilities of OGR. Thus, none of GDAL's features
with respect to raster (image) data are supported at this time.
-
+
__ http://www.gdal.org/
__ http://www.gdal.org/ogr/
@@ -68,13 +68,13 @@ each feature in that layer.
also supports a variety of more complex data sources, including
databases, that may be accessed by passing a special name string instead
of a path. For more information, see the `OGR Vector Formats`__
- documentation. The :attr:`name` property of a ``DataSource``
+ documentation. The :attr:`name` property of a ``DataSource``
instance gives the OGR name of the underlying data source that it is
using.
- Once you've created your ``DataSource``, you can find out how many
- layers of data it contains by accessing the :attr:`layer_count` property,
- or (equivalently) by using the ``len()`` function. For information on
+ Once you've created your ``DataSource``, you can find out how many
+ layers of data it contains by accessing the :attr:`layer_count` property,
+ or (equivalently) by using the ``len()`` function. For information on
accessing the layers of data themselves, see the next section::
>>> from django.contrib.gis.gdal import DataSource
@@ -105,7 +105,7 @@ __ http://www.gdal.org/ogr/ogr_formats.html
Python container of ``Layer`` objects. For example, you can access a
specific layer by its index (e.g. ``ds[0]`` to access the first
layer), or you can iterate over all the layers in the container in a
- ``for`` loop. The ``Layer`` itself acts as a container for geometric
+ ``for`` loop. The ``Layer`` itself acts as a container for geometric
features.
Typically, all the features in a given layer have the same geometry type.
@@ -120,7 +120,7 @@ __ http://www.gdal.org/ogr/ogr_formats.html
The example output is from the cities data source, loaded above, which
evidently contains one layer, called ``"cities"``, which contains three
- point features. For simplicity, the examples below assume that you've
+ point features. For simplicity, the examples below assume that you've
stored that layer in the variable ``layer``::
>>> layer = ds[0]
@@ -169,7 +169,7 @@ __ http://www.gdal.org/ogr/ogr_formats.html
>>> [ft.__name__ for ft in layer.field_types]
['OFTString', 'OFTReal', 'OFTReal', 'OFTDate']
-
+
.. attribute:: field_widths
Returns a list of the maximum field widths for each of the fields in
@@ -181,7 +181,7 @@ __ http://www.gdal.org/ogr/ogr_formats.html
.. attribute:: field_precisions
Returns a list of the numeric precisions for each of the fields in
- this layer. This is meaningless (and set to zero) for non-numeric
+ this layer. This is meaningless (and set to zero) for non-numeric
fields::
>>> layer.field_precisions
@@ -189,7 +189,7 @@ __ http://www.gdal.org/ogr/ogr_formats.html
.. attribute:: extent
- Returns the spatial extent of this layer, as an :class:`Envelope`
+ Returns the spatial extent of this layer, as an :class:`Envelope`
object::
>>> layer.extent.tuple
@@ -214,7 +214,7 @@ __ http://www.gdal.org/ogr/ogr_formats.html
Property that may be used to retrieve or set a spatial filter for this
layer. A spatial filter can only be set with an :class:`OGRGeometry`
- instance, a 4-tuple extent, or ``None``. When set with something
+ instance, a 4-tuple extent, or ``None``. When set with something
other than ``None``, only features that intersect the filter will be
returned when iterating over the layer::
@@ -258,9 +258,9 @@ __ http://www.gdal.org/ogr/ogr_formats.html
given capability (a string). Examples of valid capability strings
include: ``'RandomRead'``, ``'SequentialWrite'``, ``'RandomWrite'``,
``'FastSpatialFilter'``, ``'FastFeatureCount'``, ``'FastGetExtent'``,
- ``'CreateField'``, ``'Transactions'``, ``'DeleteFeature'``, and
+ ``'CreateField'``, ``'Transactions'``, ``'DeleteFeature'``, and
``'FastSetNextByIndex'``.
-
+
``Feature``
-----------
@@ -295,14 +295,14 @@ __ http://www.gdal.org/ogr/ogr_formats.html
Returns the type of geometry for this feature, as an :class:`OGRGeomType`
object. This will be the same for all features in a given layer, and
- is equivalent to the :attr:`Layer.geom_type` property of the
- :class:`Layer`` object the feature came from.
+ is equivalent to the :attr:`Layer.geom_type` property of the
+ :class:`Layer` object the feature came from.
.. attribute:: num_fields
Returns the number of fields of data associated with the feature.
This will be the same for all features in a given layer, and is
- equivalent to the :attr:`Layer.num_fields` property of the
+ equivalent to the :attr:`Layer.num_fields` property of the
:class:`Layer` object the feature came from.
.. attribute:: fields
@@ -350,7 +350,7 @@ __ http://www.gdal.org/ogr/ogr_formats.html
.. attribute:: type
Returns the OGR type of this field, as an integer. The
- ``FIELD_CLASSES`` dictionary maps these values onto
+ ``FIELD_CLASSES`` dictionary maps these values onto
subclasses of ``Field``::
>>> city['Density'].type
@@ -365,8 +365,8 @@ __ http://www.gdal.org/ogr/ogr_formats.html
.. attribute:: value
- Returns the value of this field. The ``Field`` class itself
- returns the value as a string, but each subclass returns the
+ Returns the value of this field. The ``Field`` class itself
+ returns the value as a string, but each subclass returns the
value in the most appropriate form::
>>> city['Population'].value
@@ -433,10 +433,10 @@ OGR Geometries
``OGRGeometry``
---------------
-:class:`OGRGeometry` objects share similar functionality with
+:class:`OGRGeometry` objects share similar functionality with
:class:`~django.contrib.gis.geos.GEOSGeometry` objects, and are thin
-wrappers around OGR's internal geometry representation. Thus,
-they allow for more efficient access to data when using :class:`DataSource`.
+wrappers around OGR's internal geometry representation. Thus,
+they allow for more efficient access to data when using :class:`DataSource`.
Unlike its GEOS counterpart, :class:`OGRGeometry` supports spatial reference
systems and coordinate transformation::
@@ -446,10 +446,10 @@ systems and coordinate transformation::
.. class:: OGRGeometry(geom_input[, srs=None])
This object is a wrapper for the `OGR Geometry`__ class.
- These objects are instantiated directly from the given ``geom_input``
+ These objects are instantiated directly from the given ``geom_input``
parameter, which may be a string containing WKT, HEX, GeoJSON, a ``buffer``
containing WKB data, or an :class:`OGRGeomType` object. These objects
- are also returned from the :class:`Feature.geom` attribute, when
+ are also returned from the :class:`Feature.geom` attribute, when
reading vector data from :class:`Layer` (which is in turn a part of
a :class:`DataSource`).
@@ -557,14 +557,14 @@ systems and coordinate transformation::
.. attribute:: srid
- Returns or sets the spatial reference identifier corresponding to
+ Returns or sets the spatial reference identifier corresponding to
:class:`SpatialReference` of this geometry. Returns ``None`` if
there is no spatial reference information associated with this
geometry, or if an SRID cannot be determined.
.. attribute:: geos
- Returns a :class:`~django.contrib.gis.geos.GEOSGeometry` object
+ Returns a :class:`~django.contrib.gis.geos.GEOSGeometry` object
corresponding to this geometry.
.. attribute:: gml
@@ -634,8 +634,8 @@ systems and coordinate transformation::
or any other input accepted by :class:`SpatialReference` (including
spatial reference WKT and PROJ.4 strings, or an integer SRID).
By default nothing is returned and the geometry is transformed in-place.
- However, if the `clone` keyword is set to ``True`` then a transformed clone
- of this geometry is returned instead.
+ However, if the ``clone`` keyword is set to ``True`` then a transformed
+ clone of this geometry is returned instead.
.. method:: intersects(other)
@@ -762,9 +762,9 @@ systems and coordinate transformation::
.. attribute:: z
- Returns a list of Z coordinates in this line, or ``None`` if the
+ Returns a list of Z coordinates in this line, or ``None`` if the
line does not have Z coordinates::
-
+
>>> OGRGeometry('LINESTRING (1 2 3,4 5 6)').z
[3.0, 6.0]
@@ -885,7 +885,7 @@ Coordinate System Objects
Spatial reference objects are initialized on the given ``srs_input``,
which may be one of the following:
-
+
* OGC Well Known Text (WKT) (a string)
* EPSG code (integer or string)
* PROJ.4 string
@@ -912,7 +912,7 @@ Coordinate System Objects
.. method:: __getitem__(target)
Returns the value of the given string attribute node, ``None`` if the node
- doesn't exist. Can also take a tuple as a parameter, (target, child),
+ doesn't exist. Can also take a tuple as a parameter, (target, child),
where child is the index of the attribute in the WKT. For example::
>>> wkt = 'GEOGCS["WGS 84", DATUM["WGS_1984, ... AUTHORITY["EPSG","4326"]]')
@@ -1011,7 +1011,7 @@ Coordinate System Objects
.. attribute:: units
- Returns a 2-tuple of the units value and the units name,
+ Returns a 2-tuple of the units value and the units name,
and will automatically determines whether to return the linear
or angular units.
@@ -1073,7 +1073,7 @@ Coordinate System Objects
.. class:: CoordTransform(source, target)
-Represents a coordinate system transform. It is initialized with two
+Represents a coordinate system transform. It is initialized with two
:class:`SpatialReference`, representing the source and target coordinate
systems, respectively. These objects should be used when performing
the same coordinate transformation repeatedly on different geometries::
diff --git a/docs/ref/contrib/gis/geoip.txt b/docs/ref/contrib/gis/geoip.txt
index e37c4c60b0..2444849a19 100644
--- a/docs/ref/contrib/gis/geoip.txt
+++ b/docs/ref/contrib/gis/geoip.txt
@@ -7,22 +7,13 @@ Geolocation with GeoIP
.. module:: django.contrib.gis.geoip
:synopsis: High-level Python interface for MaxMind's GeoIP C library.
-.. versionchanged:: 1.4
-
-.. note::
-
- In Django 1.4, the :class:`GeoIP` object was moved out of
- :mod:`django.contrib.gis.utils` and into its own module,
- :mod:`django.contrib.gis.geoip`. A shortcut is still provided
- in ``utils``, but will be removed in Django 1.6.
-
The :class:`GeoIP` object is a ctypes wrapper for the
`MaxMind GeoIP C API`__. [#]_ This interface is a BSD-licensed alternative
to the GPL-licensed `Python GeoIP`__ interface provided by MaxMind.
In order to perform IP-based geolocation, the :class:`GeoIP` object requires
-the GeoIP C libary and either the GeoIP `Country`__ or `City`__
-datasets in binary format (the CSV files will not work!). These datasets may be
+the GeoIP C libary and either the GeoIP `Country`__ or `City`__
+datasets in binary format (the CSV files will not work!). These datasets may be
`downloaded from MaxMind`__. Grab the ``GeoLiteCountry/GeoIP.dat.gz`` and
``GeoLiteCity.dat.gz`` files and unzip them in a directory corresponding to what
you set :setting:`GEOIP_PATH` with in your settings. See the example and
@@ -58,7 +49,7 @@ usage::
>>> g.lat_lon('salon.com')
(37.789798736572266, -122.39420318603516)
>>> g.lon_lat('uh.edu')
- (-95.415199279785156, 29.77549934387207)
+ (-95.415199279785156, 29.77549934387207)
>>> g.geos('24.124.1.80').wkt
'POINT (-95.2087020874023438 39.0392990112304688)'
@@ -104,30 +95,30 @@ Defaults to ``'GeoLiteCity.dat'``.
.. class:: GeoIP([path=None, cache=0, country=None, city=None])
-The ``GeoIP`` object does not require any parameters to use the default
+The ``GeoIP`` object does not require any parameters to use the default
settings. However, at the very least the :setting:`GEOIP_PATH` setting
-should be set with the path of the location of your GeoIP data sets. The
-following intialization keywords may be used to customize any of the
-defaults.
+should be set with the path of the location of your GeoIP data sets. The
+following intialization keywords may be used to customize any of the
+defaults.
=================== =======================================================
Keyword Arguments Description
=================== =======================================================
-``path`` Base directory to where GeoIP data is located or the
- full path to where the city or country data files
- (.dat) are located. Assumes that both the city and
- country data sets are located in this directory;
+``path`` Base directory to where GeoIP data is located or the
+ full path to where the city or country data files
+ (.dat) are located. Assumes that both the city and
+ country data sets are located in this directory;
overrides the :setting:`GEOIP_PATH` settings attribute.
``cache`` The cache settings when opening up the GeoIP datasets,
and may be an integer in (0, 1, 2, 4) corresponding to
- the ``GEOIP_STANDARD``, ``GEOIP_MEMORY_CACHE``,
- ``GEOIP_CHECK_CACHE``, and ``GEOIP_INDEX_CACHE``
- ``GeoIPOptions`` C API settings, respectively.
+ the ``GEOIP_STANDARD``, ``GEOIP_MEMORY_CACHE``,
+ ``GEOIP_CHECK_CACHE``, and ``GEOIP_INDEX_CACHE``
+ ``GeoIPOptions`` C API settings, respectively.
Defaults to 0 (``GEOIP_STANDARD``).
-
+
``country`` The name of the GeoIP country data file. Defaults
- to ``GeoIP.dat``. Setting this keyword overrides the
+ to ``GeoIP.dat``. Setting this keyword overrides the
:setting:`GEOIP_COUNTRY` settings attribute.
``city`` The name of the GeoIP city data file. Defaults to
@@ -142,9 +133,9 @@ Querying
--------
All the following querying routines may take either a string IP address
-or a fully qualified domain name (FQDN). For example, both
-``'205.186.163.125'`` and ``'djangoproject.com'`` would be valid query
-parameters.
+or a fully qualified domain name (FQDN). For example, both
+``'205.186.163.125'`` and ``'djangoproject.com'`` would be valid query
+parameters.
.. method:: GeoIP.city(query)
@@ -153,7 +144,7 @@ of the values in the dictionary may be undefined (``None``).
.. method:: GeoIP.country(query)
-Returns a dictionary with the country code and country for the given
+Returns a dictionary with the country code and country for the given
query.
.. method:: GeoIP.country_code(query)
@@ -202,7 +193,7 @@ and country), and the version of the GeoIP C library (if supported).
GeoIP-Python API compatibility methods
----------------------------------------
-These methods exist to ease compatibility with any code using MaxMind's
+These methods exist to ease compatibility with any code using MaxMind's
existing Python API.
.. classmethod:: GeoIP.open(path, cache)
diff --git a/docs/ref/contrib/gis/geoquerysets.txt b/docs/ref/contrib/gis/geoquerysets.txt
index 69280dc028..97217e3c38 100644
--- a/docs/ref/contrib/gis/geoquerysets.txt
+++ b/docs/ref/contrib/gis/geoquerysets.txt
@@ -683,7 +683,7 @@ Keyword Argument Description
a method name clashes with an existing
``GeoQuerySet`` method -- if you wanted to use the
``area()`` method on model with a ``PolygonField``
- named ``area``, for example.
+ named ``area``, for example.
===================== =====================================================
Measurement
@@ -949,6 +949,9 @@ __ http://geohash.org/
*Availability*: PostGIS, SpatiaLite
+.. versionchanged:: 1.5
+ ``geojson`` support for Spatialite > 3.0 has been added.
+
Attaches a ``geojson`` attribute to every model in the queryset that contains the
`GeoJSON`__ representation of the geometry.
@@ -1043,7 +1046,7 @@ Keyword Argument Description
===================== =====================================================
``relative`` If set to ``True``, the path data will be implemented
in terms of relative moves. Defaults to ``False``,
- meaning that absolute moves are used instead.
+ meaning that absolute moves are used instead.
``precision`` This keyword may be used to specify the number of
significant digits for the coordinates in the SVG
diff --git a/docs/ref/contrib/gis/geos.txt b/docs/ref/contrib/gis/geos.txt
index 7d7c32781c..4d44638488 100644
--- a/docs/ref/contrib/gis/geos.txt
+++ b/docs/ref/contrib/gis/geos.txt
@@ -142,10 +142,9 @@ Geometry Objects
.. class:: GEOSGeometry(geo_input[, srid=None])
- :param geo_input: Geometry input value
- :type geo_input: string or buffer
+ :param geo_input: Geometry input value (string or buffer)
:param srid: spatial reference identifier
- :type srid: integer
+ :type srid: int
This is the base class for all GEOS geometry objects. It initializes on the
given ``geo_input`` argument, and then assumes the proper geometry subclass
@@ -800,7 +799,7 @@ Example::
:param string: string that contains spatial data
:type string: string
:param srid: spatial reference identifier
- :type srid: integer
+ :type srid: int
:rtype: a :class:`GEOSGeometry` corresponding to the spatial data in the string
Example::
@@ -966,3 +965,10 @@ location (e.g., ``/home/bob/lib/libgeos_c.so``).
The setting must be the *full* path to the **C** shared library; in
other words you want to use ``libgeos_c.so``, not ``libgeos.so``.
+
+Exceptions
+==========
+
+.. exception:: GEOSException
+
+The base GEOS exception, indicates a GEOS-related error.
diff --git a/docs/ref/contrib/gis/install/create_template_postgis-debian.sh b/docs/ref/contrib/gis/install/create_template_postgis-debian.sh
index 3e621837fa..c59834c87e 100755
--- a/docs/ref/contrib/gis/install/create_template_postgis-debian.sh
+++ b/docs/ref/contrib/gis/install/create_template_postgis-debian.sh
@@ -3,13 +3,6 @@
GEOGRAPHY=0
POSTGIS_SQL=postgis.sql
-# For Ubuntu 8.x and 9.x releases.
-if [ -d "/usr/share/postgresql-8.3-postgis" ]
-then
- POSTGIS_SQL_PATH=/usr/share/postgresql-8.3-postgis
- POSTGIS_SQL=lwpostgis.sql
-fi
-
# For Ubuntu 10.04
if [ -d "/usr/share/postgresql/8.4/contrib" ]
then
diff --git a/docs/ref/contrib/gis/install/geolibs.txt b/docs/ref/contrib/gis/install/geolibs.txt
index c78f0c0e62..74ebf6a35f 100644
--- a/docs/ref/contrib/gis/install/geolibs.txt
+++ b/docs/ref/contrib/gis/install/geolibs.txt
@@ -88,16 +88,16 @@ internal geometry representation used by GeoDjango (it's behind the "lazy"
geometries). Specifically, the C API library is called (e.g., ``libgeos_c.so``)
directly from Python using ctypes.
-First, download GEOS 3.3.5 from the refractions Web site and untar the source
+First, download GEOS 3.3.8 from the refractions Web site and untar the source
archive::
- $ wget http://download.osgeo.org/geos/geos-3.3.5.tar.bz2
- $ tar xjf geos-3.3.5.tar.bz2
+ $ wget http://download.osgeo.org/geos/geos-3.3.8.tar.bz2
+ $ tar xjf geos-3.3.8.tar.bz2
Next, change into the directory where GEOS was unpacked, run the configure
script, compile, and install::
- $ cd geos-3.3.5
+ $ cd geos-3.3.8
$ ./configure
$ make
$ sudo make install
@@ -181,9 +181,9 @@ supports :ref:`GDAL's vector data <ref-gdal>` capabilities [#]_.
First download the latest GDAL release version and untar the archive::
- $ wget http://download.osgeo.org/gdal/gdal-1.9.1.tar.gz
- $ tar xzf gdal-1.9.1.tar.gz
- $ cd gdal-1.9.1
+ $ wget http://download.osgeo.org/gdal/gdal-1.9.2.tar.gz
+ $ tar xzf gdal-1.9.2.tar.gz
+ $ cd gdal-1.9.2
Configure, make and install::
@@ -216,7 +216,7 @@ Can't find GDAL library
When GeoDjango can't find the GDAL library, the ``HAS_GDAL`` flag
will be false:
-.. code-block:: pycon
+.. code-block:: python
>>> from django.contrib.gis import gdal
>>> gdal.HAS_GDAL
diff --git a/docs/ref/contrib/gis/install/index.txt b/docs/ref/contrib/gis/install/index.txt
index 100dc2edd0..62369d8253 100644
--- a/docs/ref/contrib/gis/install/index.txt
+++ b/docs/ref/contrib/gis/install/index.txt
@@ -46,8 +46,8 @@ how to install.
Spatial database
----------------
-PostgreSQL (with PostGIS), MySQL, Oracle, and SQLite (with SpatiaLite) are
-the spatial databases currently supported.
+PostgreSQL (with PostGIS), MySQL (mostly with MyISAM engine), Oracle, and SQLite
+(with SpatiaLite) are the spatial databases currently supported.
.. note::
@@ -61,8 +61,8 @@ supported versions, and any notes for each of the supported database backends:
================== ============================== ================== =========================================
Database Library Requirements Supported Versions Notes
================== ============================== ================== =========================================
-PostgreSQL GEOS, PROJ.4, PostGIS 8.2+ Requires PostGIS.
-MySQL GEOS 5.x Not OGC-compliant; limited functionality.
+PostgreSQL GEOS, PROJ.4, PostGIS 8.4+ Requires PostGIS.
+MySQL GEOS 5.x Not OGC-compliant; :ref:`limited functionality <mysql-spatial-limitations>`.
Oracle GEOS 10.2, 11 XE not supported; not tested with 9.
SQLite GEOS, GDAL, PROJ.4, SpatiaLite 3.6.+ Requires SpatiaLite 2.3+, pysqlite2 2.5+
================== ============================== ================== =========================================
@@ -330,7 +330,7 @@ described above, ``psycopg2`` may be installed using the following command::
.. note::
- If you don't have ``pip``, follow the the :ref:`installation instructions
+ If you don't have ``pip``, follow the :ref:`installation instructions
<installing-official-release>` to install it.
.. _fink:
@@ -392,7 +392,7 @@ GeoDjango on Windows.
.. note::
These instructions assume that you are using 32-bit versions of
- all programs. While 64-bit versions of Python and PostgreSQL 9.0
+ all programs. While 64-bit versions of Python and PostgreSQL 9.x
are available, 64-bit versions of spatial libraries, like
GEOS and GDAL, are not yet provided by the :ref:`OSGeo4W` installer.
@@ -415,7 +415,7 @@ __ http://python.org/download/
PostgreSQL
^^^^^^^^^^
-First, download the latest `PostgreSQL 9.0 installer`__ from the
+First, download the latest `PostgreSQL 9.x installer`__ from the
`EnterpriseDB`__ Web site. After downloading, simply run the installer,
follow the on-screen directions, and keep the default options unless
you know the consequences of changing them.
@@ -435,7 +435,7 @@ install :ref:`postgisasb`.
If installed successfully, the PostgreSQL server will run in the
background each time the system as started as a Windows service.
- A :menuselection:`PostgreSQL 9.0` start menu group will created
+ A :menuselection:`PostgreSQL 9.x` start menu group will created
and contains shortcuts for the ASB as well as the 'SQL Shell',
which will launch a ``psql`` command window.
@@ -448,10 +448,10 @@ PostGIS
^^^^^^^
From within the Application Stack Builder (to run outside of the installer,
-:menuselection:`Start --> Programs --> PostgreSQL 9.0`), select
-:menuselection:`PostgreSQL Database Server 9.0 on port 5432` from the drop down
+:menuselection:`Start --> Programs --> PostgreSQL 9.x`), select
+:menuselection:`PostgreSQL Database Server 9.x on port 5432` from the drop down
menu. Next, expand the :menuselection:`Categories --> Spatial Extensions` menu
-tree and select :menuselection:`PostGIS 1.5 for PostgreSQL 9.0`.
+tree and select :menuselection:`PostGIS 1.5 for PostgreSQL 9.x`.
After clicking next, you will be prompted to select your mirror, PostGIS
will be downloaded, and the PostGIS installer will begin. Select only the
@@ -530,6 +530,6 @@ Finally, :ref:`install Django <installing-official-release>` on your system.
.. rubric:: Footnotes
.. [#] GeoDjango uses the :func:`~ctypes.util.find_library` routine from
- :mod:`ctypes.util` to locate shared libraries.
+ ``ctypes.util`` to locate shared libraries.
.. [#] The ``psycopg2`` Windows installers are packaged and maintained by
`Jason Erickson <http://www.stickpeople.com/projects/python/win-psycopg/>`_.
diff --git a/docs/ref/contrib/gis/install/postgis.txt b/docs/ref/contrib/gis/install/postgis.txt
index 6d7fe88203..c651fe8fca 100644
--- a/docs/ref/contrib/gis/install/postgis.txt
+++ b/docs/ref/contrib/gis/install/postgis.txt
@@ -28,9 +28,9 @@ Building from source
First download the source archive, and extract::
- $ wget http://postgis.refractions.net/download/postgis-2.0.1.tar.gz
- $ tar xzf postgis-2.0.1.tar.gz
- $ cd postgis-2.0.1
+ $ wget http://download.osgeo.org/postgis/source/postgis-2.0.3.tar.gz
+ $ tar xzf postgis-2.0.3.tar.gz
+ $ cd postgis-2.0.3
Next, configure, make and install PostGIS::
@@ -56,10 +56,10 @@ Post-installation
.. _spatialdb_template:
.. _spatialdb_template91:
-Creating a spatial database with PostGIS 2.0 and PostgreSQL 9.1
----------------------------------------------------------------
+Creating a spatial database with PostGIS 2.0 and PostgreSQL 9.1+
+----------------------------------------------------------------
-PostGIS 2 includes an extension for Postgres 9.1 that can be used to enable
+PostGIS 2 includes an extension for Postgres 9.1+ that can be used to enable
spatial functionality::
$ createdb <db name>
@@ -166,8 +166,8 @@ Managing the database
---------------------
To administer the database, you can either use the pgAdmin III program
-(:menuselection:`Start --> PostgreSQL 9.0 --> pgAdmin III`) or the
-SQL Shell (:menuselection:`Start --> PostgreSQL 9.0 --> SQL Shell`).
+(:menuselection:`Start --> PostgreSQL 9.x --> pgAdmin III`) or the
+SQL Shell (:menuselection:`Start --> PostgreSQL 9.x --> SQL Shell`).
For example, to create a ``geodjango`` spatial database and user, the following
may be executed from the SQL Shell as the ``postgres`` user::
diff --git a/docs/ref/contrib/gis/model-api.txt b/docs/ref/contrib/gis/model-api.txt
index 8c5274e6d3..81b619e338 100644
--- a/docs/ref/contrib/gis/model-api.txt
+++ b/docs/ref/contrib/gis/model-api.txt
@@ -195,7 +195,7 @@ details.
Geography Type
^^^^^^^^^^^^^^
-In PostGIS 1.5, the geography type was introduced -- it provides
+In PostGIS 1.5, the geography type was introduced -- it provides
native support for spatial features represented with geographic
coordinates (e.g., WGS84 longitude/latitude). [#fngeography]_
Unlike the plane used by a geometry type, the geography type uses a spherical
@@ -236,13 +236,12 @@ if we had an ``Address`` model with a ``ForeignKey`` to our ``Zipcode``
model::
from django.contrib.gis.db import models
- from django.contrib.localflavor.us.models import USStateField
class Address(models.Model):
num = models.IntegerField()
street = models.CharField(max_length=100)
city = models.CharField(max_length=100)
- state = USStateField()
+ state = models.CharField(max_length=2)
zipcode = models.ForeignKey(Zipcode)
objects = models.GeoManager()
diff --git a/docs/ref/contrib/gis/testing.txt b/docs/ref/contrib/gis/testing.txt
index 86979f0308..2a6dcef46f 100644
--- a/docs/ref/contrib/gis/testing.txt
+++ b/docs/ref/contrib/gis/testing.txt
@@ -140,6 +140,8 @@ with the rest of :ref:`Django's unit tests <running-unit-tests>`.
Run only GeoDjango tests
------------------------
+.. class:: django.contrib.gis.tests.GeoDjangoTestSuiteRunner
+
To run *only* the tests for GeoDjango, the :setting:`TEST_RUNNER`
setting must be changed to use the
:class:`~django.contrib.gis.tests.GeoDjangoTestSuiteRunner`::
diff --git a/docs/ref/contrib/gis/tutorial.txt b/docs/ref/contrib/gis/tutorial.txt
index 5000622ad4..56d90c8593 100644
--- a/docs/ref/contrib/gis/tutorial.txt
+++ b/docs/ref/contrib/gis/tutorial.txt
@@ -115,13 +115,12 @@ In addition, modify the :setting:`INSTALLED_APPS` setting to include
and ``world`` (your newly created application)::
INSTALLED_APPS = (
+ 'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
- 'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
- 'django.contrib.admin',
'django.contrib.gis',
'world'
)
@@ -226,7 +225,7 @@ model to represent this data::
class WorldBorder(models.Model):
# Regular Django fields corresponding to the attributes in the
- # world borders shapefile.
+ # world borders shapefile.
name = models.CharField(max_length=50)
area = models.IntegerField()
pop2005 = models.IntegerField('Population 2005')
@@ -236,13 +235,13 @@ model to represent this data::
un = models.IntegerField('United Nations Code')
region = models.IntegerField('Region Code')
subregion = models.IntegerField('Sub-Region Code')
- lon = models.FloatField()
- lat = models.FloatField()
+ lon = models.FloatField()
+ lat = models.FloatField()
- # GeoDjango-specific: a geometry field (MultiPolygonField), and
+ # GeoDjango-specific: a geometry field (MultiPolygonField), and
# overriding the default manager with a GeoManager instance.
- mpoly = models.MultiPolygonField()
- objects = models.GeoManager()
+ mpoly = models.MultiPolygonField()
+ objects = models.GeoManager()
# Returns the string representation of the model.
def __unicode__(self):
@@ -250,7 +249,7 @@ model to represent this data::
Please note two important things:
-1. The ``models`` module is imported from :mod:`django.contrib.gis.db`.
+1. The ``models`` module is imported from ``django.contrib.gis.db``.
2. You must override the model's default manager with
:class:`~django.contrib.gis.db.models.GeoManager` to perform spatial queries.
diff --git a/docs/ref/contrib/humanize.txt b/docs/ref/contrib/humanize.txt
index 57978288b1..aca6ed990d 100644
--- a/docs/ref/contrib/humanize.txt
+++ b/docs/ref/contrib/humanize.txt
@@ -100,10 +100,8 @@ Examples (when 'today' is 17 Feb 2007):
naturaltime
-----------
-.. versionadded:: 1.4
-
For datetime values, returns a string representing how many seconds,
-minutes or hours ago it was -- falling back to the :tfilter:`timesince`
+minutes or hours ago it was -- falling back to the :tfilter:`timesince`
format if the value is more than a day old. In case the datetime value is in
the future the return value will automatically use an appropriate phrase.
diff --git a/docs/ref/contrib/index.txt b/docs/ref/contrib/index.txt
index efe4393f64..e5cea01ead 100644
--- a/docs/ref/contrib/index.txt
+++ b/docs/ref/contrib/index.txt
@@ -27,13 +27,10 @@ those packages have.
comments/index
contenttypes
csrf
- databrowse
flatpages
formtools/index
gis/index
humanize
- localflavor
- markup
messages
redirects
sitemaps
@@ -56,7 +53,7 @@ auth
Django's authentication framework.
-See :doc:`/topics/auth`.
+See :doc:`/topics/auth/index`.
comments
========
@@ -123,22 +120,6 @@ A set of Django template filters useful for adding a "human touch" to data.
See the :doc:`humanize documentation </ref/contrib/humanize>`.
-localflavor
-===========
-
-A collection of various Django snippets that are useful only for a particular
-country or culture. For example, ``django.contrib.localflavor.us.forms``
-contains a ``USZipCodeField`` that you can use to validate U.S. zip codes.
-
-See the :doc:`localflavor documentation </ref/contrib/localflavor>`.
-
-markup
-======
-
-A collection of template filters that implement common markup languages
-
-See the :doc:`markup documentation </ref/contrib/markup>`.
-
messages
========
diff --git a/docs/ref/contrib/markup.txt b/docs/ref/contrib/markup.txt
deleted file mode 100644
index 9215c64f93..0000000000
--- a/docs/ref/contrib/markup.txt
+++ /dev/null
@@ -1,77 +0,0 @@
-=====================
-django.contrib.markup
-=====================
-
-.. module:: django.contrib.markup
- :synopsis: A collection of template filters that implement common markup languages.
-
-.. deprecated:: 1.5
- This module has been deprecated.
-
-Django provides template filters that implement the following markup
-languages:
-
-* ``textile`` -- implements `Textile`_ -- requires `PyTextile`_
-* ``markdown`` -- implements `Markdown`_ -- requires `Python-markdown`_ (>=2.1)
-* ``restructuredtext`` -- implements `reST (reStructured Text)`_
- -- requires `doc-utils`_
-
-In each case, the filter expects formatted markup as a string and
-returns a string representing the marked-up text. For example, the
-``textile`` filter converts text that is marked-up in Textile format
-to HTML.
-
-To activate these filters, add ``'django.contrib.markup'`` to your
-:setting:`INSTALLED_APPS` setting. Once you've done that, use
-``{% load markup %}`` in a template, and you'll have access to these filters.
-For more documentation, read the source code in
-:file:`django/contrib/markup/templatetags/markup.py`.
-
-.. warning::
-
- The output of markup filters is marked "safe" and will not be escaped when
- rendered in a template. Always be careful to sanitize your inputs and make
- sure you are not leaving yourself vulnerable to cross-site scripting or
- other types of attacks.
-
-.. _Textile: http://en.wikipedia.org/wiki/Textile_%28markup_language%29
-.. _Markdown: http://en.wikipedia.org/wiki/Markdown
-.. _reST (reStructured Text): http://en.wikipedia.org/wiki/ReStructuredText
-.. _PyTextile: http://loopcore.com/python-textile/
-.. _Python-markdown: http://pypi.python.org/pypi/Markdown
-.. _doc-utils: http://docutils.sf.net/
-
-reStructured Text
------------------
-
-When using the ``restructuredtext`` markup filter you can define a
-:setting:`RESTRUCTUREDTEXT_FILTER_SETTINGS` in your django settings to
-override the default writer settings. See the `restructuredtext writer
-settings`_ for details on what these settings are.
-
-.. warning::
-
- reStructured Text has features that allow raw HTML to be included, and that
- allow arbitrary files to be included. These can lead to XSS vulnerabilities
- and leaking of private information. It is your responsibility to check the
- features of this library and configure appropriately to avoid this. See the
- `Deploying Docutils Securely
- <http://docutils.sourceforge.net/docs/howto/security.html>`_ documentation.
-
-.. _restructuredtext writer settings: http://docutils.sourceforge.net/docs/user/config.html#html4css1-writer
-
-Markdown
---------
-
-The Python Markdown library supports options named "safe_mode" and
-"enable_attributes". Both relate to the security of the output. To enable both
-options in tandem, the markdown filter supports the "safe" argument::
-
- {{ markdown_content_var|markdown:"safe" }}
-
-.. warning::
-
- Versions of the Python-Markdown library prior to 2.1 do not support the
- optional disabling of attributes. This is a security flaw. Therefore,
- ``django.contrib.markup`` has dropped support for versions of
- Python-Markdown < 2.1 in Django 1.5.
diff --git a/docs/ref/contrib/messages.txt b/docs/ref/contrib/messages.txt
index 4fa733edb5..0a376bca18 100644
--- a/docs/ref/contrib/messages.txt
+++ b/docs/ref/contrib/messages.txt
@@ -78,8 +78,8 @@ Django provides three built-in storage classes:
:class:`~django.contrib.messages.storage.fallback.FallbackStorage` is the
default storage class. If it isn't suitable to your needs, you can select
-another storage class by setting `MESSAGE_STORAGE`_ to its full import path,
-for example::
+another storage class by setting :setting:`MESSAGE_STORAGE` to its full import
+path, for example::
MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage'
@@ -87,6 +87,8 @@ To write your own storage class, subclass the ``BaseStorage`` class in
``django.contrib.messages.storage.base`` and implement the ``_get`` and
``_store`` methods.
+.. _message-level:
+
Message levels
--------------
@@ -108,7 +110,7 @@ Constant Purpose
``ERROR`` An action was **not** successful or some other failure occurred
=========== ========
-The `MESSAGE_LEVEL`_ setting can be used to change the minimum recorded level
+The :setting:`MESSAGE_LEVEL` setting can be used to change the minimum recorded level
(or it can be `changed per request`_). Attempts to add messages of a level less
than this will be ignored.
@@ -136,7 +138,7 @@ Level Constant Tag
============== ===========
To change the default tags for a message level (either built-in or custom),
-set the `MESSAGE_TAGS`_ setting to a dictionary containing the levels
+set the :setting:`MESSAGE_TAGS` setting to a dictionary containing the levels
you wish to change. As this extends the default tags, you only need to provide
tags for the levels you wish to override::
@@ -149,6 +151,8 @@ tags for the levels you wish to override::
Using messages in views and templates
=====================================
+.. function:: add_message(request, level, message, extra_tags='', fail_silently=False)
+
Adding a message
----------------
@@ -166,6 +170,8 @@ used tags (which are usually represented as HTML classes for the message)::
messages.warning(request, 'Your account expires in three days.')
messages.error(request, 'Document deleted.')
+.. _message-displaying:
+
Displaying messages
-------------------
@@ -214,7 +220,7 @@ Level Constant Value
============== =====
If you need to identify the custom levels in your HTML or CSS, you need to
-provide a mapping via the `MESSAGE_TAGS`_ setting.
+provide a mapping via the :setting:`MESSAGE_TAGS` setting.
.. note::
If you are creating a reusable application, it is recommended to use
@@ -280,6 +286,53 @@ example::
use one of the ``add_message`` family of methods. It does not hide failures
that may occur for other reasons.
+Adding messages in Class Based Views
+------------------------------------
+
+.. versionadded:: 1.6
+
+.. class:: views.SuccessMessageMixin
+
+ Adds a success message attribute to
+ :class:`~django.views.generic.edit.FormView` based classes
+
+ .. method:: get_success_message(cleaned_data)
+
+ ``cleaned_data`` is the cleaned data from the form which is used for
+ string formatting
+
+**Example views.py**::
+
+ from django.contrib.messages.views import SuccessMessageMixin
+ from django.views.generic.edit import CreateView
+ from myapp.models import Author
+
+ class AuthorCreate(SuccessMessageMixin, CreateView):
+ model = Author
+ success_url = '/success/'
+ success_message = "%(name)s was created successfully"
+
+The cleaned data from the ``form`` is available for string interpolation using
+the ``%(field_name)s`` syntax. For ModelForms, if you need access to fields
+from the saved ``object`` override the
+:meth:`~django.contrib.messages.views.SuccessMessageMixin.get_success_message`
+method.
+
+**Example views.py for ModelForms**::
+
+ from django.contrib.messages.views import SuccessMessageMixin
+ from django.views.generic.edit import CreateView
+ from myapp.models import ComplicatedModel
+
+ class ComplicatedCreate(SuccessMessageMixin, CreateView):
+ model = ComplicatedModel
+ success_url = '/success/'
+ success_message = "%(calculated_field)s was created successfully"
+
+ def get_success_message(self, cleaned_data):
+ return self.success_message % dict(cleaned_data,
+ calculated_field=self.object.calculated_field)
+
Expiration of messages
======================
@@ -314,80 +367,10 @@ window/tab will have its own browsing context.
Settings
========
-A few :doc:`Django settings </ref/settings>` give you control over message
+A few :ref:`settings<settings-messages>` give you control over message
behavior:
-MESSAGE_LEVEL
--------------
-
-Default: ``messages.INFO``
-
-This sets the minimum message that will be saved in the message storage. See
-`Message levels`_ above for more details.
-
-.. admonition:: Important
-
- If you override ``MESSAGE_LEVEL`` in your settings file and rely on any of
- the built-in constants, you must import the constants module directly to
- avoid the potential for circular imports, e.g.::
-
- from django.contrib.messages import constants as message_constants
- MESSAGE_LEVEL = message_constants.DEBUG
-
- If desired, you may specify the numeric values for the constants directly
- according to the values in the above :ref:`constants table
- <message-level-constants>`.
-
-MESSAGE_STORAGE
----------------
-
-Default: ``'django.contrib.messages.storage.fallback.FallbackStorage'``
-
-Controls where Django stores message data. Valid values are:
-
-* ``'django.contrib.messages.storage.fallback.FallbackStorage'``
-* ``'django.contrib.messages.storage.session.SessionStorage'``
-* ``'django.contrib.messages.storage.cookie.CookieStorage'``
-
-See `Storage backends`_ for more details.
-
-MESSAGE_TAGS
-------------
-
-Default::
-
- {messages.DEBUG: 'debug',
- messages.INFO: 'info',
- messages.SUCCESS: 'success',
- messages.WARNING: 'warning',
- messages.ERROR: 'error',}
-
-This sets the mapping of message level to message tag, which is typically
-rendered as a CSS class in HTML. If you specify a value, it will extend
-the default. This means you only have to specify those values which you need
-to override. See `Displaying messages`_ above for more details.
-
-.. admonition:: Important
-
- If you override ``MESSAGE_TAGS`` in your settings file and rely on any of
- the built-in constants, you must import the ``constants`` module directly to
- avoid the potential for circular imports, e.g.::
-
- from django.contrib.messages import constants as message_constants
- MESSAGE_TAGS = {message_constants.INFO: ''}
-
- If desired, you may specify the numeric values for the constants directly
- according to the values in the above :ref:`constants table
- <message-level-constants>`.
-
-SESSION_COOKIE_DOMAIN
----------------------
-
-Default: ``None``
-
-The storage backends that use cookies -- ``CookieStorage`` and
-``FallbackStorage`` -- use the value of :setting:`SESSION_COOKIE_DOMAIN` in
-setting their cookies. See the :doc:`settings documentation </ref/settings>`
-for more information on how this works and why you might need to set it.
-
-.. _Django settings: ../settings/
+* :setting:`MESSAGE_LEVEL`
+* :setting:`MESSAGE_STORAGE`
+* :setting:`MESSAGE_TAGS`
+* :ref:`SESSION_COOKIE_DOMAIN<messages-session_cookie_domain>`
diff --git a/docs/ref/contrib/redirects.txt b/docs/ref/contrib/redirects.txt
index e34ba405f4..0c0cb2a3c2 100644
--- a/docs/ref/contrib/redirects.txt
+++ b/docs/ref/contrib/redirects.txt
@@ -13,11 +13,12 @@ Installation
To install the redirects app, follow these steps:
-1. Add ``'django.contrib.redirects'`` to your :setting:`INSTALLED_APPS`
- setting.
-2. Add ``'django.contrib.redirects.middleware.RedirectFallbackMiddleware'``
+1. Ensure that the ``django.contrib.sites`` framework
+ :ref:`is installed <enabling-the-sites-framework>`.
+2. Add ``'django.contrib.redirects'`` to your :setting:`INSTALLED_APPS` setting.
+3. Add ``'django.contrib.redirects.middleware.RedirectFallbackMiddleware'``
to your :setting:`MIDDLEWARE_CLASSES` setting.
-3. Run the command :djadmin:`manage.py syncdb <syncdb>`.
+4. Run the command :djadmin:`manage.py syncdb <syncdb>`.
How it works
============
diff --git a/docs/ref/contrib/sitemaps.txt b/docs/ref/contrib/sitemaps.txt
index ef6c64dc61..d37ee83378 100644
--- a/docs/ref/contrib/sitemaps.txt
+++ b/docs/ref/contrib/sitemaps.txt
@@ -49,6 +49,8 @@ loader can find the default templates.)
Initialization
==============
+.. function:: views.sitemap(request, sitemaps, section=None, template_name='sitemap.xml', mimetype='application/xml')
+
To activate sitemap generation on your Django site, add this line to your
:doc:`URLconf </topics/http/urls>`::
@@ -213,8 +215,6 @@ Sitemap class reference
.. attribute:: Sitemap.protocol
- .. versionadded:: 1.4
-
**Optional.**
This attribute defines the protocol (``'http'`` or ``'https'``) of the
@@ -242,9 +242,9 @@ The sitemap framework provides a couple convenience classes for common cases:
The :class:`django.contrib.sitemaps.GenericSitemap` class allows you to
create a sitemap by passing it a dictionary which has to contain at least
- a :data:`queryset` entry. This queryset will be used to generate the items
- of the sitemap. It may also have a :data:`date_field` entry that
- specifies a date field for objects retrieved from the :data:`queryset`.
+ a ``queryset`` entry. This queryset will be used to generate the items
+ of the sitemap. It may also have a ``date_field`` entry that
+ specifies a date field for objects retrieved from the ``queryset``.
This will be used for the :attr:`~Sitemap.lastmod` attribute in the
generated sitemap. You may also pass :attr:`~Sitemap.priority` and
:attr:`~Sitemap.changefreq` keyword arguments to the
@@ -256,7 +256,7 @@ Example
Here's an example of a :doc:`URLconf </topics/http/urls>` using both::
- from django.conf.urls import patterns, url, include
+ from django.conf.urls import patterns
from django.contrib.sitemaps import FlatPageSitemap, GenericSitemap
from blog.models import Entry
@@ -283,14 +283,16 @@ Here's an example of a :doc:`URLconf </topics/http/urls>` using both::
Creating a sitemap index
========================
+.. function:: views.index(request, sitemaps, template_name='sitemap_index.xml', mimetype='application/xml', sitemap_url_name='django.contrib.sitemaps.views.sitemap')
+
The sitemap framework also has the ability to create a sitemap index that
references individual sitemap files, one per each section defined in your
-:data:`sitemaps` dictionary. The only differences in usage are:
+``sitemaps`` dictionary. The only differences in usage are:
* You use two views in your URLconf: :func:`django.contrib.sitemaps.views.index`
and :func:`django.contrib.sitemaps.views.sitemap`.
* The :func:`django.contrib.sitemaps.views.sitemap` view should take a
- :data:`section` keyword argument.
+ ``section`` keyword argument.
Here's what the relevant URLconf lines would look like for the example above::
@@ -301,15 +303,13 @@ Here's what the relevant URLconf lines would look like for the example above::
This will automatically generate a :file:`sitemap.xml` file that references
both :file:`sitemap-flatpages.xml` and :file:`sitemap-blog.xml`. The
-:class:`~django.contrib.sitemaps.Sitemap` classes and the :data:`sitemaps`
+:class:`~django.contrib.sitemaps.Sitemap` classes and the ``sitemaps``
dict don't change at all.
You should create an index file if one of your sitemaps has more than 50,000
URLs. In this case, Django will automatically paginate the sitemap, and the
index will reflect that.
-.. versionadded:: 1.4
-
If you're not using the vanilla sitemap view -- for example, if it's wrapped
with a caching decorator -- you must name your sitemap view and pass
``sitemap_url_name`` to the index view::
@@ -346,29 +346,28 @@ parameter to the ``sitemap`` and ``index`` views via the URLconf::
)
-.. versionchanged:: 1.4
- In addition, these views also return
- :class:`~django.template.response.TemplateResponse`
- instances which allow you to easily customize the response data before
- rendering. For more details, see the
- :doc:`TemplateResponse documentation </ref/template-response>`.
+These views return :class:`~django.template.response.TemplateResponse`
+instances which allow you to easily customize the response data before
+rendering. For more details, see the :doc:`TemplateResponse documentation
+</ref/template-response>`.
Context variables
------------------
-When customizing the templates for the :func:`~django.contrib.sitemaps.views.index`
-and :func:`~django.contrib.sitemaps.views.sitemaps` views, you can rely on the
+When customizing the templates for the
+:func:`~django.contrib.sitemaps.views.index` and
+:func:`~django.contrib.sitemaps.views.sitemap` views, you can rely on the
following context variables.
Index
-----
-The variable :data:`sitemaps` is a list of absolute URLs to each of the sitemaps.
+The variable ``sitemaps`` is a list of absolute URLs to each of the sitemaps.
Sitemap
-------
-The variable :data:`urlset` is a list of URLs that should appear in the
+The variable ``urlset`` is a list of URLs that should appear in the
sitemap. Each URL exposes attributes as defined in the
:class:`~django.contrib.sitemaps.Sitemap` class:
@@ -378,8 +377,6 @@ sitemap. Each URL exposes attributes as defined in the
- ``location``
- ``priority``
-.. versionadded:: 1.4
-
The ``item`` attribute has been added for each URL to allow more flexible
customization of the templates, such as `Google news sitemaps`_. Assuming
Sitemap's :attr:`~Sitemap.items()` would return a list of items with
@@ -419,14 +416,14 @@ that: :func:`django.contrib.sitemaps.ping_google()`.
.. function:: ping_google
- :func:`ping_google` takes an optional argument, :data:`sitemap_url`,
+ :func:`ping_google` takes an optional argument, ``sitemap_url``,
which should be the absolute path to your site's sitemap (e.g.,
:file:`'/sitemap.xml'`). If this argument isn't provided,
:func:`ping_google` will attempt to figure out your
sitemap by performing a reverse looking in your URLconf.
:func:`ping_google` raises the exception
- :exc:`django.contrib.sitemaps.SitemapNotFound` if it cannot determine your
+ ``django.contrib.sitemaps.SitemapNotFound`` if it cannot determine your
sitemap URL.
.. admonition:: Register with Google first!
@@ -457,8 +454,8 @@ cron script, or some other scheduled task. The function makes an HTTP request
to Google's servers, so you may not want to introduce that network overhead
each time you call ``save()``.
-Pinging Google via `manage.py`
-------------------------------
+Pinging Google via ``manage.py``
+--------------------------------
.. django-admin:: ping_google
diff --git a/docs/ref/contrib/sites.txt b/docs/ref/contrib/sites.txt
index 7e5448b3d3..139a9b377f 100644
--- a/docs/ref/contrib/sites.txt
+++ b/docs/ref/contrib/sites.txt
@@ -246,14 +246,31 @@ To do this, you can use the sites framework. A simple example::
>>> 'http://%s%s' % (Site.objects.get_current().domain, obj.get_absolute_url())
'http://example.com/mymodel/objects/3/'
+.. _enabling-the-sites-framework:
-Default site and ``syncdb``
-===========================
+Enabling the sites framework
+============================
+
+.. versionchanged:: 1.6
+ In previous versions, the sites framework was enabled by default.
+
+To enable the sites framework, follow these steps:
+
+1. Add ``'django.contrib.sites'`` to your :setting:`INSTALLED_APPS`
+ setting.
+
+2. Define a :setting:`SITE_ID` setting::
+
+ SITE_ID = 1
+
+3. Run :djadmin:`syncdb`.
``django.contrib.sites`` registers a
:data:`~django.db.models.signals.post_syncdb` signal handler which creates a
-default site named ``example.com`` with the domain ``example.com``. For
-example, this site will be created after Django creates the test database.
+default site named ``example.com`` with the domain ``example.com``. This site
+will also be created after Django creates the test database. To set the
+correct name and domain for your project, you can use an :doc:`initial data
+fixture </howto/initial-data>`.
Caching the current ``Site`` object
===================================
diff --git a/docs/ref/contrib/staticfiles.txt b/docs/ref/contrib/staticfiles.txt
index 3a74797145..806d135deb 100644
--- a/docs/ref/contrib/staticfiles.txt
+++ b/docs/ref/contrib/staticfiles.txt
@@ -12,115 +12,22 @@ can easily be served in production.
.. seealso::
For an introduction to the static files app and some usage examples, see
- :doc:`/howto/static-files`.
+ :doc:`/howto/static-files/index`. For guidelines on deploying static files,
+ see :doc:`/howto/static-files/deployment`.
.. _staticfiles-settings:
Settings
========
-.. highlight:: python
-
-.. note::
-
- The following settings control the behavior of the staticfiles app.
-
-.. setting:: STATICFILES_DIRS
-
-STATICFILES_DIRS
-----------------
-
-Default: ``[]``
-
-This setting defines the additional locations the staticfiles app will traverse
-if the :class:`FileSystemFinder` finder is enabled, e.g. if you use the
-:djadmin:`collectstatic` or :djadmin:`findstatic` management command or use the
-static file serving view.
-
-This should be set to a list or tuple of strings that contain full paths to
-your additional files directory(ies) e.g.::
-
- STATICFILES_DIRS = (
- "/home/special.polls.com/polls/static",
- "/home/polls.com/polls/static",
- "/opt/webfiles/common",
- )
-
-Prefixes (optional)
-"""""""""""""""""""
-
-In case you want to refer to files in one of the locations with an additional
-namespace, you can **optionally** provide a prefix as ``(prefix, path)``
-tuples, e.g.::
-
- STATICFILES_DIRS = (
- # ...
- ("downloads", "/opt/webfiles/stats"),
- )
-
-Example:
-
-Assuming you have :setting:`STATIC_URL` set ``'/static/'``, the
-:djadmin:`collectstatic` management command would collect the "stats" files
-in a ``'downloads'`` subdirectory of :setting:`STATIC_ROOT`.
-
-This would allow you to refer to the local file
-``'/opt/webfiles/stats/polls_20101022.tar.gz'`` with
-``'/static/downloads/polls_20101022.tar.gz'`` in your templates, e.g.:
-
-.. code-block:: html+django
-
- <a href="{{ STATIC_URL }}downloads/polls_20101022.tar.gz">
-
-.. setting:: STATICFILES_STORAGE
-
-STATICFILES_STORAGE
--------------------
-
-Default: ``'django.contrib.staticfiles.storage.StaticFilesStorage'``
-
-The file storage engine to use when collecting static files with the
-:djadmin:`collectstatic` management command.
-
-.. versionadded:: 1.4
-
-A ready-to-use instance of the storage backend defined in this setting
-can be found at ``django.contrib.staticfiles.storage.staticfiles_storage``.
-
-For an example, see :ref:`staticfiles-from-cdn`.
-
-.. setting:: STATICFILES_FINDERS
-
-STATICFILES_FINDERS
--------------------
-
-Default::
-
- ("django.contrib.staticfiles.finders.FileSystemFinder",
- "django.contrib.staticfiles.finders.AppDirectoriesFinder")
+See :ref:`staticfiles settings <settings-staticfiles>` for details on the
+following settings:
-The list of finder backends that know how to find static files in
-various locations.
-
-The default will find files stored in the :setting:`STATICFILES_DIRS` setting
-(using :class:`django.contrib.staticfiles.finders.FileSystemFinder`) and in a
-``static`` subdirectory of each app (using
-:class:`django.contrib.staticfiles.finders.AppDirectoriesFinder`)
-
-One finder is disabled by default:
-:class:`django.contrib.staticfiles.finders.DefaultStorageFinder`. If added to
-your :setting:`STATICFILES_FINDERS` setting, it will look for static files in
-the default file storage as defined by the :setting:`DEFAULT_FILE_STORAGE`
-setting.
-
-.. note::
-
- When using the :class:`AppDirectoriesFinder` finder, make sure your apps
- can be found by staticfiles. Simply add the app to the
- :setting:`INSTALLED_APPS` setting of your site.
-
-Static file finders are currently considered a private interface, and this
-interface is thus undocumented.
+* :setting:`STATIC_ROOT`
+* :setting:`STATIC_URL`
+* :setting:`STATICFILES_DIRS`
+* :setting:`STATICFILES_STORAGE`
+* :setting:`STATICFILES_FINDERS`
Management Commands
===================
@@ -146,8 +53,6 @@ Files are searched by using the :setting:`enabled finders
:setting:`STATICFILES_DIRS` and in the ``'static'`` directory of apps
specified by the :setting:`INSTALLED_APPS` setting.
-.. versionadded:: 1.4
-
The :djadmin:`collectstatic` management command calls the
:meth:`~django.contrib.staticfiles.storage.StaticFilesStorage.post_process`
method of the :setting:`STATICFILES_STORAGE` after each run and passes
@@ -176,8 +81,6 @@ Some commonly used options are:
.. django-admin-option:: -c
.. django-admin-option:: --clear
- .. versionadded:: 1.4
-
Clear the existing files before trying to copy or link the original file.
.. django-admin-option:: -l
@@ -187,8 +90,6 @@ Some commonly used options are:
.. django-admin-option:: --no-post-process
- .. versionadded:: 1.4
-
Don't call the
:meth:`~django.contrib.staticfiles.storage.StaticFilesStorage.post_process`
method of the configured :setting:`STATICFILES_STORAGE` storage backend.
@@ -212,19 +113,29 @@ Searches for one or more relative paths with the enabled finders.
For example::
$ python manage.py findstatic css/base.css admin/js/core.js
- /home/special.polls.com/core/static/css/base.css
- /home/polls.com/core/static/css/base.css
- /home/polls.com/src/django/contrib/admin/media/js/core.js
+ Found 'css/base.css' here:
+ /home/special.polls.com/core/static/css/base.css
+ /home/polls.com/core/static/css/base.css
+ Found 'admin/js/core.js' here:
+ /home/polls.com/src/django/contrib/admin/media/js/core.js
By default, all matching locations are found. To only return the first match
for each relative path, use the ``--first`` option::
$ python manage.py findstatic css/base.css --first
- /home/special.polls.com/core/static/css/base.css
+ Found 'css/base.css' here:
+ /home/special.polls.com/core/static/css/base.css
This is a debugging aid; it'll show you exactly which static file will be
collected for a given path.
+By setting the :djadminopt:`--verbosity` flag to 0, you can suppress the extra
+output and just get the path names::
+
+ $ python manage.py findstatic css/base.css --verbosity 0
+ /home/special.polls.com/core/static/css/base.css
+ /home/polls.com/core/static/css/base.css
+
.. _staticfiles-runserver:
runserver
@@ -276,8 +187,6 @@ StaticFilesStorage
.. method:: post_process(paths, **options)
- .. versionadded:: 1.4
-
This method is called by the :djadmin:`collectstatic` management command
after each run and gets passed the local storages and paths of found
files as a dictionary, as well as the command line options.
@@ -291,8 +200,6 @@ CachedStaticFilesStorage
.. class:: storage.CachedStaticFilesStorage
- .. versionadded:: 1.4
-
A subclass of the :class:`~django.contrib.staticfiles.storage.StaticFilesStorage`
storage backend which caches the files it saves by appending the MD5 hash
of the file's content to the filename. For example, the file
@@ -370,8 +277,6 @@ static
.. templatetag:: staticfiles-static
-.. versionadded:: 1.4
-
Uses the configured :setting:`STATICFILES_STORAGE` storage to create the
full URL for the given relative path, e.g.:
@@ -422,9 +327,18 @@ files:
Static file development view
----------------------------
+.. currentmodule:: django.contrib.staticfiles
+
+The static files tools are mostly designed to help with getting static files
+successfully deployed into production. This usually means a separate,
+dedicated static file server, which is a lot of overhead to mess with when
+developing locally. Thus, the ``staticfiles`` app ships with a
+**quick and dirty helper view** that you can use to serve files locally in
+development.
+
.. highlight:: python
-.. function:: django.contrib.staticfiles.views.serve(request, path)
+.. function:: views.serve(request, path)
This view function serves static files in development.
@@ -436,6 +350,16 @@ This view function serves static files in development.
**insecure**. This is only intended for local development, and should
**never be used in production**.
+.. note::
+
+ To guess the served files' content types, this view relies on the
+ :py:mod:`mimetypes` module from the Python standard library, which itself
+ relies on the underlying platform's map files. If you find that this view
+ doesn't return proper content types for certain files, it is most likely
+ that the platform's map files need to be updated. This can be achieved, for
+ example, by installing or updating the ``mailcap`` package on a Red Hat
+ distribution, or ``mime-support`` on a Debian distribution.
+
This view is automatically enabled by :djadmin:`runserver` (with a
:setting:`DEBUG` setting set to ``True``). To use the view with a different
local development server, add the following snippet to the end of your
@@ -451,9 +375,10 @@ primary URL configuration::
Note, the beginning of the pattern (``r'^static/'``) should be your
:setting:`STATIC_URL` setting.
-Since this is a bit finicky, there's also a helper function that'll do this for you:
+Since this is a bit finicky, there's also a helper function that'll do this for
+you:
-.. function:: django.contrib.staticfiles.urls.staticfiles_urlpatterns()
+.. function:: urls.staticfiles_urlpatterns()
This will return the proper URL pattern for serving static files to your
already defined pattern list. Use it like this::
@@ -464,8 +389,18 @@ already defined pattern list. Use it like this::
urlpatterns += staticfiles_urlpatterns()
+This will inspect your :setting:`STATIC_URL` setting and wire up the view
+to serve static files accordingly. Don't forget to set the
+:setting:`STATICFILES_DIRS` setting appropriately to let
+``django.contrib.staticfiles`` know where to look for files in addition to
+files in app directories.
+
.. warning::
This helper function will only work if :setting:`DEBUG` is ``True``
and your :setting:`STATIC_URL` setting is neither empty nor a full
URL such as ``http://static.example.com/``.
+
+ That's because this view is **grossly inefficient** and probably
+ **insecure**. This is only intended for local development, and should
+ **never be used in production**.
diff --git a/docs/ref/contrib/syndication.txt b/docs/ref/contrib/syndication.txt
index 2418dba8ef..02159c415b 100644
--- a/docs/ref/contrib/syndication.txt
+++ b/docs/ref/contrib/syndication.txt
@@ -77,7 +77,7 @@ latest five news items::
To connect a URL to this feed, put an instance of the Feed object in
your :doc:`URLconf </topics/http/urls>`. For example::
- from django.conf.urls import patterns, url, include
+ from django.conf.urls import patterns
from myproject.feeds import LatestEntriesFeed
urlpatterns = patterns('',
@@ -137,6 +137,51 @@ into those elements.
See `a complex example`_ below that uses a description template.
+ There is also a way to pass additional information to title and description
+ templates, if you need to supply more than the two variables mentioned
+ before. You can provide your implementation of ``get_context_data`` method
+ in your Feed subclass. For example::
+
+ from mysite.models import Article
+ from django.contrib.syndication.views import Feed
+
+ class ArticlesFeed(Feed):
+ title = "My articles"
+ description_template = "feeds/articles.html"
+
+ def items(self):
+ return Article.objects.order_by('-pub_date')[:5]
+
+ def get_context_data(self, **kwargs):
+ context = super(ArticlesFeed, self).get_context_data(**kwargs)
+ context['foo'] = 'bar'
+ return context
+
+ And the template:
+
+ .. code-block:: html+django
+
+ Something about {{ foo }}: {{ obj.description }}
+
+ This method will be called once per each item in the list returned by
+ ``items()`` with the following keyword arguments:
+
+ * ``item``: the current item. For backward compatibility reasons, the name
+ of this context variable is ``{{ obj }}``.
+
+ * ``obj``: the object returned by ``get_object()``. By default this is not
+ exposed to the templates to avoid confusion with ``{{ obj }}`` (see above),
+ but you can use it in your implementation of ``get_context_data()``.
+
+ * ``site``: current site as described above.
+
+ * ``request``: current request.
+
+ The behavior of ``get_context_data()`` mimics that of
+ :ref:`generic views <adding-extra-context>` - you're supposed to call
+ ``super()`` to retrieve context data from parent class, add your data
+ and return the modified dictionary.
+
* To specify the contents of ``<link>``, you have two options. For each item
in ``items()``, Django first tries calling the
``item_link()`` method on the
@@ -321,7 +366,7 @@ Here's a full example::
And the accompanying URLconf::
- from django.conf.urls import patterns, url, include
+ from django.conf.urls import patterns
from myproject.feeds import RssSiteNewsFeed, AtomSiteNewsFeed
urlpatterns = patterns('',
@@ -334,7 +379,7 @@ And the accompanying URLconf::
Feed class reference
--------------------
-.. class:: django.contrib.syndication.views.Feed
+.. class:: views.Feed
This example illustrates all possible attributes and methods for a
:class:`~django.contrib.syndication.views.Feed` class::
@@ -599,6 +644,15 @@ This example illustrates all possible attributes and methods for a
item_description = 'A description of the item.' # Hard-coded description.
+ def get_context_data(self, **kwargs):
+ """
+ Returns a dictionary to use as extra context if either
+ description_template or item_template are used.
+
+ Default implementation preserves the old behavior
+ of using {'obj': item, 'site': current_site} as the context.
+ """
+
# ITEM LINK -- One of these three is required. The framework looks for
# them in this order.
@@ -624,6 +678,18 @@ This example illustrates all possible attributes and methods for a
Takes an item, as return by items(), and returns the item's ID.
"""
+ # ITEM_GUID_IS_PERMALINK -- The following method is optional. If
+ # provided, it sets the 'isPermaLink' attribute of an item's
+ # GUID element. This method is used only when 'item_guid' is
+ # specified.
+
+ def item_guid_is_permalink(self, obj):
+ """
+ Takes an item, as returned by items(), and returns a boolean.
+ """
+
+ item_guid_is_permalink = False # Hard coded value
+
# ITEM AUTHOR NAME -- One of the following three is optional. The
# framework looks for them in this order.
diff --git a/docs/ref/databases.txt b/docs/ref/databases.txt
index 352c0f4584..395abd90dd 100644
--- a/docs/ref/databases.txt
+++ b/docs/ref/databases.txt
@@ -11,27 +11,73 @@ This file describes some of the features that might be relevant to Django
usage. Of course, it is not intended as a replacement for server-specific
documentation or reference manuals.
-.. _postgresql-notes:
+General notes
+=============
-PostgreSQL notes
-================
+.. _persistent-database-connections:
+
+Persistent connections
+----------------------
+
+.. versionadded:: 1.6
+
+Persistent connections avoid the overhead of re-establishing a connection to
+the database in each request. By default, connections are kept open for up 10
+minutes — if not specified, :setting:`CONN_MAX_AGE` defaults to 600 seconds.
+
+Django 1.5 and earlier didn't have persistent connections. To restore the
+legacy behavior of closing the connection at the end of every request, set
+:setting:`CONN_MAX_AGE` to ``0``.
+
+For unlimited persistent connections, set :setting:`CONN_MAX_AGE` to ``None``.
+
+Connection management
+~~~~~~~~~~~~~~~~~~~~~
+
+Django opens a connection to the database when it first makes a database
+query. It keeps this connection open and reuses it in subsequent requests.
+Django closes the connection once it exceeds the maximum age defined by
+:setting:`CONN_MAX_AGE` or when it isn't usable any longer.
-.. versionchanged:: 1.4
+In detail, Django automatically opens a connection to the database whenever it
+needs one and doesn't have one already — either because this is the first
+connection, or because the previous connection was closed.
-Django supports PostgreSQL 8.2 and higher.
+At the beginning of each request, Django closes the connection if it has
+reached its maximum age. If your database terminates idle connections after
+some time, you should set :setting:`CONN_MAX_AGE` to a lower value, so that
+Django doesn't attempt to use a connection that has been terminated by the
+database server. (This problem may only affect very low traffic sites.)
-PostgreSQL 8.2 to 8.2.4
------------------------
+At the end of each request, Django closes the connection if it has reached its
+maximum age or if it is in an unrecoverable error state. If any database
+errors have occurred while processing the requests, Django checks whether the
+connection still works, and closes it if it doesn't. Thus, database errors
+affect at most one request; if the connection becomes unusable, the next
+request gets a fresh connection.
-The implementation of the population statistics aggregates ``STDDEV_POP`` and
-``VAR_POP`` that shipped with PostgreSQL 8.2 to 8.2.4 are `known to be
-faulty`_. Users of these releases of PostgreSQL are advised to upgrade to
-`Release 8.2.5`_ or later. Django will raise a ``NotImplementedError`` if you
-attempt to use the ``StdDev(sample=False)`` or ``Variance(sample=False)``
-aggregate with a database backend that falls within the affected release range.
+Caveats
+~~~~~~~
-.. _known to be faulty: http://archives.postgresql.org/pgsql-bugs/2007-07/msg00046.php
-.. _Release 8.2.5: http://www.postgresql.org/docs/devel/static/release-8-2-5.html
+Since each thread maintains its own connection, your database must support at
+least as many simultaneous connections as you have worker threads.
+
+Sometimes a database won't be accessed by the majority of your views, for
+example because it's the database of an external system, or thanks to caching.
+In such cases, you should set :setting:`CONN_MAX_AGE` to a lower value, or
+even ``0``, because it doesn't make sense to maintain a connection that's
+unlikely to be reused. This will help keep the number of simultaneous
+connections to this database small.
+
+The development server creates a new thread for each request it handles,
+negating the effect of persistent connections.
+
+.. _postgresql-notes:
+
+PostgreSQL notes
+================
+
+Django supports PostgreSQL 8.4 and higher.
PostgreSQL connection settings
-------------------------------
@@ -44,7 +90,8 @@ Optimizing PostgreSQL's configuration
Django needs the following parameters for its database connections:
- ``client_encoding``: ``'UTF8'``,
-- ``default_transaction_isolation``: ``'read committed'``,
+- ``default_transaction_isolation``: ``'read committed'`` by default,
+ or the value set in the connection options (see below),
- ``timezone``: ``'UTC'`` when :setting:`USE_TZ` is ``True``, value of
:setting:`TIME_ZONE` otherwise.
@@ -58,58 +105,57 @@ will do some additional queries to set these parameters.
.. _ALTER ROLE: http://www.postgresql.org/docs/current/interactive/sql-alterrole.html
-Transaction handling
----------------------
-
-:doc:`By default </topics/db/transactions>`, Django runs with an open
-transaction which it commits automatically when any built-in, data-altering
-model function is called. The PostgreSQL backends normally operate the same as
-any other Django backend in this respect.
-
.. _postgresql-autocommit-mode:
Autocommit mode
-~~~~~~~~~~~~~~~
+---------------
-If your application is particularly read-heavy and doesn't make many
-database writes, the overhead of a constantly open transaction can
-sometimes be noticeable. For those situations, you can configure Django
-to use *"autocommit"* behavior for the connection, meaning that each database
-operation will normally be in its own transaction, rather than having
-the transaction extend over multiple operations. In this case, you can
-still manually start a transaction if you're doing something that
-requires consistency across multiple database operations. The
-autocommit behavior is enabled by setting the ``autocommit`` key in
-the :setting:`OPTIONS` part of your database configuration in
-:setting:`DATABASES`::
+.. versionchanged:: 1.6
- 'OPTIONS': {
- 'autocommit': True,
+In previous versions of Django, database-level autocommit could be enabled by
+setting the ``autocommit`` key in the :setting:`OPTIONS` part of your database
+configuration in :setting:`DATABASES`::
+
+ DATABASES = {
+ # ...
+ 'OPTIONS': {
+ 'autocommit': True,
+ },
}
-In this configuration, Django still ensures that :ref:`delete()
-<topics-db-queries-delete>` and :ref:`update() <topics-db-queries-update>`
-queries run inside a single transaction, so that either all the affected
-objects are changed or none of them are.
+Since Django 1.6, autocommit is turned on by default. This configuration is
+ignored and can be safely removed.
+
+Isolation level
+---------------
+
+.. versionadded:: 1.6
+
+Like PostgreSQL itself, Django defaults to the ``READ COMMITTED`` `isolation
+level <postgresql-isolation-levels>`_. If you need a higher isolation level
+such as ``REPEATABLE READ`` or ``SERIALIZABLE``, set it in the
+:setting:`OPTIONS` part of your database configuration in
+:setting:`DATABASES`::
+
+ import psycopg2.extensions
-.. admonition:: This is database-level autocommit
+ DATABASES = {
+ # ...
+ 'OPTIONS': {
+ 'isolation_level': psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE,
+ },
+ }
+
+.. note::
- This functionality is not the same as the :ref:`autocommit
- <topics-db-transactions-autocommit>` decorator. That decorator is
- a Django-level implementation that commits automatically after
- data changing operations. The feature enabled using the
- :setting:`OPTIONS` option provides autocommit behavior at the
- database adapter level. It commits after *every* operation.
+ Under higher isolation levels, your application should be prepared to
+ handle exceptions raised on serialization failures. This option is
+ designed for advanced uses.
-If you are using this feature and performing an operation akin to delete or
-updating that requires multiple operations, you are strongly recommended to
-wrap you operations in manual transaction handling to ensure data consistency.
-You should also audit your existing code for any instances of this behavior
-before enabling this feature. It's faster, but it provides less automatic
-protection for multi-call operations.
+.. _postgresql-isolation-levels: http://www.postgresql.org/docs/current/static/transaction-iso.html
Indexes for ``varchar`` and ``text`` columns
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+--------------------------------------------
When specifying ``db_index=True`` on your model fields, Django typically
outputs a single ``CREATE INDEX`` statement. However, if the database type
@@ -120,7 +166,7 @@ for the column. The extra index is necessary to correctly perform
lookups that use the ``LIKE`` operator in their SQL, as is done with the
``contains`` and ``startswith`` lookup types.
-.. _PostgreSQL operator class: http://www.postgresql.org/docs/8.4/static/indexes-opclass.html
+.. _PostgreSQL operator class: http://www.postgresql.org/docs/current/static/indexes-opclass.html
.. _mysql-notes:
@@ -173,18 +219,6 @@ running ``syncdb``::
1005, "Can't create table '\\db_name\\.#sql-4a8_ab' (errno: 150)"
)
-.. versionchanged:: 1.4
-
-In previous versions of Django, fixtures with forward references (i.e.
-relations to rows that have not yet been inserted into the database) would fail
-to load when using the InnoDB storage engine. This was due to the fact that InnoDB
-deviates from the SQL standard by checking foreign key constraints immediately
-instead of deferring the check until the transaction is committed. This
-problem has been resolved in Django 1.4. Fixture data is now loaded with foreign key
-checks turned off; foreign key checks are then re-enabled when the data has
-finished loading, at which point the entire table is checked for invalid foreign
-key references and an `IntegrityError` is raised if any are found.
-
.. _storage engines: http://dev.mysql.com/doc/refman/5.5/en/storage-engines.html
.. _MyISAM: http://dev.mysql.com/doc/refman/5.5/en/myisam-storage-engine.html
.. _InnoDB: http://dev.mysql.com/doc/refman/5.5/en/innodb.html
@@ -207,6 +241,14 @@ required for full MySQL support in Django.
1.2.1p2 or newer, then delete the ``sets.py`` file in the MySQLdb
directory that was left by an earlier version.
+.. note::
+ There are known issues with the way MySQLdb converts date strings into
+ datetime objects. Specifically, date strings with value 0000-00-00 are valid for
+ MySQL but will be converted into None by MySQLdb.
+
+ This means you should be careful while using loaddata/dumpdata with rows
+ that may have 0000-00-00 values, as they will be converted to None.
+
.. _MySQLdb: http://sourceforge.net/projects/mysql-python
Creating your database
@@ -273,9 +315,9 @@ recommended solution.
Should you decide to use ``utf8_bin`` collation for some of your tables with
MySQLdb 1.2.1p2 or 1.2.2, you should still use ``utf8_collation_ci_swedish``
-(the default) collation for the :class:`django.contrib.sessions.models.Session`
+(the default) collation for the ``django.contrib.sessions.models.Session``
table (usually called ``django_session``) and the
-:class:`django.contrib.admin.models.LogEntry` table (usually called
+``django.contrib.admin.models.LogEntry`` table (usually called
``django_admin_log``). Those are the two standard tables that use
:class:`~django.db.models.TextField` internally.
@@ -377,8 +419,7 @@ Savepoints
Both the Django ORM and MySQL (when using the InnoDB :ref:`storage engine
<mysql-storage-engines>`) support database :ref:`savepoints
-<topics-db-transactions-savepoints>`, but this feature wasn't available in
-Django until version 1.4 when such supports was added.
+<topics-db-transactions-savepoints>`.
If you use the MyISAM storage engine please be aware of the fact that you will
receive database-generated errors if you try to use the :ref:`savepoint-related
diff --git a/docs/ref/django-admin.txt b/docs/ref/django-admin.txt
index 306db8439e..1d3f1b8d1d 100644
--- a/docs/ref/django-admin.txt
+++ b/docs/ref/django-admin.txt
@@ -25,9 +25,10 @@ copy ``django-admin.py`` to a location on your existing path or edit the
Environment...``) to point to its installed location.
Generally, when working on a single Django project, it's easier to use
-``manage.py``. Use ``django-admin.py`` with ``DJANGO_SETTINGS_MODULE``, or the
-``--settings`` command line option, if you need to switch between multiple
-Django settings files.
+``manage.py`` than ``django-admin.py``. If you need to switch between multiple
+Django settings files, use ``django-admin.py`` with
+:envvar:`DJANGO_SETTINGS_MODULE` or the :djadminopt:`--settings` command line
+option.
The command-line examples throughout this document use ``django-admin.py`` to
be consistent, but any example can use ``manage.py`` just as well.
@@ -107,12 +108,21 @@ compilemessages
Compiles .po files created with ``makemessages`` to .mo files for use with
the builtin gettext support. See :doc:`/topics/i18n/index`.
-Use the :djadminopt:`--locale` option to specify the locale to process.
-If not provided, all locales are processed.
+Use the :djadminopt:`--locale` option (or its shorter version ``-l``) to
+specify the locale(s) to process. If not provided, all locales are processed.
Example usage::
django-admin.py compilemessages --locale=pt_BR
+ django-admin.py compilemessages --locale=pt_BR --locale=fr
+ django-admin.py compilemessages -l pt_BR
+ django-admin.py compilemessages -l pt_BR -l fr
+ django-admin.py compilemessages --locale=pt_BR,fr
+ django-admin.py compilemessages -l pt_BR,fr
+
+.. versionchanged:: 1.6
+
+Added the ability to specify multiple locales.
createcachetable
----------------
@@ -159,8 +169,11 @@ example, the default settings don't define :setting:`ROOT_URLCONF`, so
:setting:`ROOT_URLCONF` is followed by ``"###"`` in the output of
``diffsettings``.
-Note that Django's default settings live in ``django/conf/global_settings.py``,
-if you're ever curious to see the full list of defaults.
+The :djadminopt:`--all` option may be provided to display all settings, even
+if they have Django's default value. Such settings are prefixed by ``"###"``.
+
+.. versionadded:: 1.6
+ The :djadminopt:`--all` option was added.
dumpdata <appname appname appname.Model ...>
--------------------------------------------
@@ -279,9 +292,24 @@ needed.
``inspectdb`` works with PostgreSQL, MySQL and SQLite. Foreign-key detection
only works in PostgreSQL and with certain types of MySQL tables.
+If your plan is that your Django application(s) modify data (i.e. edit, remove
+records and create new ones) in the existing database tables corresponding to
+any of the introspected models then one of the manual review and edit steps
+you need to perform on the resulting ``models.py`` file is to change the
+Python declaration of each one of these models to specify it is a
+:attr:`managed <django.db.models.Options.managed>` one.
+
+This servers as an explicit opt-in to give your nascent Django project write
+access to your precious data on a model by model basis.
+
The :djadminopt:`--database` option may be used to specify the
database to introspect.
+.. versionchanged:: 1.6
+
+The behavior by which introspected models are created as unmanaged ones is new
+in Django 1.6.
+
loaddata <fixture fixture ...>
------------------------------
@@ -292,6 +320,8 @@ Searches for and loads the contents of the named fixture into the database.
The :djadminopt:`--database` option can be used to specify the database
onto which the data will be loaded.
+.. django-admin-option:: --ignorenonexistent
+
.. versionadded:: 1.5
The :djadminopt:`--ignorenonexistent` option can be used to ignore fields that
@@ -420,11 +450,24 @@ Separate multiple extensions with commas or use -e or --extension multiple times
django-admin.py makemessages --locale=de --extension=html,txt --extension xml
-Use the :djadminopt:`--locale` option to specify the locale to process.
+Use the :djadminopt:`--locale` option (or its shorter version ``-l``) to
+specify the locale(s) to process.
Example usage::
django-admin.py makemessages --locale=pt_BR
+ django-admin.py makemessages --locale=pt_BR --locale=fr
+ django-admin.py makemessages -l pt_BR
+ django-admin.py makemessages -l pt_BR -l fr
+
+You can also use commas to separate multiple locales::
+
+ django-admin.py makemessages --locale=de,fr,pt_BR
+ django-admin.py makemessages -l de,fr,pt_BR
+
+.. versionchanged:: 1.6
+
+Added the ability to specify multiple locales.
.. django-admin-option:: --domain
@@ -466,12 +509,18 @@ several lines in language files.
.. django-admin-option:: --no-location
-.. versionadded:: 1.4
-
Use the ``--no-location`` option to not write '``#: filename:line``'
comment lines in language files. Note that using this option makes it harder
for technically skilled translators to understand each message's context.
+.. django-admin-option:: --keep-pot
+
+.. versionadded:: 1.6
+
+Use the ``--keep-pot`` option to prevent django from deleting the temporary
+.pot file it generates before creating the .po file. This is useful for
+debugging errors which may prevent the final language files from being created.
+
runfcgi [options]
-----------------
@@ -482,9 +531,8 @@ supports the FastCGI protocol. See the :doc:`FastCGI deployment documentation
</howto/deployment/fastcgi>` for details. Requires the Python FastCGI module from
`flup`_.
-.. versionadded:: 1.4
- Internally, this wraps the WSGI application object specified by the
- :setting:`WSGI_APPLICATION` setting.
+Internally, this wraps the WSGI application object specified by the
+:setting:`WSGI_APPLICATION` setting.
.. _flup: http://www.saddi.com/software/flup/
@@ -610,9 +658,8 @@ If you run this script as a user with normal privileges (recommended), you
might not have access to start a port on a low port number. Low port numbers
are reserved for the superuser (root).
-.. versionadded:: 1.4
- This server uses the WSGI application object specified by the
- :setting:`WSGI_APPLICATION` setting.
+This server uses the WSGI application object specified by the
+:setting:`WSGI_APPLICATION` setting.
DO NOT USE THIS SERVER IN A PRODUCTION SETTING. It has not gone through
security audits or performance tests. (And that's how it's gonna stay. We're in
@@ -658,11 +705,8 @@ Example usage::
.. django-admin-option:: --nothreading
-.. versionadded:: 1.4
-
-Since version 1.4, the development server is multithreaded by default.
-Use the ``--nothreading`` option to disable the use of threading in the
-development server.
+The development server is multithreaded by default. Use the ``--nothreading``
+option to disable the use of threading in the development server.
.. django-admin-option:: --ipv6, -6
@@ -718,7 +762,8 @@ Serving static files with the development server
By default, the development server doesn't serve any static files for your site
(such as CSS files, images, things under :setting:`MEDIA_URL` and so forth). If
-you want to configure Django to serve static media, read :doc:`/howto/static-files`.
+you want to configure Django to serve static media, read
+:doc:`/howto/static-files/index`.
shell
-----
@@ -754,6 +799,18 @@ bpython::
.. _IPython: http://ipython.scipy.org/
.. _bpython: http://bpython-interpreter.org/
+When the "plain" Python interactive interpreter starts (be it because
+``--plain`` was specified or because no other interactive interface is
+available) it reads the script pointed to by the :envvar:`PYTHONSTARTUP`
+environment variable and the ``~/.pythonrc.py`` script. If you don't wish this
+behavior you can use the ``--no-startup`` option. e.g.::
+
+ django-admin.py shell --plain --no-startup
+
+.. versionadded:: 1.6
+
+The ``--no-startup`` option was added in Django 1.6.
+
sql <appname appname ...>
-------------------------
@@ -811,6 +868,18 @@ Note that the order in which the SQL files are processed is undefined.
The :djadminopt:`--database` option can be used to specify the database for
which to print the SQL.
+sqldropindexes <appname appname ...>
+------------------------------------
+
+.. django-admin:: sqldropindexes
+
+.. versionadded:: 1.6
+
+Prints the DROP INDEX SQL statements for the given app name(s).
+
+The :djadminopt:`--database` option can be used to specify the database for
+which to print the SQL.
+
sqlflush
--------
@@ -856,8 +925,6 @@ startapp <appname> [destination]
Creates a Django app directory structure for the given app name in the current
directory or the given destination.
-.. versionchanged:: 1.4
-
By default the directory created contains a ``models.py`` file and other app
template files. (See the `source`_ for more details.) If only the app
name is given, the app directory will be created in the current working
@@ -871,7 +938,8 @@ For example::
django-admin.py startapp myapp /Users/jezdez/Code/myapp
-.. versionadded:: 1.4
+.. _custom-app-and-project-templates:
+
.. django-admin-option:: --template
With the ``--template`` option, you can use a custom app template by providing
@@ -893,8 +961,6 @@ zip files, you can use a URL like::
django-admin.py startapp --template=https://github.com/githubuser/django-app-template/archive/master.zip myapp
-.. versionadded:: 1.4
-
When Django copies the app template files, it also renders certain files
through the template engine: the files whose extensions match the
``--extension`` option (``py`` by default) and the files whose names are passed
@@ -905,6 +971,7 @@ with the ``--name`` option. The :class:`template context
options)
- ``app_name`` -- the app name as passed to the command
- ``app_directory`` -- the full path of the newly created app
+- ``docs_version`` -- the version of the documentation: ``'dev'`` or ``'1.x'``
.. _render_warning:
@@ -929,8 +996,6 @@ startproject <projectname> [destination]
Creates a Django project directory structure for the given project name in
the current directory or the given destination.
-.. versionchanged:: 1.4
-
By default, the new directory contains ``manage.py`` and a project package
(containing a ``settings.py`` and other files). See the `template source`_ for
details.
@@ -947,8 +1012,6 @@ For example::
django-admin.py startproject myproject /Users/jezdez/Code/myproject_repo
-.. versionadded:: 1.4
-
As with the :djadmin:`startapp` command, the ``--template`` option lets you
specify a directory, file path or URL of a custom project template. See the
:djadmin:`startapp` documentation for details of supported project template
@@ -974,10 +1037,12 @@ through the template engine: the files whose extensions match the
with the ``--name`` option. The :class:`template context
<django.template.Context>` used is:
-- Any option passed to the startproject command
+- Any option passed to the startapp command (among the command's supported
+ options)
- ``project_name`` -- the project name as passed to the command
- ``project_directory`` -- the full path of the newly created project
- ``secret_key`` -- a random key for the :setting:`SECRET_KEY` setting
+- ``docs_version`` -- the version of the documentation: ``'dev'`` or ``'1.x'``
Please also see the :ref:`rendering warning <render_warning>` as mentioned
for :djadmin:`startapp`.
@@ -1036,7 +1101,7 @@ test <app or test identifier>
.. django-admin:: test
-Runs tests for all installed models. See :doc:`/topics/testing` for more
+Runs tests for all installed models. See :doc:`/topics/testing/index` for more
information.
.. django-admin-option:: --failfast
@@ -1044,14 +1109,12 @@ information.
The ``--failfast`` option can be used to stop running tests and report the
failure immediately after a test fails.
-.. versionadded:: 1.4
.. django-admin-option:: --testrunner
The ``--testrunner`` option can be used to control the test runner class that
is used to execute tests. If this value is provided, it overrides the value
provided by the :setting:`TEST_RUNNER` setting.
-.. versionadded:: 1.4
.. django-admin-option:: --liveserver
The ``--liveserver`` option can be used to override the default address where
@@ -1072,7 +1135,7 @@ For example, this command::
...would perform the following steps:
-1. Create a test database, as described in :doc:`/topics/testing`.
+1. Create a test database, as described in :ref:`the-test-database`.
2. Populate the test database with fixture data from the given fixtures.
(For more on fixtures, see the documentation for ``loaddata`` above.)
3. Runs the Django development server (as in ``runserver``), pointed at
@@ -1080,7 +1143,7 @@ For example, this command::
This is useful in a number of ways:
-* When you're writing :doc:`unit tests </topics/testing>` of how your views
+* When you're writing :doc:`unit tests </topics/testing/overview>` of how your views
act with certain fixture data, you can use ``testserver`` to interact with
the views in a Web browser, manually.
@@ -1145,15 +1208,13 @@ changepassword
.. django-admin:: changepassword
This command is only available if Django's :doc:`authentication system
-</topics/auth>` (``django.contrib.auth``) is installed.
+</topics/auth/index>` (``django.contrib.auth``) is installed.
Allows changing a user's password. It prompts you to enter twice the password of
the user given as parameter. If they both match, the new password will be
changed immediately. If you do not supply a user, the command will attempt to
change the password whose username matches the current user.
-.. versionadded:: 1.4
-
Use the ``--database`` option to specify the database to query for the user. If
it's not supplied, Django will use the ``default`` database.
@@ -1167,7 +1228,7 @@ createsuperuser
.. django-admin:: createsuperuser
This command is only available if Django's :doc:`authentication system
-</topics/auth>` (``django.contrib.auth``) is installed.
+</topics/auth/index>` (``django.contrib.auth``) is installed.
Creates a superuser account (a user who has all permissions). This is
useful if you need to create an initial superuser account but did not
@@ -1187,8 +1248,6 @@ using the ``--username`` and ``--email`` arguments on the command
line. If either of those is not supplied, ``createsuperuser`` will prompt for
it when running interactively.
-.. versionadded:: 1.4
-
Use the ``--database`` option to specify the database into which the superuser
object will be saved.
@@ -1233,7 +1292,7 @@ collectstatic
~~~~~~~~~~~~~
This command is only available if the :doc:`static files application
-</howto/static-files>` (``django.contrib.staticfiles``) is installed.
+</howto/static-files/index>` (``django.contrib.staticfiles``) is installed.
Please refer to its :djadmin:`description <collectstatic>` in the
:doc:`staticfiles </ref/contrib/staticfiles>` documentation.
@@ -1242,7 +1301,7 @@ findstatic
~~~~~~~~~~
This command is only available if the :doc:`static files application
-</howto/static-files>` (``django.contrib.staticfiles``) is installed.
+</howto/static-files/index>` (``django.contrib.staticfiles``) is installed.
Please refer to its :djadmin:`description <findstatic>` in the :doc:`staticfiles
</ref/contrib/staticfiles>` documentation.
@@ -1289,8 +1348,13 @@ Example usage::
django-admin.py syncdb --traceback
By default, ``django-admin.py`` will show a simple error message whenever an
-error occurs. If you specify ``--traceback``, ``django-admin.py`` will
-output a full stack trace whenever an exception is raised.
+:class:`~django.core.management.CommandError` occurs, but a full stack trace
+for any other exception. If you specify ``--traceback``, ``django-admin.py``
+will also output a full stack trace when a ``CommandError`` is raised.
+
+.. versionchanged:: 1.6
+ Previously, Django didn't show a full stack trace by default for exceptions
+ other than ``CommandError``.
.. django-admin-option:: --verbosity
@@ -1324,7 +1388,7 @@ For example, to dump data from the database with the alias ``master``::
.. django-admin-option:: --exclude
Exclude a specific application from the applications whose contents is
-output. For example, to specifically exclude the `auth` application from
+output. For example, to specifically exclude the ``auth`` application from
the output of dumpdata, you would call::
django-admin.py dumpdata --exclude=auth
diff --git a/docs/ref/exceptions.txt b/docs/ref/exceptions.txt
index e91a5dd85e..93bb9ed251 100644
--- a/docs/ref/exceptions.txt
+++ b/docs/ref/exceptions.txt
@@ -119,18 +119,43 @@ NoReverseMatch
Database Exceptions
===================
-Django wraps the standard database exceptions :exc:`DatabaseError` and
-:exc:`IntegrityError` so that your Django code has a guaranteed common
-implementation of these classes. These database exceptions are
-provided in :mod:`django.db`.
+Django wraps the standard database exceptions so that your Django code has a
+guaranteed common implementation of these classes. These database exceptions
+are provided in :mod:`django.db`.
+.. exception:: Error
+.. exception:: InterfaceError
.. exception:: DatabaseError
+.. exception:: DataError
+.. exception:: OperationalError
.. exception:: IntegrityError
+.. exception:: InternalError
+.. exception:: ProgrammingError
+.. exception:: NotSupportedError
The Django wrappers for database exceptions behave exactly the same as
the underlying database exceptions. See :pep:`249`, the Python Database API
Specification v2.0, for further information.
+.. versionchanged:: 1.6
+ Previous version of Django only wrapped ``DatabaseError`` and
+ ``IntegrityError``.
+
+.. exception:: models.ProtectedError
+
+Raised to prevent deletion of referenced objects when using
+:attr:`django.db.models.PROTECT`. Subclass of :exc:`IntegrityError`.
+
+.. currentmodule:: django.http
+
+Http Exceptions
+===============
+
+.. exception:: UnreadablePostError
+
+ The :exc:`UnreadablePostError` is raised when a user cancels an upload.
+ It is available from :mod:`django.http`.
+
.. currentmodule:: django.db.transaction
Transaction Exceptions
diff --git a/docs/ref/files/file.txt b/docs/ref/files/file.txt
index ada614df45..7562f9b6bf 100644
--- a/docs/ref/files/file.txt
+++ b/docs/ref/files/file.txt
@@ -14,7 +14,7 @@ The ``File`` Class
The :class:`File` is a thin wrapper around Python's built-in file object
with some Django-specific additions. Internally, Django uses this class
any time it needs to represent a file.
-
+
:class:`File` objects have the following attributes and methods:
.. attribute:: name
@@ -148,7 +148,7 @@ below) will also have a couple of extra methods:
Note that the ``content`` argument must be an instance of either
:class:`File` or of a subclass of :class:`File`, such as
- :class:`ContentFile`.
+ :class:`~django.core.files.base.ContentFile`.
.. method:: File.delete([save=True])
diff --git a/docs/ref/files/storage.txt b/docs/ref/files/storage.txt
index f9bcf9b61e..b9742514ea 100644
--- a/docs/ref/files/storage.txt
+++ b/docs/ref/files/storage.txt
@@ -38,7 +38,7 @@ The FileSystemStorage Class
.. note::
- The :class:`FileSystemStorage.delete` method will not raise
+ The ``FileSystemStorage.delete()`` method will not raise
raise an exception if the given file name does not exist.
The Storage Class
@@ -66,7 +66,7 @@ The Storage Class
.. method:: delete(name)
Deletes the file referenced by ``name``. If deletion is not supported
- on the targest storage system this will raise ``NotImplementedError``
+ on the target storage system this will raise ``NotImplementedError``
instead
.. method:: exists(name)
diff --git a/docs/ref/forms/api.txt b/docs/ref/forms/api.txt
index dffef314b7..34ed2e493e 100644
--- a/docs/ref/forms/api.txt
+++ b/docs/ref/forms/api.txt
@@ -2,9 +2,7 @@
The Forms API
=============
-.. module:: django.forms.forms
-
-.. currentmodule:: django.forms
+.. module:: django.forms
.. admonition:: About this document
@@ -150,11 +148,11 @@ it's not necessary to include every field in your form. For example::
These values are only displayed for unbound forms, and they're not used as
fallback values if a particular value isn't provided.
-Note that if a :class:`~django.forms.fields.Field` defines
-:attr:`~Form.initial` *and* you include ``initial`` when instantiating the
-``Form``, then the latter ``initial`` will have precedence. In this example,
-``initial`` is provided both at the field level and at the form instance level,
-and the latter gets precedence::
+Note that if a :class:`~django.forms.Field` defines :attr:`~Form.initial` *and*
+you include ``initial`` when instantiating the ``Form``, then the latter
+``initial`` will have precedence. In this example, ``initial`` is provided both
+at the field level and at the form instance level, and the latter gets
+precedence::
>>> class CommentForm(forms.Form):
... name = forms.CharField(initial='class')
@@ -163,7 +161,7 @@ and the latter gets precedence::
>>> f = CommentForm(initial={'name': 'instance'}, auto_id=False)
>>> print(f)
<tr><th>Name:</th><td><input type="text" name="name" value="instance" /></td></tr>
- <tr><th>Url:</th><td><input type="text" name="url" /></td></tr>
+ <tr><th>Url:</th><td><input type="url" name="url" /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
Accessing "clean" data
@@ -272,7 +270,7 @@ simply ``print`` it::
>>> print(f)
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" /></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr>
- <tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" /></td></tr>
+ <tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" /></td></tr>
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr>
If the form is bound to data, the HTML output will include that data
@@ -289,7 +287,7 @@ include ``checked="checked"`` if appropriate::
>>> print(f)
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" value="hello" /></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" value="Hi there" /></td></tr>
- <tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" value="foo@example.com" /></td></tr>
+ <tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" value="foo@example.com" /></td></tr>
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" checked="checked" /></td></tr>
This default output is a two-column HTML table, with a ``<tr>`` for each field.
@@ -299,8 +297,9 @@ Notice the following:
``</table>`` tags, nor does it include the ``<form>`` and ``</form>``
tags or an ``<input type="submit">`` tag. It's your job to do that.
-* Each field type has a default HTML representation. ``CharField`` and
- ``EmailField`` are represented by an ``<input type="text">``.
+* Each field type has a default HTML representation. ``CharField`` is
+ represented by an ``<input type="text">`` and ``EmailField`` by an
+ ``<input type="email">``.
``BooleanField`` is represented by an ``<input type="checkbox">``. Note
these are merely sensible defaults; you can specify which HTML to use for
a given field by using widgets, which we'll explain shortly.
@@ -337,7 +336,7 @@ a form object, and each rendering method returns a Unicode object.
>>> print(f.as_p())
<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></p>
<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></p>
- <p><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" /></p>
+ <p><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" /></p>
<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></p>
``as_ul()``
@@ -352,11 +351,11 @@ a form object, and each rendering method returns a Unicode object.
>>> f = ContactForm()
>>> f.as_ul()
- u'<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></li>\n<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></li>\n<li><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" /></li>\n<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></li>'
+ u'<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></li>\n<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></li>\n<li><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" /></li>\n<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></li>'
>>> print(f.as_ul())
<li><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" /></li>
<li><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" /></li>
- <li><label for="id_sender">Sender:</label> <input type="text" name="sender" id="id_sender" /></li>
+ <li><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" /></li>
<li><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></li>
``as_table()``
@@ -370,16 +369,19 @@ a form object, and each rendering method returns a Unicode object.
>>> f = ContactForm()
>>> f.as_table()
- u'<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" /></td></tr>\n<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr>\n<tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" /></td></tr>\n<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr>'
+ u'<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" /></td></tr>\n<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr>\n<tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" /></td></tr>\n<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr>'
>>> print(f.as_table())
<tr><th><label for="id_subject">Subject:</label></th><td><input id="id_subject" type="text" name="subject" maxlength="100" /></td></tr>
<tr><th><label for="id_message">Message:</label></th><td><input type="text" name="message" id="id_message" /></td></tr>
- <tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" /></td></tr>
+ <tr><th><label for="id_sender">Sender:</label></th><td><input type="email" name="sender" id="id_sender" /></td></tr>
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr>
Styling required or erroneous form rows
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.. attribute:: Form.error_css_class
+.. attribute:: Form.required_css_class
+
It's pretty common to style form rows and fields that are required or have
errors. For example, you might want to present required form rows in bold and
highlight errors in red.
@@ -430,17 +432,17 @@ tags nor ``id`` attributes::
>>> print(f.as_table())
<tr><th>Subject:</th><td><input type="text" name="subject" maxlength="100" /></td></tr>
<tr><th>Message:</th><td><input type="text" name="message" /></td></tr>
- <tr><th>Sender:</th><td><input type="text" name="sender" /></td></tr>
+ <tr><th>Sender:</th><td><input type="email" name="sender" /></td></tr>
<tr><th>Cc myself:</th><td><input type="checkbox" name="cc_myself" /></td></tr>
>>> print(f.as_ul())
<li>Subject: <input type="text" name="subject" maxlength="100" /></li>
<li>Message: <input type="text" name="message" /></li>
- <li>Sender: <input type="text" name="sender" /></li>
+ <li>Sender: <input type="email" name="sender" /></li>
<li>Cc myself: <input type="checkbox" name="cc_myself" /></li>
>>> print(f.as_p())
<p>Subject: <input type="text" name="subject" maxlength="100" /></p>
<p>Message: <input type="text" name="message" /></p>
- <p>Sender: <input type="text" name="sender" /></p>
+ <p>Sender: <input type="email" name="sender" /></p>
<p>Cc myself: <input type="checkbox" name="cc_myself" /></p>
If ``auto_id`` is set to ``True``, then the form output *will* include
@@ -451,17 +453,17 @@ field::
>>> print(f.as_table())
<tr><th><label for="subject">Subject:</label></th><td><input id="subject" type="text" name="subject" maxlength="100" /></td></tr>
<tr><th><label for="message">Message:</label></th><td><input type="text" name="message" id="message" /></td></tr>
- <tr><th><label for="sender">Sender:</label></th><td><input type="text" name="sender" id="sender" /></td></tr>
+ <tr><th><label for="sender">Sender:</label></th><td><input type="email" name="sender" id="sender" /></td></tr>
<tr><th><label for="cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="cc_myself" /></td></tr>
>>> print(f.as_ul())
<li><label for="subject">Subject:</label> <input id="subject" type="text" name="subject" maxlength="100" /></li>
<li><label for="message">Message:</label> <input type="text" name="message" id="message" /></li>
- <li><label for="sender">Sender:</label> <input type="text" name="sender" id="sender" /></li>
+ <li><label for="sender">Sender:</label> <input type="email" name="sender" id="sender" /></li>
<li><label for="cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="cc_myself" /></li>
>>> print(f.as_p())
<p><label for="subject">Subject:</label> <input id="subject" type="text" name="subject" maxlength="100" /></p>
<p><label for="message">Message:</label> <input type="text" name="message" id="message" /></p>
- <p><label for="sender">Sender:</label> <input type="text" name="sender" id="sender" /></p>
+ <p><label for="sender">Sender:</label> <input type="email" name="sender" id="sender" /></p>
<p><label for="cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="cc_myself" /></p>
If ``auto_id`` is set to a string containing the format character ``'%s'``,
@@ -474,17 +476,17 @@ attributes based on the format string. For example, for a format string
>>> print(f.as_table())
<tr><th><label for="id_for_subject">Subject:</label></th><td><input id="id_for_subject" type="text" name="subject" maxlength="100" /></td></tr>
<tr><th><label for="id_for_message">Message:</label></th><td><input type="text" name="message" id="id_for_message" /></td></tr>
- <tr><th><label for="id_for_sender">Sender:</label></th><td><input type="text" name="sender" id="id_for_sender" /></td></tr>
+ <tr><th><label for="id_for_sender">Sender:</label></th><td><input type="email" name="sender" id="id_for_sender" /></td></tr>
<tr><th><label for="id_for_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></td></tr>
>>> print(f.as_ul())
<li><label for="id_for_subject">Subject:</label> <input id="id_for_subject" type="text" name="subject" maxlength="100" /></li>
<li><label for="id_for_message">Message:</label> <input type="text" name="message" id="id_for_message" /></li>
- <li><label for="id_for_sender">Sender:</label> <input type="text" name="sender" id="id_for_sender" /></li>
+ <li><label for="id_for_sender">Sender:</label> <input type="email" name="sender" id="id_for_sender" /></li>
<li><label for="id_for_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></li>
>>> print(f.as_p())
<p><label for="id_for_subject">Subject:</label> <input id="id_for_subject" type="text" name="subject" maxlength="100" /></p>
<p><label for="id_for_message">Message:</label> <input type="text" name="message" id="id_for_message" /></p>
- <p><label for="id_for_sender">Sender:</label> <input type="text" name="sender" id="id_for_sender" /></p>
+ <p><label for="id_for_sender">Sender:</label> <input type="email" name="sender" id="id_for_sender" /></p>
<p><label for="id_for_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></p>
If ``auto_id`` is set to any other true value -- such as a string that doesn't
@@ -500,13 +502,13 @@ entirely, using the ``label_suffix`` parameter::
>>> print(f.as_ul())
<li><label for="id_for_subject">Subject</label> <input id="id_for_subject" type="text" name="subject" maxlength="100" /></li>
<li><label for="id_for_message">Message</label> <input type="text" name="message" id="id_for_message" /></li>
- <li><label for="id_for_sender">Sender</label> <input type="text" name="sender" id="id_for_sender" /></li>
+ <li><label for="id_for_sender">Sender</label> <input type="email" name="sender" id="id_for_sender" /></li>
<li><label for="id_for_cc_myself">Cc myself</label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></li>
>>> f = ContactForm(auto_id='id_for_%s', label_suffix=' ->')
>>> print(f.as_ul())
<li><label for="id_for_subject">Subject -></label> <input id="id_for_subject" type="text" name="subject" maxlength="100" /></li>
<li><label for="id_for_message">Message -></label> <input type="text" name="message" id="id_for_message" /></li>
- <li><label for="id_for_sender">Sender -></label> <input type="text" name="sender" id="id_for_sender" /></li>
+ <li><label for="id_for_sender">Sender -></label> <input type="email" name="sender" id="id_for_sender" /></li>
<li><label for="id_for_cc_myself">Cc myself -></label> <input type="checkbox" name="cc_myself" id="id_for_cc_myself" /></li>
Note that the label suffix is added only if the last character of the
@@ -538,19 +540,19 @@ method you're using::
>>> print(f.as_table())
<tr><th>Subject:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="subject" maxlength="100" /></td></tr>
<tr><th>Message:</th><td><input type="text" name="message" value="Hi there" /></td></tr>
- <tr><th>Sender:</th><td><ul class="errorlist"><li>Enter a valid email address.</li></ul><input type="text" name="sender" value="invalid email address" /></td></tr>
+ <tr><th>Sender:</th><td><ul class="errorlist"><li>Enter a valid email address.</li></ul><input type="email" name="sender" value="invalid email address" /></td></tr>
<tr><th>Cc myself:</th><td><input checked="checked" type="checkbox" name="cc_myself" /></td></tr>
>>> print(f.as_ul())
<li><ul class="errorlist"><li>This field is required.</li></ul>Subject: <input type="text" name="subject" maxlength="100" /></li>
<li>Message: <input type="text" name="message" value="Hi there" /></li>
- <li><ul class="errorlist"><li>Enter a valid email address.</li></ul>Sender: <input type="text" name="sender" value="invalid email address" /></li>
+ <li><ul class="errorlist"><li>Enter a valid email address.</li></ul>Sender: <input type="email" name="sender" value="invalid email address" /></li>
<li>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></li>
>>> print(f.as_p())
<p><ul class="errorlist"><li>This field is required.</li></ul></p>
<p>Subject: <input type="text" name="subject" maxlength="100" /></p>
<p>Message: <input type="text" name="message" value="Hi there" /></p>
<p><ul class="errorlist"><li>Enter a valid email address.</li></ul></p>
- <p>Sender: <input type="text" name="sender" value="invalid email address" /></p>
+ <p>Sender: <input type="email" name="sender" value="invalid email address" /></p>
<p>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></p>
Customizing the error list format
@@ -573,7 +575,7 @@ pass that in at construction time::
<p>Subject: <input type="text" name="subject" maxlength="100" /></p>
<p>Message: <input type="text" name="message" value="Hi there" /></p>
<div class="errorlist"><div class="error">Enter a valid email address.</div></div>
- <p>Sender: <input type="text" name="sender" value="invalid email address" /></p>
+ <p>Sender: <input type="email" name="sender" value="invalid email address" /></p>
<p>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></p>
More granular output
@@ -587,24 +589,24 @@ lazy developers -- they're not the only way a form object can be displayed.
Used to display HTML or access attributes for a single field of a
:class:`Form` instance.
- The :meth:`__unicode__` and :meth:`__str__` methods of this object displays
+ The ``__unicode__()`` and ``__str__()`` methods of this object displays
the HTML for this field.
To retrieve a single ``BoundField``, use dictionary lookup syntax on your form
using the field's name as the key::
- >>> form = ContactForm()
- >>> print(form['subject'])
- <input id="id_subject" type="text" name="subject" maxlength="100" />
+ >>> form = ContactForm()
+ >>> print(form['subject'])
+ <input id="id_subject" type="text" name="subject" maxlength="100" />
To retrieve all ``BoundField`` objects, iterate the form::
- >>> form = ContactForm()
- >>> for boundfield in form: print(boundfield)
- <input id="id_subject" type="text" name="subject" maxlength="100" />
- <input type="text" name="message" id="id_message" />
- <input type="text" name="sender" id="id_sender" />
- <input type="checkbox" name="cc_myself" id="id_cc_myself" />
+ >>> form = ContactForm()
+ >>> for boundfield in form: print(boundfield)
+ <input id="id_subject" type="text" name="subject" maxlength="100" />
+ <input type="text" name="message" id="id_message" />
+ <input type="email" name="sender" id="id_sender" />
+ <input type="checkbox" name="cc_myself" id="id_cc_myself" />
The field-specific output honors the form object's ``auto_id`` setting::
@@ -635,7 +637,20 @@ For a field's list of errors, access the field's ``errors`` attribute.
>>> print(f['subject'].errors)
>>> str(f['subject'].errors)
- ''
+ ''
+
+.. method:: BoundField.label_tag(contents=None, attrs=None)
+
+To separately render the label tag of a form field, you can call its
+``label_tag`` method::
+
+ >>> f = ContactForm(data)
+ >>> print(f['message'].label_tag())
+ <label for="id_message">Message</label>
+
+Optionally, you can provide the ``contents`` parameter which will replace the
+auto-generated label tag. An optional ``attrs`` dictionary may contain
+additional attributes for the ``<label>`` tag.
.. method:: BoundField.css_classes()
@@ -644,17 +659,17 @@ indicate required form fields or fields that contain errors. If you're
manually rendering a form, you can access these CSS classes using the
``css_classes`` method::
- >>> f = ContactForm(data)
- >>> f['message'].css_classes()
- 'required'
+ >>> f = ContactForm(data)
+ >>> f['message'].css_classes()
+ 'required'
If you want to provide some additional classes in addition to the
error and required classes that may be required, you can provide
those classes as an argument::
- >>> f = ContactForm(data)
- >>> f['message'].css_classes('foo bar')
- 'foo bar required'
+ >>> f = ContactForm(data)
+ >>> f['message'].css_classes('foo bar')
+ 'foo bar required'
.. method:: BoundField.value()
@@ -715,6 +730,8 @@ form data *and* file data::
Testing for multipart forms
~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.. method:: Form.is_multipart
+
If you're writing reusable views or templates, you may not know ahead of time
whether your form is a multipart form or not. The ``is_multipart()`` method
tells you whether the form requires multipart encoding for submission::
@@ -753,7 +770,7 @@ fields are ordered first::
>>> print(f.as_ul())
<li>Subject: <input type="text" name="subject" maxlength="100" /></li>
<li>Message: <input type="text" name="message" /></li>
- <li>Sender: <input type="text" name="sender" /></li>
+ <li>Sender: <input type="email" name="sender" /></li>
<li>Cc myself: <input type="checkbox" name="cc_myself" /></li>
<li>Priority: <input type="text" name="priority" /></li>
diff --git a/docs/ref/forms/fields.txt b/docs/ref/forms/fields.txt
index 75d05c6829..c8b8044d26 100644
--- a/docs/ref/forms/fields.txt
+++ b/docs/ref/forms/fields.txt
@@ -112,7 +112,7 @@ We've specified ``auto_id=False`` to simplify the output::
>>> f = CommentForm(auto_id=False)
>>> print(f)
<tr><th>Your name:</th><td><input type="text" name="name" /></td></tr>
- <tr><th>Your Web site:</th><td><input type="text" name="url" /></td></tr>
+ <tr><th>Your Web site:</th><td><input type="url" name="url" /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
``initial``
@@ -135,7 +135,7 @@ field is initialized to a particular value. For example::
>>> f = CommentForm(auto_id=False)
>>> print(f)
<tr><th>Name:</th><td><input type="text" name="name" value="Your name" /></td></tr>
- <tr><th>Url:</th><td><input type="text" name="url" value="http://" /></td></tr>
+ <tr><th>Url:</th><td><input type="url" name="url" value="http://" /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
You may be thinking, why not just pass a dictionary of the initial values as
@@ -150,7 +150,7 @@ and the HTML output will include any validation errors::
>>> f = CommentForm(default_data, auto_id=False)
>>> print(f)
<tr><th>Name:</th><td><input type="text" name="name" value="Your name" /></td></tr>
- <tr><th>Url:</th><td><ul class="errorlist"><li>Enter a valid URL.</li></ul><input type="text" name="url" value="http://" /></td></tr>
+ <tr><th>Url:</th><td><ul class="errorlist"><li>Enter a valid URL.</li></ul><input type="url" name="url" value="http://" /></td></tr>
<tr><th>Comment:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="comment" /></td></tr>
This is why ``initial`` values are only displayed for unbound forms. For bound
@@ -212,17 +212,17 @@ fields. We've specified ``auto_id=False`` to simplify the output::
>>> print(f.as_table())
<tr><th>Subject:</th><td><input type="text" name="subject" maxlength="100" /><br /><span class="helptext">100 characters max.</span></td></tr>
<tr><th>Message:</th><td><input type="text" name="message" /></td></tr>
- <tr><th>Sender:</th><td><input type="text" name="sender" /><br />A valid email address, please.</td></tr>
+ <tr><th>Sender:</th><td><input type="email" name="sender" /><br />A valid email address, please.</td></tr>
<tr><th>Cc myself:</th><td><input type="checkbox" name="cc_myself" /></td></tr>
>>> print(f.as_ul()))
<li>Subject: <input type="text" name="subject" maxlength="100" /> <span class="helptext">100 characters max.</span></li>
<li>Message: <input type="text" name="message" /></li>
- <li>Sender: <input type="text" name="sender" /> A valid email address, please.</li>
+ <li>Sender: <input type="email" name="sender" /> A valid email address, please.</li>
<li>Cc myself: <input type="checkbox" name="cc_myself" /></li>
>>> print(f.as_p())
<p>Subject: <input type="text" name="subject" maxlength="100" /> <span class="helptext">100 characters max.</span></p>
<p>Message: <input type="text" name="message" /></p>
- <p>Sender: <input type="text" name="sender" /> A valid email address, please.</p>
+ <p>Sender: <input type="email" name="sender" /> A valid email address, please.</p>
<p>Cc myself: <input type="checkbox" name="cc_myself" /></p>
``error_messages``
@@ -289,7 +289,7 @@ For each field, we describe the default widget used if you don't specify
.. class:: BooleanField(**kwargs)
- * Default widget: ``CheckboxInput``
+ * Default widget: :class:`CheckboxInput`
* Empty value: ``False``
* Normalizes to: A Python ``True`` or ``False`` value.
* Validates that the value is ``True`` (e.g. the check box is checked) if
@@ -309,7 +309,7 @@ For each field, we describe the default widget used if you don't specify
.. class:: CharField(**kwargs)
- * Default widget: ``TextInput``
+ * Default widget: :class:`TextInput`
* Empty value: ``''`` (an empty string)
* Normalizes to: A Unicode object.
* Validates ``max_length`` or ``min_length``, if they are provided.
@@ -329,7 +329,7 @@ For each field, we describe the default widget used if you don't specify
.. class:: ChoiceField(**kwargs)
- * Default widget: ``Select``
+ * Default widget: :class:`Select`
* Empty value: ``''`` (an empty string)
* Normalizes to: A Unicode object.
* Validates that the given value exists in the list of choices.
@@ -355,7 +355,7 @@ For each field, we describe the default widget used if you don't specify
Just like a :class:`ChoiceField`, except :class:`TypedChoiceField` takes two
extra arguments, ``coerce`` and ``empty_value``.
- * Default widget: ``Select``
+ * Default widget: :class:`Select`
* Empty value: Whatever you've given as ``empty_value``
* Normalizes to: A value of the type provided by the ``coerce`` argument.
* Validates that the given value exists in the list of choices and can be
@@ -382,7 +382,7 @@ For each field, we describe the default widget used if you don't specify
.. class:: DateField(**kwargs)
- * Default widget: ``DateInput``
+ * Default widget: :class:`DateInput`
* Empty value: ``None``
* Normalizes to: A Python ``datetime.date`` object.
* Validates that the given value is either a ``datetime.date``,
@@ -398,21 +398,21 @@ For each field, we describe the default widget used if you don't specify
If no ``input_formats`` argument is provided, the default input formats are::
- '%Y-%m-%d', # '2006-10-25'
+ ['%Y-%m-%d', # '2006-10-25'
'%m/%d/%Y', # '10/25/2006'
- '%m/%d/%y', # '10/25/06'
+ '%m/%d/%y'] # '10/25/06'
Additionally, if you specify :setting:`USE_L10N=False<USE_L10N>` in your settings, the
following will also be included in the default input formats::
- '%b %d %Y', # 'Oct 25 2006'
+ ['%b %d %Y', # 'Oct 25 2006'
'%b %d, %Y', # 'Oct 25, 2006'
'%d %b %Y', # '25 Oct 2006'
'%d %b, %Y', # '25 Oct, 2006'
'%B %d %Y', # 'October 25 2006'
'%B %d, %Y', # 'October 25, 2006'
'%d %B %Y', # '25 October 2006'
- '%d %B, %Y', # '25 October, 2006'
+ '%d %B, %Y'] # '25 October, 2006'
See also :ref:`format localization <format-localization>`.
@@ -421,7 +421,7 @@ For each field, we describe the default widget used if you don't specify
.. class:: DateTimeField(**kwargs)
- * Default widget: ``DateTimeInput``
+ * Default widget: :class:`DateTimeInput`
* Empty value: ``None``
* Normalizes to: A Python ``datetime.datetime`` object.
* Validates that the given value is either a ``datetime.datetime``,
@@ -437,7 +437,7 @@ For each field, we describe the default widget used if you don't specify
If no ``input_formats`` argument is provided, the default input formats are::
- '%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59'
+ ['%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59'
'%Y-%m-%d %H:%M', # '2006-10-25 14:30'
'%Y-%m-%d', # '2006-10-25'
'%m/%d/%Y %H:%M:%S', # '10/25/2006 14:30:59'
@@ -445,7 +445,7 @@ For each field, we describe the default widget used if you don't specify
'%m/%d/%Y', # '10/25/2006'
'%m/%d/%y %H:%M:%S', # '10/25/06 14:30:59'
'%m/%d/%y %H:%M', # '10/25/06 14:30'
- '%m/%d/%y', # '10/25/06'
+ '%m/%d/%y'] # '10/25/06'
See also :ref:`format localization <format-localization>`.
@@ -454,7 +454,8 @@ For each field, we describe the default widget used if you don't specify
.. class:: DecimalField(**kwargs)
- * Default widget: ``TextInput``
+ * Default widget: :class:`NumberInput` when :attr:`Field.localize` is
+ ``False``, else :class:`TextInput`.
* Empty value: ``None``
* Normalizes to: A Python ``decimal``.
* Validates that the given value is a decimal. Leading and trailing
@@ -489,7 +490,7 @@ For each field, we describe the default widget used if you don't specify
.. class:: EmailField(**kwargs)
- * Default widget: ``TextInput``
+ * Default widget: :class:`EmailInput`
* Empty value: ``''`` (an empty string)
* Normalizes to: A Unicode object.
* Validates that the given value is a valid email address, using a
@@ -505,7 +506,7 @@ For each field, we describe the default widget used if you don't specify
.. class:: FileField(**kwargs)
- * Default widget: ``ClearableFileInput``
+ * Default widget: :class:`ClearableFileInput`
* Empty value: ``None``
* Normalizes to: An ``UploadedFile`` object that wraps the file content
and file name into a single object.
@@ -533,7 +534,7 @@ For each field, we describe the default widget used if you don't specify
.. class:: FilePathField(**kwargs)
- * Default widget: ``Select``
+ * Default widget: :class:`Select`
* Empty value: ``None``
* Normalizes to: A unicode object
* Validates that the selected choice exists in the list of choices.
@@ -580,7 +581,8 @@ For each field, we describe the default widget used if you don't specify
.. class:: FloatField(**kwargs)
- * Default widget: ``TextInput``
+ * Default widget: :class:`NumberInput` when :attr:`Field.localize` is
+ ``False``, else :class:`TextInput`.
* Empty value: ``None``
* Normalizes to: A Python float.
* Validates that the given value is an float. Leading and trailing
@@ -596,7 +598,7 @@ For each field, we describe the default widget used if you don't specify
.. class:: ImageField(**kwargs)
- * Default widget: ``ClearableFileInput``
+ * Default widget: :class:`ClearableFileInput`
* Empty value: ``None``
* Normalizes to: An ``UploadedFile`` object that wraps the file content
and file name into a single object.
@@ -621,7 +623,8 @@ For each field, we describe the default widget used if you don't specify
.. class:: IntegerField(**kwargs)
- * Default widget: ``TextInput``
+ * Default widget: :class:`NumberInput` when :attr:`Field.localize` is
+ ``False``, else :class:`TextInput`.
* Empty value: ``None``
* Normalizes to: A Python integer or long integer.
* Validates that the given value is an integer. Leading and trailing
@@ -644,7 +647,7 @@ For each field, we describe the default widget used if you don't specify
.. class:: IPAddressField(**kwargs)
- * Default widget: ``TextInput``
+ * Default widget: :class:`TextInput`
* Empty value: ``''`` (an empty string)
* Normalizes to: A Unicode object.
* Validates that the given value is a valid IPv4 address, using a regular
@@ -654,13 +657,11 @@ For each field, we describe the default widget used if you don't specify
``GenericIPAddressField``
~~~~~~~~~~~~~~~~~~~~~~~~~
-.. versionadded:: 1.4
-
.. class:: GenericIPAddressField(**kwargs)
A field containing either an IPv4 or an IPv6 address.
- * Default widget: ``TextInput``
+ * Default widget: :class:`TextInput`
* Empty value: ``''`` (an empty string)
* Normalizes to: A Unicode object. IPv6 addresses are
normalized as described below.
@@ -693,7 +694,7 @@ For each field, we describe the default widget used if you don't specify
.. class:: MultipleChoiceField(**kwargs)
- * Default widget: ``SelectMultiple``
+ * Default widget: :class:`SelectMultiple`
* Empty value: ``[]`` (an empty list)
* Normalizes to: A list of Unicode objects.
* Validates that every value in the given list of values exists in the list
@@ -713,7 +714,7 @@ For each field, we describe the default widget used if you don't specify
Just like a :class:`MultipleChoiceField`, except :class:`TypedMultipleChoiceField`
takes two extra arguments, ``coerce`` and ``empty_value``.
- * Default widget: ``SelectMultiple``
+ * Default widget: :class:`SelectMultiple`
* Empty value: Whatever you've given as ``empty_value``
* Normalizes to: A list of values of the type provided by the ``coerce``
argument.
@@ -731,7 +732,7 @@ For each field, we describe the default widget used if you don't specify
.. class:: NullBooleanField(**kwargs)
- * Default widget: ``NullBooleanSelect``
+ * Default widget: :class:`NullBooleanSelect`
* Empty value: ``None``
* Normalizes to: A Python ``True``, ``False`` or ``None`` value.
* Validates nothing (i.e., it never raises a ``ValidationError``).
@@ -741,7 +742,7 @@ For each field, we describe the default widget used if you don't specify
.. class:: RegexField(**kwargs)
- * Default widget: ``TextInput``
+ * Default widget: :class:`TextInput`
* Empty value: ``''`` (an empty string)
* Normalizes to: A Unicode object.
* Validates that the given value matches against a certain regular
@@ -768,7 +769,7 @@ For each field, we describe the default widget used if you don't specify
.. class:: SlugField(**kwargs)
- * Default widget: ``TextInput``
+ * Default widget: :class:`TextInput`
* Empty value: ``''`` (an empty string)
* Normalizes to: A Unicode object.
* Validates that the given value contains only letters, numbers,
@@ -783,7 +784,7 @@ For each field, we describe the default widget used if you don't specify
.. class:: TimeField(**kwargs)
- * Default widget: ``TextInput``
+ * Default widget: :class:`TextInput`
* Empty value: ``None``
* Normalizes to: A Python ``datetime.time`` object.
* Validates that the given value is either a ``datetime.time`` or string
@@ -807,7 +808,7 @@ For each field, we describe the default widget used if you don't specify
.. class:: URLField(**kwargs)
- * Default widget: ``TextInput``
+ * Default widget: :class:`URLInput`
* Empty value: ``''`` (an empty string)
* Normalizes to: A Unicode object.
* Validates that the given value is a valid URL.
@@ -829,7 +830,7 @@ Slightly complex built-in ``Field`` classes
.. class:: ComboField(**kwargs)
- * Default widget: ``TextInput``
+ * Default widget: :class:`TextInput`
* Empty value: ``''`` (an empty string)
* Normalizes to: A Unicode object.
* Validates that the given value against each of the fields specified
@@ -856,7 +857,7 @@ Slightly complex built-in ``Field`` classes
.. class:: MultiValueField(fields=(), **kwargs)
- * Default widget: ``TextInput``
+ * Default widget: :class:`TextInput`
* Empty value: ``''`` (an empty string)
* Normalizes to: the type returned by the ``compress`` method of the subclass.
* Validates that the given value against each of the fields specified
@@ -885,7 +886,7 @@ Slightly complex built-in ``Field`` classes
.. attribute:: MultiValueField.widget
Must be a subclass of :class:`django.forms.MultiWidget`.
- Default value is :class:`~django.forms.widgets.TextInput`, which
+ Default value is :class:`~django.forms.TextInput`, which
probably is not very useful in this case.
.. method:: compress(data_list)
@@ -902,7 +903,7 @@ Slightly complex built-in ``Field`` classes
.. class:: SplitDateTimeField(**kwargs)
- * Default widget: ``SplitDateTimeWidget``
+ * Default widget: :class:`SplitDateTimeWidget`
* Empty value: ``None``
* Normalizes to: A Python ``datetime.datetime`` object.
* Validates that the given value is a ``datetime.datetime`` or string
@@ -945,7 +946,7 @@ objects (in the case of ``ModelMultipleChoiceField``) into the
.. class:: ModelChoiceField(**kwargs)
- * Default widget: ``Select``
+ * Default widget: :class:`Select`
* Empty value: ``None``
* Normalizes to: A model instance.
* Validates that the given id exists in the queryset.
@@ -1000,7 +1001,7 @@ objects (in the case of ``ModelMultipleChoiceField``) into the
.. class:: ModelMultipleChoiceField(**kwargs)
- * Default widget: ``SelectMultiple``
+ * Default widget: :class:`SelectMultiple`
* Empty value: An empty ``QuerySet`` (self.queryset.none())
* Normalizes to: A ``QuerySet`` of model instances.
* Validates that every id in the given list of values exists in the
diff --git a/docs/ref/forms/formsets.txt b/docs/ref/forms/formsets.txt
new file mode 100644
index 0000000000..0ab2590fce
--- /dev/null
+++ b/docs/ref/forms/formsets.txt
@@ -0,0 +1,16 @@
+====================
+Formset Functions
+====================
+
+.. module:: django.forms.formsets
+ :synopsis: Django's functions for building formsets.
+
+.. function:: formset_factory(form, formset=BaseFormSet, extra=1, can_order=False, can_delete=False, max_num=None, validate_max=False)
+
+ Returns a ``FormSet`` class for the given ``form`` class.
+
+ See :ref:`formsets` for example usage.
+
+ .. versionchanged:: 1.6
+
+ The ``validate_max`` parameter was added.
diff --git a/docs/ref/forms/index.txt b/docs/ref/forms/index.txt
index 866afed6dc..e6edc88ca1 100644
--- a/docs/ref/forms/index.txt
+++ b/docs/ref/forms/index.txt
@@ -9,5 +9,7 @@ Detailed form API reference. For introductory material, see :doc:`/topics/forms/
api
fields
+ models
+ formsets
widgets
validation
diff --git a/docs/ref/forms/models.txt b/docs/ref/forms/models.txt
new file mode 100644
index 0000000000..dd0a422fd0
--- /dev/null
+++ b/docs/ref/forms/models.txt
@@ -0,0 +1,60 @@
+====================
+Model Form Functions
+====================
+
+.. module:: django.forms.models
+ :synopsis: Django's functions for building model forms and formsets.
+
+.. function:: modelform_factory(model, form=ModelForm, fields=None, exclude=None, formfield_callback=None, widgets=None)
+
+ Returns a :class:`~django.forms.ModelForm` class for the given ``model``.
+ You can optionally pass a ``form`` argument to use as a starting point for
+ constructing the ``ModelForm``.
+
+ ``fields`` is an optional list of field names. If provided, only the named
+ fields will be included in the returned fields.
+
+ ``exclude`` is an optional list of field names. If provided, the named
+ fields will be excluded from the returned fields, even if they are listed
+ in the ``fields`` argument.
+
+ ``widgets`` is a dictionary of model field names mapped to a widget.
+
+ ``formfield_callback`` is a callable that takes a model field and returns
+ a form field.
+
+ See :ref:`modelforms-factory` for example usage.
+
+.. function:: modelformset_factory(model, form=ModelForm, formfield_callback=None, formset=BaseModelFormSet, extra=1, can_delete=False, can_order=False, max_num=None, fields=None, exclude=None, widgets=None, validate_max=False)
+
+ Returns a ``FormSet`` class for the given ``model`` class.
+
+ Arguments ``model``, ``form``, ``fields``, ``exclude``,
+ ``formfield_callback`` and ``widgets`` are all passed through to
+ :func:`~django.forms.models.modelform_factory`.
+
+ Arguments ``formset``, ``extra``, ``max_num``, ``can_order``,
+ ``can_delete`` and ``validate_max`` are passed through to
+ :func:`~django.forms.formsets.formset_factory`. See :ref:`formsets` for
+ details.
+
+ See :ref:`model-formsets` for example usage.
+
+ .. versionchanged:: 1.6
+
+ The ``widgets`` and the ``validate_max`` parameters were added.
+
+.. function:: inlineformset_factory(parent_model, model, form=ModelForm, formset=BaseInlineFormSet, fk_name=None, fields=None, exclude=None, extra=3, can_order=False, can_delete=True, max_num=None, formfield_callback=None, widgets=None, validate_max=False)
+
+ Returns an ``InlineFormSet`` using :func:`modelformset_factory` with
+ defaults of ``formset=BaseInlineFormSet``, ``can_delete=True``, and
+ ``extra=3``.
+
+ If your model has more than one :class:`~django.db.models.ForeignKey` to
+ the ``parent_model``, you must specify a ``fk_name``.
+
+ See :ref:`inline-formsets` for example usage.
+
+ .. versionchanged:: 1.6
+
+ The ``widgets`` and the ``validate_max`` parameters were added.
diff --git a/docs/ref/forms/validation.txt b/docs/ref/forms/validation.txt
index e89bce748f..978c985b55 100644
--- a/docs/ref/forms/validation.txt
+++ b/docs/ref/forms/validation.txt
@@ -181,24 +181,20 @@ the field's ``validators`` argument, or defined on the Field class itself with
the ``default_validators`` attribute.
Simple validators can be used to validate values inside the field, let's have
-a look at Django's ``EmailField``::
+a look at Django's ``SlugField``::
- class EmailField(CharField):
- default_error_messages = {
- 'invalid': _('Enter a valid email address.'),
- }
- default_validators = [validators.validate_email]
+ class SlugField(CharField):
+ default_validators = [validators.validate_slug]
-As you can see, ``EmailField`` is just a ``CharField`` with customized error
-message and a validator that validates email addresses. This can also be done
-on field definition so::
+As you can see, ``SlugField`` is just a ``CharField`` with a customized
+validator that validates that submitted text obeys to some character rules.
+This can also be done on field definition so::
- email = forms.EmailField()
+ slug = forms.SlugField()
is equivalent to::
- email = forms.CharField(validators=[validators.validate_email],
- error_messages={'invalid': _('Enter a valid email address.')})
+ slug = forms.CharField(validators=[validators.validate_slug])
Form field default cleaning
diff --git a/docs/ref/forms/widgets.txt b/docs/ref/forms/widgets.txt
index a0ef0731ad..514e8b3dc0 100644
--- a/docs/ref/forms/widgets.txt
+++ b/docs/ref/forms/widgets.txt
@@ -49,8 +49,8 @@ Setting arguments for widgets
Many widgets have optional extra arguments; they can be set when defining the
widget on the field. In the following example, the
-:attr:`~SelectDateWidget.years` attribute is set for a
-:class:`~django.forms.extras.widgets.SelectDateWidget`::
+:attr:`~django.forms.extras.widgets.SelectDateWidget.years` attribute is set
+for a :class:`~django.forms.extras.widgets.SelectDateWidget`::
from django.forms.fields import DateField, ChoiceField, MultipleChoiceField
from django.forms.widgets import RadioSelect, CheckboxSelectMultiple
@@ -139,7 +139,7 @@ provided for each widget will be rendered exactly the same::
>>> f = CommentForm(auto_id=False)
>>> f.as_table()
<tr><th>Name:</th><td><input type="text" name="name" /></td></tr>
- <tr><th>Url:</th><td><input type="text" name="url"/></td></tr>
+ <tr><th>Url:</th><td><input type="url" name="url"/></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
On a real Web page, you probably don't want every widget to look the same. You
@@ -160,7 +160,7 @@ Django will then include the extra attributes in the rendered output:
>>> f = CommentForm(auto_id=False)
>>> f.as_table()
<tr><th>Name:</th><td><input type="text" name="name" class="special"/></td></tr>
- <tr><th>Url:</th><td><input type="text" name="url"/></td></tr>
+ <tr><th>Url:</th><td><input type="url" name="url"/></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" size="40"/></td></tr>
.. _styling-widget-classes:
@@ -222,7 +222,7 @@ foundation for custom widgets.
.. class:: MultiWidget(widgets, attrs=None)
A widget that is composed of multiple widgets.
- :class:`~django.forms.widgets.MultiWidget` works hand in hand with the
+ :class:`~django.forms.MultiWidget` works hand in hand with the
:class:`~django.forms.MultiValueField`.
:class:`MultiWidget` has one required argument:
@@ -246,8 +246,8 @@ foundation for custom widgets.
the combined value of the form field into the values for each widget.
An example of this is how :class:`SplitDateTimeWidget` turns a
- :class:`datetime` value into a list with date and time split into two
- separate values::
+ :class:`~datetime.datetime` value into a list with date and time split
+ into two separate values::
class SplitDateTimeWidget(MultiWidget):
@@ -279,15 +279,10 @@ foundation for custom widgets.
* A single value (e.g., a string) that is the "compressed" representation
of a ``list`` of values.
- If `value` is a list, output of :meth:`~MultiWidget.render` will be a
- concatenation of rendered child widgets. If `value` is not a list, it
- will be first processed by the method :meth:`~MultiWidget.decompress()`
- to create the list and then processed as above.
-
- In the second case -- i.e., if the value is *not* a list --
- ``render()`` will first decompress the value into a ``list`` before
- rendering it. It does so by calling the ``decompress()`` method, which
- :class:`MultiWidget`'s subclasses must implement (see above).
+ If ``value`` is a list, the output of :meth:`~MultiWidget.render` will
+ be a concatenation of rendered child widgets. If ``value`` is not a
+ list, it will first be processed by the method
+ :meth:`~MultiWidget.decompress()` to create the list and then rendered.
When ``render()`` executes its HTML rendering, each value in the list
is rendered with the corresponding widget -- the first value is
@@ -392,7 +387,38 @@ These widgets make use of the HTML elements ``input`` and ``textarea``.
.. class:: TextInput
- Text input: ``<input type='text' ...>``
+ Text input: ``<input type="text" ...>``
+
+``NumberInput``
+~~~~~~~~~~~~~~~
+
+.. class:: NumberInput
+
+ .. versionadded:: 1.6
+
+ Text input: ``<input type="number" ...>``
+
+ Beware that not all browsers support entering localized numbers in
+ ``number`` input types. Django itself avoids using them for fields having
+ their :attr:`~django.forms.Field.localize` property to ``True``.
+
+``EmailInput``
+~~~~~~~~~~~~~~
+
+.. class:: EmailInput
+
+ .. versionadded:: 1.6
+
+ Text input: ``<input type="email" ...>``
+
+``URLInput``
+~~~~~~~~~~~~
+
+.. class:: URLInput
+
+ .. versionadded:: 1.6
+
+ Text input: ``<input type="url" ...>``
``PasswordInput``
~~~~~~~~~~~~~~~~~
@@ -508,9 +534,9 @@ Selector and checkbox widgets
.. attribute:: Select.choices
- This attribute is optional when the field does not have a
- :attr:`~Field.choices` attribute. If it does, it will override anything
- you set here when the attribute is updated on the :class:`Field`.
+ This attribute is optional when the form field does not have a
+ ``choices`` attribute. If it does, it will override anything you set
+ here when the attribute is updated on the :class:`Field`.
``NullBooleanSelect``
~~~~~~~~~~~~~~~~~~~~~
@@ -542,8 +568,6 @@ Selector and checkbox widgets
...
</ul>
- .. versionadded:: 1.4
-
For more granular control over the generated markup, you can loop over the
radio buttons in the template. Assuming a form ``myform`` with a field
``beatles`` that uses a ``RadioSelect`` as its widget:
@@ -609,6 +633,11 @@ Selector and checkbox widgets
If you decide not to loop over the radio buttons -- e.g., if your template simply includes
``{{ myform.beatles }}`` -- they'll be output in a ``<ul>`` with ``<li>`` tags, as above.
+.. versionchanged:: 1.6
+
+The outer ``<ul>`` container will now receive the ``id`` attribute defined on
+the widget.
+
``CheckboxSelectMultiple``
~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -624,6 +653,14 @@ Selector and checkbox widgets
...
</ul>
+.. versionchanged:: 1.6
+
+The outer ``<ul>`` container will now receive the ``id`` attribute defined on
+the widget.
+
+Like :class:`RadioSelect`, you can now loop over the individual checkboxes making
+up the lists. See the documentation of :class:`RadioSelect` for more details.
+
.. _file-upload-widgets:
File upload widgets
@@ -662,9 +699,9 @@ Composite widgets
.. attribute:: MultipleHiddenInput.choices
- This attribute is optional when the field does not have a
- :attr:`~Field.choices` attribute. If it does, it will override anything
- you set here when the attribute is updated on the :class:`Field`.
+ This attribute is optional when the form field does not have a
+ ``choices`` attribute. If it does, it will override anything you set
+ here when the attribute is updated on the :class:`Field`.
``SplitDateTimeWidget``
~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/docs/ref/index.txt b/docs/ref/index.txt
index e1959d44a6..1d71b62f41 100644
--- a/docs/ref/index.txt
+++ b/docs/ref/index.txt
@@ -5,7 +5,6 @@ API Reference
.. toctree::
:maxdepth: 1
- authbackends
class-based-views/index
clickjacking
contrib/index
@@ -26,3 +25,4 @@ API Reference
urls
utils
validators
+ views
diff --git a/docs/ref/middleware.txt b/docs/ref/middleware.txt
index b542aee6e2..20bb2fb751 100644
--- a/docs/ref/middleware.txt
+++ b/docs/ref/middleware.txt
@@ -61,14 +61,16 @@ Adds a few conveniences for perfectionists:
indexer would treat them as separate URLs -- so it's best practice to
normalize URLs.
-* Sends broken link notification emails to :setting:`MANAGERS` if
- :setting:`SEND_BROKEN_LINK_EMAILS` is set to ``True``.
-
* Handles ETags based on the :setting:`USE_ETAGS` setting. If
:setting:`USE_ETAGS` is set to ``True``, Django will calculate an ETag
for each request by MD5-hashing the page content, and it'll take care of
sending ``Not Modified`` responses, if appropriate.
+.. class:: BrokenLinkEmailsMiddleware
+
+* Sends broken link notification emails to :setting:`MANAGERS` (see
+ :doc:`/howto/error-reporting`).
+
View metadata middleware
------------------------
@@ -111,7 +113,7 @@ It will NOT compress content if any of the following are true:
not to be performed on certain content types.
You can apply GZip compression to individual views using the
-:func:`~django.views.decorators.http.gzip_page()` decorator.
+:func:`~django.views.decorators.gzip.gzip_page()` decorator.
Conditional GET middleware
--------------------------
@@ -124,7 +126,7 @@ Conditional GET middleware
Handles conditional GET operations. If the response has a ``ETag`` or
``Last-Modified`` header, and the request has ``If-None-Match`` or
``If-Modified-Since``, the response is replaced by an
-:class:`~django.http.HttpNotModified`.
+:class:`~django.http.HttpResponseNotModified`.
Also sets the ``Date`` and ``Content-Length`` response-headers.
@@ -179,8 +181,8 @@ Authentication middleware
.. class:: AuthenticationMiddleware
Adds the ``user`` attribute, representing the currently-logged-in user, to
-every incoming ``HttpRequest`` object. See :doc:`Authentication in Web requests
-</topics/auth>`.
+every incoming ``HttpRequest`` object. See :ref:`Authentication in Web requests
+<auth-web-requests>`.
CSRF protection middleware
--------------------------
@@ -203,6 +205,10 @@ Transaction middleware
.. class:: TransactionMiddleware
+.. versionchanged:: 1.6
+ ``TransactionMiddleware`` is deprecated. The documentation of transactions
+ contains :ref:`upgrade instructions <transactions-upgrading-from-1.5>`.
+
Binds commit and rollback of the default database to the request/response
phase. If a view function runs successfully, a commit is done. If it fails with
an exception, a rollback is done.
@@ -222,7 +228,4 @@ X-Frame-Options middleware
.. class:: XFrameOptionsMiddleware
-.. versionadded:: 1.4
- ``XFrameOptionsMiddleware`` was added.
-
Simple :doc:`clickjacking protection via the X-Frame-Options header </ref/clickjacking/>`.
diff --git a/docs/ref/models/fields.txt b/docs/ref/models/fields.txt
index cd1185585c..7ef251c907 100644
--- a/docs/ref/models/fields.txt
+++ b/docs/ref/models/fields.txt
@@ -12,8 +12,8 @@ This document contains all the gory details about all the `field options`_ and
.. seealso::
- If the built-in fields don't do the trick, you can try
- :mod:`django.contrib.localflavor`, which contains assorted pieces of code
+ If the built-in fields don't do the trick, you can try :doc:`localflavor
+ </topics/localflavor>`, which contains assorted pieces of code
that are useful for particular countries or cultures. Also, you can easily
:doc:`write your own custom model fields </howto/custom-model-fields>`.
@@ -113,7 +113,7 @@ define a suitably-named constant for each value::
default=FRESHMAN)
def is_upperclass(self):
- return self.year_in_school in (self.JUNIOR, self.SENIOR)
+ return self.year_in_school in (self.JUNIOR, self.SENIOR)
Though you can define a choices list outside of a model class and then
refer to it, defining the choices and names for each choice inside the
@@ -248,8 +248,8 @@ Alternatively you can use plain text and
If ``True``, this field is the primary key for the model.
-If you don't specify ``primary_key=True`` for any fields in your model, Django
-will automatically add an :class:`IntegerField` to hold the primary key, so you
+If you don't specify ``primary_key=True`` for any field in your model, Django
+will automatically add an :class:`AutoField` to hold the primary key, so you
don't need to set ``primary_key=True`` on any of your fields unless you want to
override the default primary-key behavior. For more, see
:ref:`automatic-primary-key-fields`.
@@ -272,6 +272,9 @@ field, a :exc:`django.db.IntegrityError` will be raised by the model's
This option is valid on all field types except :class:`ManyToManyField` and
:class:`FileField`.
+Note that when ``unique`` is ``True``, you don't need to specify
+:attr:`~Field.db_index`, because ``unique`` implies the creation of an index.
+
``unique_for_date``
-------------------
@@ -344,6 +347,22 @@ A 64 bit integer, much like an :class:`IntegerField` except that it is
guaranteed to fit numbers from -9223372036854775808 to 9223372036854775807. The
default form widget for this field is a :class:`~django.forms.TextInput`.
+``BinaryField``
+-------------------
+
+.. class:: BinaryField([**options])
+
+.. versionadded:: 1.6
+
+A field to store raw binary data. It only supports ``bytes`` assignment. Be
+aware that this field has limited functionality. For example, it is not possible
+to filter a queryset on a ``BinaryField`` value.
+
+.. admonition:: Abusing ``BinaryField``
+
+ Although you might think about storing files in the database, consider that
+ it is bad design in 99% of the cases. This field is *not* a replacement for
+ proper :doc`static files </howto/static-files>` handling.
``BooleanField``
----------------
@@ -358,6 +377,10 @@ The default form widget for this field is a
If you need to accept :attr:`~Field.null` values then use
:class:`NullBooleanField` instead.
+.. versionchanged:: 1.6
+ The default value of ``BooleanField`` was changed from ``False`` to
+ ``None`` when :attr:`Field.default` isn't defined.
+
``CharField``
-------------
@@ -453,7 +476,7 @@ A fixed-precision decimal number, represented in Python by a
.. attribute:: DecimalField.max_digits
The maximum number of digits allowed in the number. Note that this number
- must be greater than or equal to ``decimal_places``, if it exists.
+ must be greater than or equal to ``decimal_places``.
.. attribute:: DecimalField.decimal_places
@@ -509,8 +532,8 @@ Has one **required** argument:
.. attribute:: FileField.upload_to
A local filesystem path that will be appended to your :setting:`MEDIA_ROOT`
- setting to determine the value of the :attr:`~django.core.files.File.url`
- attribute.
+ setting to determine the value of the
+ :attr:`~django.db.models.fields.files.FieldFile.url` attribute.
This path may contain :func:`~time.strftime` formatting, which will be
replaced by the date/time of the file upload (so that uploaded files don't
@@ -547,8 +570,7 @@ Also has one optional argument:
Optional. A storage object, which handles the storage and retrieval of your
files. See :doc:`/topics/files` for details on how to provide this object.
-The default form widget for this field is a
-:class:`~django.forms.widgets.FileInput`.
+The default form widget for this field is a :class:`~django.forms.FileInput`.
Using a :class:`FileField` or an :class:`ImageField` (see below) in a model
takes a few steps:
@@ -565,9 +587,9 @@ takes a few steps:
3. All that will be stored in your database is a path to the file
(relative to :setting:`MEDIA_ROOT`). You'll most likely want to use the
- convenience :attr:`~django.core.files.File.url` function provided by
- Django. For example, if your :class:`ImageField` is called ``mug_shot``,
- you can get the absolute path to your image in a template with
+ convenience :attr:`~django.db.models.fields.files.FieldFile.url` attribute
+ provided by Django. For example, if your :class:`ImageField` is called
+ ``mug_shot``, you can get the absolute path to your image in a template with
``{{ object.mug_shot.url }}``.
For example, say your :setting:`MEDIA_ROOT` is set to ``'/home/media'``, and
@@ -590,7 +612,7 @@ topic guide.
saved.
The uploaded file's relative URL can be obtained using the
-:attr:`~django.db.models.fields.FileField.url` attribute. Internally,
+:attr:`~django.db.models.fields.files.FieldFile.url` attribute. Internally,
this calls the :meth:`~django.core.files.storage.Storage.url` method of the
underlying :class:`~django.core.files.storage.Storage` class.
@@ -615,9 +637,20 @@ can change the maximum length using the :attr:`~CharField.max_length` argument.
FileField and FieldFile
~~~~~~~~~~~~~~~~~~~~~~~
-When you access a :class:`FileField` on a model, you are given an instance
-of :class:`FieldFile` as a proxy for accessing the underlying file. This
-class has several methods that can be used to interact with file data:
+.. currentmodule:: django.db.models.fields.files
+
+.. class:: FieldFile
+
+When you access a :class:`~django.db.models.FileField` on a model, you are
+given an instance of :class:`FieldFile` as a proxy for accessing the underlying
+file. This class has several attributes and methods that can be used to
+interact with file data:
+
+.. attribute:: FieldFile.url
+
+A read-only property to access the file's relative URL by calling the
+:meth:`~django.core.files.storage.Storage.url` method of the underlying
+:class:`~django.core.files.storage.Storage` class.
.. method:: FieldFile.open(mode='rb')
@@ -633,9 +666,9 @@ associated with this instance.
This method takes a filename and file contents and passes them to the storage
class for the field, then associates the stored file with the model field.
-If you want to manually associate file data with :class:`FileField`
-instances on your model, the ``save()`` method is used to persist that file
-data.
+If you want to manually associate file data with
+:class:`~django.db.models.FileField` instances on your model, the ``save()``
+method is used to persist that file data.
Takes two required arguments: ``name`` which is the name of the file, and
``content`` which is an object containing the file's contents. The
@@ -673,6 +706,8 @@ to cleanup orphaned files, you'll need to handle it yourself (for instance,
with a custom management command that can be run manually or scheduled to run
periodically via e.g. cron).
+.. currentmodule:: django.db.models
+
``FilePathField``
-----------------
@@ -760,8 +795,7 @@ Inherits all attributes and methods from :class:`FileField`, but also
validates that the uploaded object is a valid image.
In addition to the special attributes that are available for :class:`FileField`,
-an :class:`ImageField` also has :attr:`~django.core.files.File.height` and
-:attr:`~django.core.files.File.width` attributes.
+an :class:`ImageField` also has ``height`` and ``width`` attributes.
To facilitate querying on those attributes, :class:`ImageField` has two extra
optional arguments:
@@ -805,8 +839,6 @@ for this field is a :class:`~django.forms.TextInput`.
.. class:: GenericIPAddressField([protocol=both, unpack_ipv4=False, **options])
-.. versionadded:: 1.4
-
An IPv4 or IPv6 address, in string format (e.g. ``192.0.2.30`` or
``2a02:42fe::4``). The default form widget for this field is a
:class:`~django.forms.TextInput`.
@@ -844,8 +876,8 @@ widget for this field is a :class:`~django.forms.NullBooleanSelect`.
.. class:: PositiveIntegerField([**options])
-Like an :class:`IntegerField`, but must be either positive or zero (`0`).
-The value `0` is accepted for backward compatibility reasons.
+Like an :class:`IntegerField`, but must be either positive or zero (``0``).
+The value ``0`` is accepted for backward compatibility reasons.
``PositiveSmallIntegerField``
-----------------------------
@@ -919,7 +951,7 @@ A :class:`CharField` for a URL.
The default form widget for this field is a :class:`~django.forms.TextInput`.
Like all :class:`CharField` subclasses, :class:`URLField` takes the optional
-:attr:`~CharField.max_length`argument. If you don't specify
+:attr:`~CharField.max_length` argument. If you don't specify
:attr:`~CharField.max_length`, a default of 200 is used.
.. versionadded:: 1.5
@@ -1039,6 +1071,21 @@ define the details of how the relation works.
The field on the related object that the relation is to. By default, Django
uses the primary key of the related object.
+.. attribute:: ForeignKey.db_constraint
+
+ .. versionadded:: 1.6
+
+ Controls whether or not a constraint should be created in the database for
+ this foreign key. The default is ``True``, and that's almost certainly what
+ you want; setting this to ``False`` can be very bad for data integrity.
+ That said, here are some scenarios where you might want to do this:
+
+ * You have legacy data that is not valid.
+ * You're sharding your database.
+
+ If this is set to ``False``, accessing a related object that doesn't exist
+ will raise its ``DoesNotExist`` exception.
+
.. attribute:: ForeignKey.on_delete
When an object referenced by a :class:`ForeignKey` is deleted, Django by
@@ -1050,26 +1097,36 @@ define the details of how the relation works.
user = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL)
- The possible values for :attr:`on_delete` are found in
- :mod:`django.db.models`:
+The possible values for :attr:`~ForeignKey.on_delete` are found in
+:mod:`django.db.models`:
+
+* .. attribute:: CASCADE
+
+ Cascade deletes; the default.
+
+* .. attribute:: PROTECT
+
+ Prevent deletion of the referenced object by raising
+ :exc:`~django.db.models.ProtectedError`, a subclass of
+ :exc:`django.db.IntegrityError`.
- * :attr:`~django.db.models.CASCADE`: Cascade deletes; the default.
+* .. attribute:: SET_NULL
- * :attr:`~django.db.models.PROTECT`: Prevent deletion of the referenced
- object by raising :exc:`django.db.models.ProtectedError`, a subclass of
- :exc:`django.db.IntegrityError`.
+ Set the :class:`ForeignKey` null; this is only possible if
+ :attr:`~Field.null` is ``True``.
- * :attr:`~django.db.models.SET_NULL`: Set the :class:`ForeignKey` null;
- this is only possible if :attr:`null` is ``True``.
+* .. attribute:: SET_DEFAULT
- * :attr:`~django.db.models.SET_DEFAULT`: Set the :class:`ForeignKey` to its
- default value; a default for the :class:`ForeignKey` must be set.
+ Set the :class:`ForeignKey` to its default value; a default for the
+ :class:`ForeignKey` must be set.
- * :func:`~django.db.models.SET()`: Set the :class:`ForeignKey` to the value
- passed to :func:`~django.db.models.SET()`, or if a callable is passed in,
- the result of calling it. In most cases, passing a callable will be
- necessary to avoid executing queries at the time your models.py is
- imported::
+* .. function:: SET()
+
+ Set the :class:`ForeignKey` to the value passed to
+ :func:`~django.db.models.SET()`, or if a callable is passed in,
+ the result of calling it. In most cases, passing a callable will be
+ necessary to avoid executing queries at the time your models.py is
+ imported::
def get_sentinel_user():
return User.objects.get_or_create(username='deleted')[0]
@@ -1077,11 +1134,12 @@ define the details of how the relation works.
class MyModel(models.Model):
user = models.ForeignKey(User, on_delete=models.SET(get_sentinel_user))
- * :attr:`~django.db.models.DO_NOTHING`: Take no action. If your database
- backend enforces referential integrity, this will cause an
- :exc:`~django.db.IntegrityError` unless you manually add a SQL ``ON
- DELETE`` constraint to the database field (perhaps using
- :ref:`initial sql<initial-sql>`).
+* .. attribute:: DO_NOTHING
+
+ Take no action. If your database backend enforces referential
+ integrity, this will cause an :exc:`~django.db.IntegrityError` unless
+ you manually add a SQL ``ON DELETE`` constraint to the database field
+ (perhaps using :ref:`initial sql<initial-sql>`).
.. _ref-manytomany:
@@ -1175,6 +1233,22 @@ that control how the relationship functions.
the table for the model defining the relationship and the name of the field
itself.
+.. attribute:: ManyToManyField.db_constraint
+
+ .. versionadded:: 1.6
+
+ Controls whether or not constraints should be created in the database for
+ the foreign keys in the intermediary table. The default is ``True``, and
+ that's almost certainly what you want; setting this to ``False`` can be
+ very bad for data integrity. That said, here are some scenarios where you
+ might want to do this:
+
+ * You have legacy data that is not valid.
+ * You're sharding your database.
+
+ It is an error to pass both ``db_constraint`` and ``through``.
+
+
.. _ref-onetoone:
``OneToOneField``
diff --git a/docs/ref/models/instances.txt b/docs/ref/models/instances.txt
index 6315985ba9..9f583c42ac 100644
--- a/docs/ref/models/instances.txt
+++ b/docs/ref/models/instances.txt
@@ -292,12 +292,13 @@ follows this algorithm:
* If the object's primary key attribute is set to a value that evaluates to
``True`` (i.e., a value other than ``None`` or the empty string), Django
- executes a ``SELECT`` query to determine whether a record with the given
- primary key already exists.
-* If the record with the given primary key does already exist, Django
- executes an ``UPDATE`` query.
-* If the object's primary key attribute is *not* set, or if it's set but a
- record doesn't exist, Django executes an ``INSERT``.
+ executes an ``UPDATE``.
+* If the object's primary key attribute is *not* set or if the ``UPDATE``
+ didn't update anything, Django executes an ``INSERT``.
+
+.. versionchanged:: 1.6
+ Previously Django used ``SELECT`` - if not found ``INSERT`` else ``UPDATE``
+ algorithm. The old algorithm resulted in one more query in ``UPDATE`` case.
The one gotcha here is that you should be careful not to specify a primary-key
value explicitly when saving new objects, if you cannot guarantee the
@@ -601,7 +602,7 @@ pattern, it's possible to give a name to a pattern, and then reference the name
rather than the view function. A named URL pattern is defined by replacing the
pattern tuple by a call to the ``url`` function)::
- from django.conf.urls import patterns, url, include
+ from django.conf.urls import url
url(r'^people/(\d+)/$', 'blog_views.generic_detail', name='people_view'),
@@ -659,7 +660,7 @@ For every :class:`~django.db.models.DateField` and
<django.db.models.Field.null>`, the object will have ``get_next_by_FOO()`` and
``get_previous_by_FOO()`` methods, where ``FOO`` is the name of the field. This
returns the next and previous object with respect to the date field, raising
-a :exc:`~django.db.DoesNotExist` exception when appropriate.
+a :exc:`~django.core.exceptions.DoesNotExist` exception when appropriate.
Both methods accept optional keyword arguments, which should be in the format
described in :ref:`Field lookups <field-lookups>`.
diff --git a/docs/ref/models/options.txt b/docs/ref/models/options.txt
index a577135271..5f9316bd2a 100644
--- a/docs/ref/models/options.txt
+++ b/docs/ref/models/options.txt
@@ -85,14 +85,15 @@ Django quotes column and table names behind the scenes.
The name of an orderable field in the model, typically a :class:`DateField`,
:class:`DateTimeField`, or :class:`IntegerField`. This specifies the default
- field to use in your model :class:`Manager`'s :class:`~QuerySet.latest`
- method.
+ field to use in your model :class:`Manager`'s
+ :meth:`~django.db.models.query.QuerySet.latest` and
+ :meth:`~django.db.models.query.QuerySet.earliest` methods.
Example::
get_latest_by = "order_date"
- See the docs for :meth:`~django.db.models.query.QuerySet.latest` for more.
+ See the :meth:`~django.db.models.query.QuerySet.latest` docs for more.
``managed``
-----------
@@ -100,7 +101,7 @@ Django quotes column and table names behind the scenes.
.. attribute:: Options.managed
Defaults to ``True``, meaning Django will create the appropriate database
- tables in :djadmin:`syncdb` and remove them as part of a :djadmin:`reset`
+ tables in :djadmin:`syncdb` and remove them as part of a :djadmin:`flush`
management command. That is, Django *manages* the database tables'
lifecycles.
@@ -209,10 +210,6 @@ Django quotes column and table names behind the scenes.
ordering = ['-pub_date', 'author']
- .. versionchanged:: 1.4
- The Django admin honors all elements in the list/tuple; before 1.4, only
- the first one was respected.
-
``permissions``
---------------
@@ -262,6 +259,7 @@ Django quotes column and table names behind the scenes.
an explicit :attr:`through <ManyToManyField.through>` model.
``index_together``
+------------------
.. attribute:: Options.index_together
diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt
index 40fa2d2b2f..9c1337d59f 100644
--- a/docs/ref/models/querysets.txt
+++ b/docs/ref/models/querysets.txt
@@ -378,16 +378,12 @@ query spans multiple tables, it's possible to get duplicate results when a
:meth:`values()` together, be careful when ordering by fields not in the
:meth:`values()` call.
-.. versionadded:: 1.4
-
-As of Django 1.4, you can pass positional arguments (``*fields``) in order to
-specify the names of fields to which the ``DISTINCT`` should apply. This
-translates to a ``SELECT DISTINCT ON`` SQL query.
-
-Here's the difference. For a normal ``distinct()`` call, the database compares
-*each* field in each row when determining which rows are distinct. For a
-``distinct()`` call with specified field names, the database will only compare
-the specified field names.
+You can pass positional arguments (``*fields``) in order to specify the names
+of fields to which the ``DISTINCT`` should apply. This translates to a
+``SELECT DISTINCT ON`` SQL query. Here's the difference. For a normal
+``distinct()`` call, the database compares *each* field in each row when
+determining which rows are distinct. For a ``distinct()`` call with specified
+field names, the database will only compare the specified field names.
.. note::
This ability to specify field names is only available in PostgreSQL.
@@ -554,14 +550,19 @@ dates
.. method:: dates(field, kind, order='ASC')
Returns a ``DateQuerySet`` — a ``QuerySet`` that evaluates to a list of
-``datetime.datetime`` objects representing all available dates of a particular
-kind within the contents of the ``QuerySet``.
+:class:`datetime.date` objects representing all available dates of a
+particular kind within the contents of the ``QuerySet``.
+
+.. versionchanged:: 1.6
+ ``dates`` used to return a list of :class:`datetime.datetime` objects.
-``field`` should be the name of a ``DateField`` or ``DateTimeField`` of your
-model.
+``field`` should be the name of a ``DateField`` of your model.
+
+.. versionchanged:: 1.6
+ ``dates`` used to accept operating on a ``DateTimeField``.
``kind`` should be either ``"year"``, ``"month"`` or ``"day"``. Each
-``datetime.datetime`` object in the result list is "truncated" to the given
+``datetime.date`` object in the result list is "truncated" to the given
``type``.
* ``"year"`` returns a list of all distinct year values for the field.
@@ -576,36 +577,77 @@ model.
Examples::
>>> Entry.objects.dates('pub_date', 'year')
- [datetime.datetime(2005, 1, 1)]
+ [datetime.date(2005, 1, 1)]
>>> Entry.objects.dates('pub_date', 'month')
- [datetime.datetime(2005, 2, 1), datetime.datetime(2005, 3, 1)]
+ [datetime.date(2005, 2, 1), datetime.date(2005, 3, 1)]
>>> Entry.objects.dates('pub_date', 'day')
- [datetime.datetime(2005, 2, 20), datetime.datetime(2005, 3, 20)]
+ [datetime.date(2005, 2, 20), datetime.date(2005, 3, 20)]
>>> Entry.objects.dates('pub_date', 'day', order='DESC')
- [datetime.datetime(2005, 3, 20), datetime.datetime(2005, 2, 20)]
+ [datetime.date(2005, 3, 20), datetime.date(2005, 2, 20)]
>>> Entry.objects.filter(headline__contains='Lennon').dates('pub_date', 'day')
- [datetime.datetime(2005, 3, 20)]
+ [datetime.date(2005, 3, 20)]
-.. warning::
+datetimes
+~~~~~~~~~
+
+.. versionadded:: 1.6
+
+.. method:: datetimes(field, kind, order='ASC', tzinfo=None)
+
+Returns a ``DateTimeQuerySet`` — a ``QuerySet`` that evaluates to a list of
+:class:`datetime.datetime` objects representing all available dates of a
+particular kind within the contents of the ``QuerySet``.
+
+``field`` should be the name of a ``DateTimeField`` of your model.
+
+``kind`` should be either ``"year"``, ``"month"``, ``"day"``, ``"hour"``,
+``"minute"`` or ``"second"``. Each ``datetime.datetime`` object in the result
+list is "truncated" to the given ``type``.
+
+``order``, which defaults to ``'ASC'``, should be either ``'ASC'`` or
+``'DESC'``. This specifies how to order the results.
+
+``tzinfo`` defines the time zone to which datetimes are converted prior to
+truncation. Indeed, a given datetime has different representations depending
+on the time zone in use. This parameter must be a :class:`datetime.tzinfo`
+object. If it's ``None``, Django uses the :ref:`current time zone
+<default-current-time-zone>`. It has no effect when :setting:`USE_TZ` is
+``False``.
+
+.. _database-time-zone-definitions:
+
+.. note::
+
+ This function performs time zone conversions directly in the database.
+ As a consequence, your database must be able to interpret the value of
+ ``tzinfo.tzname(None)``. This translates into the following requirements:
+
+ - SQLite: install pytz_ — conversions are actually performed in Python.
+ - PostgreSQL: no requirements (see `Time Zones`_).
+ - Oracle: no requirements (see `Choosing a Time Zone File`_).
+ - MySQL: load the time zone tables with `mysql_tzinfo_to_sql`_.
- When :doc:`time zone support </topics/i18n/timezones>` is enabled, Django
- uses UTC in the database connection, which means the aggregation is
- performed in UTC. This is a known limitation of the current implementation.
+ .. _pytz: http://pytz.sourceforge.net/
+ .. _Time Zones: http://www.postgresql.org/docs/current/static/datatype-datetime.html#DATATYPE-TIMEZONES
+ .. _Choosing a Time Zone File: http://docs.oracle.com/cd/B19306_01/server.102/b14225/ch4datetime.htm#i1006667
+ .. _mysql_tzinfo_to_sql: http://dev.mysql.com/doc/refman/5.5/en/mysql-tzinfo-to-sql.html
none
~~~~
.. method:: none()
-Returns an ``EmptyQuerySet`` — a ``QuerySet`` subclass that always evaluates to
-an empty list. This can be used in cases where you know that you should return
-an empty result set and your caller is expecting a ``QuerySet`` object (instead
-of returning an empty list, for example.)
+Calling none() will create a queryset that never returns any objects and no
+query will be executed when accessing the results. A qs.none() queryset
+is an instance of ``EmptyQuerySet``.
Examples::
>>> Entry.objects.none()
[]
+ >>> from django.db.models.query import EmptyQuerySet
+ >>> isinstance(Entry.objects.none(), EmptyQuerySet)
+ True
all
~~~
@@ -740,8 +782,6 @@ prefetch_related
.. method:: prefetch_related(*lookups)
-.. versionadded:: 1.4
-
Returns a ``QuerySet`` that will automatically retrieve, in a single batch,
related objects for each of the specified lookups.
@@ -1112,9 +1152,9 @@ one, doing so will result in an error.
.. note::
- When calling :meth:`~Model.save()` for instances with deferred fields,
- only the loaded fields will be saved. See :meth:`~Model.save()` for more
- details.
+ When calling :meth:`~django.db.models.Model.save()` for instances with
+ deferred fields, only the loaded fields will be saved. See
+ :meth:`~django.db.models.Model.save()` for more details.
only
@@ -1164,9 +1204,9 @@ using :meth:`select_related` is an error as well.
.. note::
- When calling :meth:`~Model.save()` for instances with deferred fields,
- only the loaded fields will be saved. See :meth:`~Model.save()` for more
- details.
+ When calling :meth:`~django.db.models.Model.save()` for instances with
+ deferred fields, only the loaded fields will be saved. See
+ :meth:`~django.db.models.Model.save()` for more details.
using
~~~~~
@@ -1191,8 +1231,6 @@ select_for_update
.. method:: select_for_update(nowait=False)
-.. versionadded:: 1.4
-
Returns a queryset that will lock rows until the end of the transaction,
generating a ``SELECT ... FOR UPDATE`` SQL statement on supported databases.
@@ -1211,11 +1249,6 @@ make the call non-blocking. If a conflicting lock is already acquired by
another transaction, :exc:`~django.db.DatabaseError` will be raised when the
queryset is evaluated.
-Note that using ``select_for_update()`` will cause the current transaction to be
-considered dirty, if under transaction management. This is to ensure that
-Django issues a ``COMMIT`` or ``ROLLBACK``, releasing any locks held by the
-``SELECT FOR UPDATE``.
-
Currently, the ``postgresql_psycopg2``, ``oracle``, and ``mysql`` database
backends support ``select_for_update()``. However, MySQL has no support for the
``nowait`` argument. Obviously, users of external third-party backends should
@@ -1248,7 +1281,7 @@ the format described in `Field lookups`_.
``get()`` raises :exc:`~django.core.exceptions.MultipleObjectsReturned` if more
than one object was found. The
-:exc:`~django.core.excpetions.MultipleObjectsReturned` exception is an
+:exc:`~django.core.exceptions.MultipleObjectsReturned` exception is an
attribute of the model class.
``get()`` raises a :exc:`~django.core.exceptions.DoesNotExist` exception if an
@@ -1368,8 +1401,6 @@ bulk_create
.. method:: bulk_create(objs, batch_size=None)
-.. versionadded:: 1.4
-
This method inserts the provided list of objects into the database in an
efficient manner (generally only 1 query, no matter how many objects there
are)::
@@ -1485,14 +1516,25 @@ This example returns the latest ``Entry`` in the table, according to the
If your model's :ref:`Meta <meta-options>` specifies
:attr:`~django.db.models.Options.get_latest_by`, you can leave off the
-``field_name`` argument to ``latest()``. Django will use the field specified
-in :attr:`~django.db.models.Options.get_latest_by` by default.
+``field_name`` argument to ``earliest()`` or ``latest()``. Django will use the
+field specified in :attr:`~django.db.models.Options.get_latest_by` by default.
-Like :meth:`get()`, ``latest()`` raises
-:exc:`~django.core.exceptions.DoesNotExist` if there is no object with the given
-parameters.
+Like :meth:`get()`, ``earliest()`` and ``latest()`` raise
+:exc:`~django.core.exceptions.DoesNotExist` if there is no object with the
+given parameters.
+
+Note that ``earliest()`` and ``latest()`` exist purely for convenience and
+readability.
+
+earliest
+~~~~~~~~
+
+.. method:: earliest(field_name=None)
+
+.. versionadded:: 1.6
-Note ``latest()`` exists purely for convenience and readability.
+Works otherwise like :meth:`~django.db.models.query.QuerySet.latest` except
+the direction is changed.
aggregate
~~~~~~~~~
@@ -1544,32 +1586,32 @@ The most efficient method of finding whether a model with a unique field
(e.g. ``primary_key``) is a member of a :class:`.QuerySet` is::
entry = Entry.objects.get(pk=123)
- if some_query_set.filter(pk=entry.pk).exists():
+ if some_queryset.filter(pk=entry.pk).exists():
print("Entry contained in queryset")
Which will be faster than the following which requires evaluating and iterating
through the entire queryset::
- if entry in some_query_set:
+ if entry in some_queryset:
print("Entry contained in QuerySet")
And to find whether a queryset contains any items::
- if some_query_set.exists():
- print("There is at least one object in some_query_set")
+ if some_queryset.exists():
+ print("There is at least one object in some_queryset")
Which will be faster than::
- if some_query_set:
- print("There is at least one object in some_query_set")
+ if some_queryset:
+ print("There is at least one object in some_queryset")
... but not by a large degree (hence needing a large queryset for efficiency
gains).
-Additionally, if a ``some_query_set`` has not yet been evaluated, but you know
-that it will be at some point, then using ``some_query_set.exists()`` will do
+Additionally, if a ``some_queryset`` has not yet been evaluated, but you know
+that it will be at some point, then using ``some_queryset.exists()`` will do
more overall work (one query for the existence check plus an extra one to later
-retrieve the results) than simply using ``bool(some_query_set)``, which
+retrieve the results) than simply using ``bool(some_queryset)``, which
retrieves the results and then checks if any were returned.
update
@@ -1637,7 +1679,7 @@ Finally, realize that ``update()`` does an update at the SQL level and, thus,
does not call any ``save()`` methods on your models, nor does it emit the
:attr:`~django.db.models.signals.pre_save` or
:attr:`~django.db.models.signals.post_save` signals (which are a consequence of
-calling :meth:`Model.save() <~django.db.models.Model.save()>`). If you want to
+calling :meth:`Model.save() <django.db.models.Model.save>`). If you want to
update a bunch of records for a model that has a custom
:meth:`~django.db.models.Model.save()` method, loop over them and call
:meth:`~django.db.models.Model.save()`, like this::
@@ -2017,7 +2059,7 @@ numbers and even characters.
year
~~~~
-For date/datetime fields, exact year match. Takes a four-digit year.
+For date and datetime fields, an exact year match. Takes an integer year.
Example::
@@ -2029,6 +2071,9 @@ SQL equivalent::
(The exact SQL syntax varies for each database engine.)
+When :setting:`USE_TZ` is ``True``, datetime fields are converted to the
+current time zone before filtering.
+
.. fieldlookup:: month
month
@@ -2047,12 +2092,15 @@ SQL equivalent::
(The exact SQL syntax varies for each database engine.)
+When :setting:`USE_TZ` is ``True``, datetime fields are converted to the
+current time zone before filtering.
+
.. fieldlookup:: day
day
~~~
-For date and datetime fields, an exact day match.
+For date and datetime fields, an exact day match. Takes an integer day.
Example::
@@ -2067,6 +2115,9 @@ SQL equivalent::
Note this will match any record with a pub_date on the third day of the month,
such as January 3, July 3, etc.
+When :setting:`USE_TZ` is ``True``, datetime fields are converted to the
+current time zone before filtering.
+
.. fieldlookup:: week_day
week_day
@@ -2088,12 +2139,74 @@ Note this will match any record with a ``pub_date`` that falls on a Monday (day
2 of the week), regardless of the month or year in which it occurs. Week days
are indexed with day 1 being Sunday and day 7 being Saturday.
-.. warning::
+When :setting:`USE_TZ` is ``True``, datetime fields are converted to the
+current time zone before filtering.
+
+.. fieldlookup:: hour
+
+hour
+~~~~
+
+.. versionadded:: 1.6
+
+For datetime fields, an exact hour match. Takes an integer between 0 and 23.
- When :doc:`time zone support </topics/i18n/timezones>` is enabled, Django
- uses UTC in the database connection, which means the ``year``, ``month``,
- ``day`` and ``week_day`` lookups are performed in UTC. This is a known
- limitation of the current implementation.
+Example::
+
+ Event.objects.filter(timestamp__hour=23)
+
+SQL equivalent::
+
+ SELECT ... WHERE EXTRACT('hour' FROM timestamp) = '23';
+
+(The exact SQL syntax varies for each database engine.)
+
+When :setting:`USE_TZ` is ``True``, values are converted to the current time
+zone before filtering.
+
+.. fieldlookup:: minute
+
+minute
+~~~~~~
+
+.. versionadded:: 1.6
+
+For datetime fields, an exact minute match. Takes an integer between 0 and 59.
+
+Example::
+
+ Event.objects.filter(timestamp__minute=29)
+
+SQL equivalent::
+
+ SELECT ... WHERE EXTRACT('minute' FROM timestamp) = '29';
+
+(The exact SQL syntax varies for each database engine.)
+
+When :setting:`USE_TZ` is ``True``, values are converted to the current time
+zone before filtering.
+
+.. fieldlookup:: second
+
+second
+~~~~~~
+
+.. versionadded:: 1.6
+
+For datetime fields, an exact second match. Takes an integer between 0 and 59.
+
+Example::
+
+ Event.objects.filter(timestamp__second=31)
+
+SQL equivalent::
+
+ SELECT ... WHERE EXTRACT('second' FROM timestamp) = '31';
+
+(The exact SQL syntax varies for each database engine.)
+
+When :setting:`USE_TZ` is ``True``, values are converted to the current time
+zone before filtering.
.. fieldlookup:: isnull
@@ -2196,6 +2309,14 @@ Django provides the following aggregation functions in the
aggregate functions, see
:doc:`the topic guide on aggregation </topics/db/aggregation>`.
+.. warning::
+
+ SQLite can't handle aggregation on date/time fields out of the box.
+ This is because there are no native date/time fields in SQLite and Django
+ currently emulates these features using a text field. Attempts to use
+ aggregation on date/time fields in SQLite will raise
+ ``NotImplementedError``.
+
Avg
~~~
diff --git a/docs/ref/models/relations.txt b/docs/ref/models/relations.txt
index 37986ec08d..c923961a19 100644
--- a/docs/ref/models/relations.txt
+++ b/docs/ref/models/relations.txt
@@ -82,14 +82,13 @@ Related objects reference
>>> e = Entry.objects.get(id=234)
>>> b.entry_set.remove(e) # Disassociates Entry e from Blog b.
- In order to prevent database inconsistency, this method only exists on
- :class:`~django.db.models.ForeignKey` objects where ``null=True``. If
- the related field can't be set to ``None`` (``NULL``), then an object
- can't be removed from a relation without being added to another. In the
- above example, removing ``e`` from ``b.entry_set()`` is equivalent to
- doing ``e.blog = None``, and because the ``blog``
- :class:`~django.db.models.ForeignKey` doesn't have ``null=True``, this
- is invalid.
+ For :class:`~django.db.models.ForeignKey` objects, this method only
+ exists if ``null=True``. If the related field can't be set to ``None``
+ (``NULL``), then an object can't be removed from a relation without
+ being added to another. In the above example, removing ``e`` from
+ ``b.entry_set()`` is equivalent to doing ``e.blog = None``, and because
+ the ``blog`` :class:`~django.db.models.ForeignKey` doesn't have
+ ``null=True``, this is invalid.
.. method:: clear()
diff --git a/docs/ref/request-response.txt b/docs/ref/request-response.txt
index c3ba99168d..0f62741c5d 100644
--- a/docs/ref/request-response.txt
+++ b/docs/ref/request-response.txt
@@ -34,11 +34,6 @@ All attributes should be considered read-only, unless stated otherwise below.
.. attribute:: HttpRequest.body
- .. versionchanged:: 1.4
-
- Before Django 1.4, ``HttpRequest.body`` was named
- ``HttpRequest.raw_post_data``.
-
The raw HTTP request body as a byte string. This is useful for processing
data in different ways than conventional HTML forms: binary images,
XML payload etc. For processing conventional form data, use ``HttpRequest.POST``.
@@ -55,12 +50,12 @@ All attributes should be considered read-only, unless stated otherwise below.
.. attribute:: HttpRequest.path_info
- Under some Web server configurations, the portion of the URL after the host
- name is split up into a script prefix portion and a path info portion.
- The ``path_info`` attribute always contains the path info portion of the
- path, no matter what Web server is being used. Using this instead of
- attr:`~HttpRequest.path` can make your code much easier to move between test
- and deployment servers.
+ Under some Web server configurations, the portion of the URL after the
+ host name is split up into a script prefix portion and a path info
+ portion. The ``path_info`` attribute always contains the path info portion
+ of the path, no matter what Web server is being used. Using this instead
+ of :attr:`~HttpRequest.path` can make your code easier to move between
+ test and deployment servers.
For example, if the ``WSGIScriptAlias`` for your application is set to
``"/minfo"``, then ``path`` might be ``"/minfo/music/bands/the_beatles/"``
@@ -181,7 +176,7 @@ All attributes should be considered read-only, unless stated otherwise below.
``user`` is only available if your Django installation has the
``AuthenticationMiddleware`` activated. For more, see
- :doc:`/topics/auth`.
+ :doc:`/topics/auth/index`.
.. attribute:: HttpRequest.session
@@ -267,10 +262,8 @@ Methods
.. method:: HttpRequest.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
- .. versionadded:: 1.4
-
Returns a cookie value for a signed cookie, or raises a
- :class:`~django.core.signing.BadSignature` exception if the signature is
+ ``django.core.signing.BadSignature`` exception if the signature is
no longer valid. If you provide the ``default`` argument the exception
will be suppressed and that default value will be returned instead.
@@ -478,9 +471,6 @@ In addition, ``QueryDict`` has the following methods:
It's guaranteed to return a list of some sort unless the default value
was no list.
- .. versionchanged:: 1.4
- The ``default`` parameter was added.
-
.. method:: QueryDict.setlist(key, list_)
Sets the given key to ``list_`` (unlike ``__setitem__()``).
@@ -505,8 +495,6 @@ In addition, ``QueryDict`` has the following methods:
.. method:: QueryDict.dict()
- .. versionadded:: 1.4
-
Returns ``dict`` representation of ``QueryDict``. For every (key, list)
pair in ``QueryDict``, ``dict`` will have (key, item), where item is one
element of the list, using same logic as :meth:`QueryDict.__getitem__()`::
@@ -708,9 +696,7 @@ Methods
.. _HTTPOnly: https://www.owasp.org/index.php/HTTPOnly
-.. method:: HttpResponse.set_signed_cookie(key, value='', salt='', max_age=None, expires=None, path='/', domain=None, secure=None, httponly=True)
-
- .. versionadded:: 1.4
+.. method:: HttpResponse.set_signed_cookie(key, value, salt='', max_age=None, expires=None, path='/', domain=None, secure=None, httponly=True)
Like :meth:`~HttpResponse.set_cookie()`, but
:doc:`cryptographic signing </topics/signing>` the cookie before setting
@@ -760,6 +746,13 @@ types of HTTP responses. Like ``HttpResponse``, these subclasses live in
domain (e.g. ``'/search/'``). See :class:`HttpResponse` for other optional
constructor arguments. Note that this returns an HTTP status code 302.
+ .. attribute:: HttpResponseRedirect.url
+
+ .. versionadded:: 1.6
+
+ This read-only attribute represents the URL the response will redirect
+ to (equivalent to the ``Location`` response header).
+
.. class:: HttpResponsePermanentRedirect
Like :class:`HttpResponseRedirect`, but it returns a permanent redirect
@@ -804,6 +797,8 @@ types of HTTP responses. Like ``HttpResponse``, these subclasses live in
:class:`~django.template.response.SimpleTemplateResponse`, and the
``render`` method must itself return a valid response object.
+.. _httpresponse-streaming:
+
StreamingHttpResponse objects
=============================
@@ -819,8 +814,8 @@ generating large CSV files.
.. admonition:: Performance considerations
Django is designed for short-lived requests. Streaming responses will tie
- a worker process and keep a database connection idle in transaction for
- the entire duration of the response. This may result in poor performance.
+ a worker process for the entire duration of the response. This may result
+ in poor performance.
Generally speaking, you should perform expensive tasks outside of the
request-response cycle, rather than resorting to a streamed response.
diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt
index daa4ee9a46..1fc9d2ff92 100644
--- a/docs/ref/settings.txt
+++ b/docs/ref/settings.txt
@@ -13,11 +13,12 @@ Settings
and :setting:`TEMPLATE_CONTEXT_PROCESSORS`. Make sure you keep the
components required by the features of Django you wish to use.
-Available settings
-==================
+Core settings
+=============
-Here's a full list of all available settings, in alphabetical order, and their
-default values.
+Here's a list of settings available in Django core and their default values.
+Settings provided by contrib apps are listed below, followed by a topical index
+of the core settings.
.. setting:: ABSOLUTE_URL_OVERRIDES
@@ -38,19 +39,6 @@ a model object and return its URL. This is a way of overriding
Note that the model name used in this setting should be all lower-case, regardless
of the case of the actual model class name.
-.. setting:: ADMIN_FOR
-
-ADMIN_FOR
----------
-
-Default: ``()`` (Empty tuple)
-
-Used for admin-site settings modules, this should be a tuple of settings
-modules (in the format ``'foo.bar.baz'``) for which this site is an admin.
-
-The admin site uses this in its automatically-introspected documentation of
-models, views and template tags.
-
.. setting:: ADMINS
ADMINS
@@ -68,6 +56,42 @@ of (Full name, email address). Example::
Note that Django will email *all* of these people whenever an error happens.
See :doc:`/howto/error-reporting` for more information.
+.. setting:: ALLOWED_HOSTS
+
+ALLOWED_HOSTS
+-------------
+
+Default: ``[]`` (Empty list)
+
+A list of strings representing the host/domain names that this Django site can
+serve. This is a security measure to prevent an attacker from poisoning caches
+and password reset emails with links to malicious hosts by submitting requests
+with a fake HTTP ``Host`` header, which is possible even under many
+seemingly-safe webserver configurations.
+
+Values in this list can be fully qualified names (e.g. ``'www.example.com'``),
+in which case they will be matched against the request's ``Host`` header
+exactly (case-insensitive, not including port). A value beginning with a period
+can be used as a subdomain wildcard: ``'.example.com'`` will match
+``example.com``, ``www.example.com``, and any other subdomain of
+``example.com``. A value of ``'*'`` will match anything; in this case you are
+responsible to provide your own validation of the ``Host`` header (perhaps in a
+middleware; if so this middleware must be listed first in
+:setting:`MIDDLEWARE_CLASSES`).
+
+If the ``Host`` header (or ``X-Forwarded-Host`` if
+:setting:`USE_X_FORWARDED_HOST` is enabled) does not match any value in this
+list, the :meth:`django.http.HttpRequest.get_host()` method will raise
+:exc:`~django.core.exceptions.SuspiciousOperation`.
+
+When :setting:`DEBUG` is ``True`` or when running tests, host validation is
+disabled; any host will be accepted. Thus it's usually only necessary to set it
+in production.
+
+This validation only applies via :meth:`~django.http.HttpRequest.get_host()`;
+if your code accesses the ``Host`` header directly from ``request.META`` you
+are bypassing this security protection.
+
.. setting:: ALLOWED_INCLUDE_ROOTS
ALLOWED_INCLUDE_ROOTS
@@ -99,26 +123,6 @@ The :setting:`APPEND_SLASH` setting is only used if
:class:`~django.middleware.common.CommonMiddleware` is installed
(see :doc:`/topics/http/middleware`). See also :setting:`PREPEND_WWW`.
-.. setting:: AUTHENTICATION_BACKENDS
-
-AUTHENTICATION_BACKENDS
------------------------
-
-Default: ``('django.contrib.auth.backends.ModelBackend',)``
-
-A tuple of authentication backend classes (as strings) to use when attempting to
-authenticate a user. See the :doc:`authentication backends documentation
-</ref/authbackends>` for details.
-
-.. setting:: AUTH_USER_MODEL
-
-AUTH_USER_MODEL
----------------
-
-Default: 'auth.User'
-
-The model to use to represent a User. See :ref:`auth-custom-user`.
-
.. setting:: CACHES
CACHES
@@ -159,7 +163,7 @@ The cache backend to use. The built-in cache backends are:
* ``'django.core.cache.backends.memcached.PyLibMCCache'``
You can use a cache backend that doesn't ship with Django by setting
-:setting:`BACKEND <CACHE-BACKEND>` to a fully-qualified path of a cache
+:setting:`BACKEND <CACHES-BACKEND>` to a fully-qualified path of a cache
backend class (i.e. ``mypackage.backends.whatever.WhateverCache``).
Writing a whole new cache backend from scratch is left as an exercise
to the reader; see the other backends for examples.
@@ -179,7 +183,8 @@ implementation is equivalent to the function::
You may use any key function you want, as long as it has the same
argument signature.
-See the :ref:`cache documentation <cache_key_transformation>` for more information.
+See the :ref:`cache documentation <cache_key_transformation>` for more
+information.
.. setting:: CACHES-KEY_PREFIX
@@ -293,6 +298,8 @@ The default number of seconds to cache a page when the caching middleware or
See :doc:`/topics/cache`.
+.. _settings-csrf:
+
.. setting:: CSRF_COOKIE_DOMAIN
CSRF_COOKIE_DOMAIN
@@ -304,12 +311,25 @@ The domain to be used when setting the CSRF cookie. This can be useful for
easily allowing cross-subdomain requests to be excluded from the normal cross
site request forgery protection. It should be set to a string such as
``".example.com"`` to allow a POST request from a form on one subdomain to be
-accepted by accepted by a view served from another subdomain.
+accepted by a view served from another subdomain.
Please note that the presence of this setting does not imply that Django's CSRF
protection is safe from cross-subdomain attacks by default - please see the
:ref:`CSRF limitations <csrf-limitations>` section.
+.. setting:: CSRF_COOKIE_HTTPONLY
+
+CSRF_COOKIE_HTTPONLY
+--------------------
+
+.. versionadded:: 1.6
+
+Default: ``False``
+
+Whether to use HttpOnly flag on the CSRF cookie. If this is set to ``True``,
+client-side JavaScript will not to be able to access the CSRF cookie. See
+:setting:`SESSION_COOKIE_HTTPONLY` for details on HttpOnly.
+
.. setting:: CSRF_COOKIE_NAME
CSRF_COOKIE_NAME
@@ -325,8 +345,6 @@ want. See :doc:`/ref/contrib/csrf`.
CSRF_COOKIE_PATH
----------------
-.. versionadded:: 1.4
-
Default: ``'/'``
The path set on the CSRF cookie. This should either match the URL path of your
@@ -341,8 +359,6 @@ its own CSRF cookie.
CSRF_COOKIE_SECURE
------------------
-.. versionadded:: 1.4
-
Default: ``False``
Whether to use a secure cookie for the CSRF cookie. If this is set to ``True``,
@@ -365,7 +381,6 @@ where ``reason`` is a short message (intended for developers or logging, not for
end users) indicating the reason the request was rejected. See
:doc:`/ref/contrib/csrf`.
-
.. setting:: DATABASES
DATABASES
@@ -393,6 +408,30 @@ SQLite. This can be configured using the following::
For other database backends, or more complex SQLite configurations, other options
will be required. The following inner options are available.
+.. setting:: DATABASE-ATOMIC_REQUESTS
+
+ATOMIC_REQUESTS
+~~~~~~~~~~~~~~~
+
+.. versionadded:: 1.6
+
+Default: ``False``
+
+Set this to ``True`` to wrap each HTTP request in a transaction on this
+database. See :ref:`tying-transactions-to-http-requests`.
+
+.. setting:: DATABASE-AUTOCOMMIT
+
+AUTOCOMMIT
+~~~~~~~~~~
+
+.. versionadded:: 1.6
+
+Default: ``True``
+
+Set this to ``False`` if you want to :ref:`disable Django's transaction
+management <deactivate-transaction-management>` and implement your own.
+
.. setting:: DATABASE-ENGINE
ENGINE
@@ -449,6 +488,19 @@ The name of the database to use. For SQLite, it's the full path to the database
file. When specifying the path, always use forward slashes, even on Windows
(e.g. ``C:/homes/user/mysite/sqlite3.db``).
+.. setting:: CONN_MAX_AGE
+
+CONN_MAX_AGE
+~~~~~~~~~~~~
+
+.. versionadded:: 1.6
+
+Default: ``600``
+
+The lifetime of a database connection, in seconds. Use ``0`` to close database
+connections at the end of each request — Django's historical behavior — and
+``None`` for unlimited persistent connections.
+
.. setting:: OPTIONS
OPTIONS
@@ -505,7 +557,7 @@ backend-specific.
Supported for the PostgreSQL_ (``postgresql_psycopg2``) and MySQL_ (``mysql``)
backends.
-.. _PostgreSQL: http://www.postgresql.org/docs/8.2/static/multibyte.html
+.. _PostgreSQL: http://www.postgresql.org/docs/current/static/multibyte.html
.. _MySQL: http://dev.mysql.com/doc/refman/5.0/en/charset-database.html
.. setting:: TEST_COLLATION
@@ -562,7 +614,7 @@ If the default value (``None``) is used with the SQLite database engine, the
tests will use a memory resident database. For all other database engines the
test database will use the name ``'test_' + DATABASE_NAME``.
-See :doc:`/topics/testing`.
+See :ref:`the-test-database`.
.. setting:: TEST_CREATE
@@ -670,9 +722,13 @@ DATE_INPUT_FORMATS
Default::
- ('%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', '%b %d %Y',
- '%b %d, %Y', '%d %b %Y', '%d %b, %Y', '%B %d %Y',
- '%B %d, %Y', '%d %B %Y', '%d %B, %Y')
+ (
+ '%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06'
+ '%b %d %Y', '%b %d, %Y', # 'Oct 25 2006', 'Oct 25, 2006'
+ '%d %b %Y', '%d %b, %Y', # '25 Oct 2006', '25 Oct, 2006'
+ '%B %d %Y', '%B %d, %Y', # 'October 25 2006', 'October 25, 2006'
+ '%d %B %Y', '%d %B, %Y', # '25 October 2006', '25 October, 2006'
+ )
A tuple of formats that will be accepted when inputting data on a date field.
Formats will be tried in order, using the first valid one. Note that these
@@ -707,9 +763,20 @@ DATETIME_INPUT_FORMATS
Default::
- ('%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M', '%Y-%m-%d',
- '%m/%d/%Y %H:%M:%S', '%m/%d/%Y %H:%M', '%m/%d/%Y',
- '%m/%d/%y %H:%M:%S', '%m/%d/%y %H:%M', '%m/%d/%y')
+ (
+ '%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59'
+ '%Y-%m-%d %H:%M:%S.%f', # '2006-10-25 14:30:59.000200'
+ '%Y-%m-%d %H:%M', # '2006-10-25 14:30'
+ '%Y-%m-%d', # '2006-10-25'
+ '%m/%d/%Y %H:%M:%S', # '10/25/2006 14:30:59'
+ '%m/%d/%Y %H:%M:%S.%f', # '10/25/2006 14:30:59.000200'
+ '%m/%d/%Y %H:%M', # '10/25/2006 14:30'
+ '%m/%d/%Y', # '10/25/2006'
+ '%m/%d/%y %H:%M:%S', # '10/25/06 14:30:59'
+ '%m/%d/%y %H:%M:%S.%f', # '10/25/06 14:30:59.000200'
+ '%m/%d/%y %H:%M', # '10/25/06 14:30'
+ '%m/%d/%y', # '10/25/06'
+ )
A tuple of formats that will be accepted when inputting data on a datetime
field. Formats will be tried in order, using the first valid one. Note that
@@ -748,18 +815,13 @@ sensitive (or offensive), such as :setting:`SECRET_KEY` or
:setting:`PROFANITIES_LIST`. Specifically, it will exclude any setting whose
name includes any of the following:
- * API
- * KEY
- * PASS
- * PROFANITIES_LIST
- * SECRET
- * SIGNATURE
- * TOKEN
-
-.. versionchanged:: 1.4
-
- We changed ``'PASSWORD'`` ``'PASS'``. ``'API'``, ``'TOKEN'`` and ``'KEY'``
- were added.
+* ``'API'``
+* ``'KEY'``
+* ``'PASS'``
+* ``'PROFANITIES_LIST'``
+* ``'SECRET'``
+* ``'SIGNATURE'``
+* ``'TOKEN'``
Note that these are *partial* matches. ``'PASS'`` will also match PASSWORD,
just as ``'TOKEN'`` will also match TOKENIZED and so on.
@@ -774,6 +836,8 @@ when you're debugging, but it'll rapidly consume memory on a production server.
.. _django/views/debug.py: https://github.com/django/django/blob/master/django/views/debug.py
+.. setting:: DEBUG_PROPAGATE_EXCEPTIONS
+
DEBUG_PROPAGATE_EXCEPTIONS
--------------------------
@@ -830,7 +894,7 @@ DEFAULT_EXCEPTION_REPORTER_FILTER
Default: :class:`django.views.debug.SafeExceptionReporterFilter`
Default exception reporter filter class to be used if none has been assigned to
-the :class:`HttpRequest` instance yet.
+the :class:`~django.http.HttpRequest` instance yet.
See :ref:`Filtering error reports<filtering-error-reports>`.
.. setting:: DEFAULT_FILE_STORAGE
@@ -1070,6 +1134,8 @@ Note that these paths should use Unix-style forward slashes, even on Windows.
See :ref:`initial-data-via-fixtures` and :ref:`topics-testing-fixtures`.
+.. setting:: FORCE_SCRIPT_NAME
+
FORCE_SCRIPT_NAME
------------------
@@ -1115,8 +1181,6 @@ Available formats are :setting:`DATE_FORMAT`, :setting:`TIME_FORMAT`,
IGNORABLE_404_URLS
------------------
-.. versionadded:: 1.4
-
Default: ``()``
List of compiled regular expression objects describing URLs that should be
@@ -1127,8 +1191,9 @@ query string, if any). Use this if your site does not provide a commonly
requested file such as ``favicon.ico`` or ``robots.txt``, or if it gets
hammered by script kiddies.
-This is only used if :setting:`SEND_BROKEN_LINK_EMAILS` is set to ``True`` and
-``CommonMiddleware`` is installed (see :doc:`/topics/http/middleware`).
+This is only used if
+:class:`~django.middleware.common.BrokenLinkEmailsMiddleware` is enabled (see
+:doc:`/topics/http/middleware`).
.. setting:: INSTALLED_APPS
@@ -1170,9 +1235,12 @@ LANGUAGE_CODE
Default: ``'en-us'``
-A string representing the language code for this installation. This should be in
-standard :term:`language format<language code>`. For example, U.S. English is
-``"en-us"``. See :doc:`/topics/i18n/index`.
+A string representing the language code for this installation. This should be
+in standard :term:`language format<language code>`. For example, U.S. English
+is ``"en-us"``. See also the `list of language identifiers`_ and
+:doc:`/topics/i18n/index`.
+
+.. _list of language identifiers: http://www.i18nguy.com/unicode/language-identifiers.html
.. setting:: LANGUAGE_COOKIE_NAME
@@ -1197,9 +1265,9 @@ see the current list of translated languages by looking in
.. _online source: https://github.com/django/django/blob/master/django/conf/global_settings.py
-The list is a tuple of two-tuples in the format ``(language code, language
-name)``, the ``language code`` part should be a
-:term:`language name<language code>` -- for example, ``('ja', 'Japanese')``.
+The list is a tuple of two-tuples in the format
+(:term:`language code<language code>`, ``language name``) -- for example,
+``('ja', 'Japanese')``.
This specifies which languages are available for language selection. See
:doc:`/topics/i18n/index`.
@@ -1279,54 +1347,6 @@ configuration process will be skipped.
.. _dictConfig: http://docs.python.org/library/logging.config.html#configuration-dictionary-schema
-.. setting:: LOGIN_REDIRECT_URL
-
-LOGIN_REDIRECT_URL
-------------------
-
-Default: ``'/accounts/profile/'``
-
-The URL where requests are redirected after login when the
-``contrib.auth.login`` view gets no ``next`` parameter.
-
-This is used by the :func:`~django.contrib.auth.decorators.login_required`
-decorator, for example.
-
-.. versionchanged:: 1.5
-
-This setting now also accepts view function names and
-:ref:`named URL patterns <naming-url-patterns>` which can be used to reduce
-configuration duplication since you no longer have to define the URL in two
-places (``settings`` and URLconf).
-For backward compatibility reasons the default remains unchanged.
-
-.. setting:: LOGIN_URL
-
-LOGIN_URL
----------
-
-Default: ``'/accounts/login/'``
-
-The URL where requests are redirected for login, especially when using the
-:func:`~django.contrib.auth.decorators.login_required` decorator.
-
-.. versionchanged:: 1.5
-
-This setting now also accepts view function names and
-:ref:`named URL patterns <naming-url-patterns>` which can be used to reduce
-configuration duplication since you no longer have to define the URL in two
-places (``settings`` and URLconf).
-For backward compatibility reasons the default remains unchanged.
-
-.. setting:: LOGOUT_URL
-
-LOGOUT_URL
-----------
-
-Default: ``'/accounts/logout/'``
-
-LOGIN_URL counterpart.
-
.. setting:: MANAGERS
MANAGERS
@@ -1335,7 +1355,8 @@ MANAGERS
Default: ``()`` (Empty tuple)
A tuple in the same format as :setting:`ADMINS` that specifies who should get
-broken-link notifications when :setting:`SEND_BROKEN_LINK_EMAILS` is ``True``.
+broken link notifications when
+:class:`~django.middleware.common.BrokenLinkEmailsMiddleware` is enabled.
.. setting:: MEDIA_ROOT
@@ -1364,37 +1385,6 @@ to a non-empty value.
Example: ``"http://media.example.com/"``
-MESSAGE_LEVEL
--------------
-
-Default: `messages.INFO`
-
-Sets the minimum message level that will be recorded by the messages
-framework. See the :doc:`messages documentation </ref/contrib/messages>` for
-more details.
-
-MESSAGE_STORAGE
----------------
-
-Default: ``'django.contrib.messages.storage.fallback.FallbackStorage'``
-
-Controls where Django stores message data. See the
-:doc:`messages documentation </ref/contrib/messages>` for more details.
-
-MESSAGE_TAGS
-------------
-
-Default::
-
- {messages.DEBUG: 'debug',
- messages.INFO: 'info',
- messages.SUCCESS: 'success',
- messages.WARNING: 'warning',
- messages.ERROR: 'error',}
-
-Sets the mapping of message levels to message tags. See the
-:doc:`messages documentation </ref/contrib/messages>` for more details.
-
.. setting:: MIDDLEWARE_CLASSES
MIDDLEWARE_CLASSES
@@ -1450,16 +1440,6 @@ format has higher precedence and will be applied instead.
See also :setting:`DECIMAL_SEPARATOR`, :setting:`THOUSAND_SEPARATOR` and
:setting:`USE_THOUSAND_SEPARATOR`.
-.. setting:: PASSWORD_RESET_TIMEOUT_DAYS
-
-PASSWORD_RESET_TIMEOUT_DAYS
----------------------------
-
-Default: ``3``
-
-The number of days a password reset link is valid for. Used by the
-:mod:`django.contrib.auth` password reset mechanism.
-
.. setting:: PREPEND_WWW
PREPEND_WWW
@@ -1471,30 +1451,6 @@ Whether to prepend the "www." subdomain to URLs that don't have it. This is only
used if :class:`~django.middleware.common.CommonMiddleware` is installed
(see :doc:`/topics/http/middleware`). See also :setting:`APPEND_SLASH`.
-.. setting:: PROFANITIES_LIST
-
-PROFANITIES_LIST
-----------------
-
-Default: ``()`` (Empty tuple)
-
-A tuple of profanities, as strings, that will be forbidden in comments when
-:setting:`COMMENTS_ALLOW_PROFANITIES` is ``False``.
-
-.. setting:: RESTRUCTUREDTEXT_FILTER_SETTINGS
-
-RESTRUCTUREDTEXT_FILTER_SETTINGS
---------------------------------
-
-Default: ``{}``
-
-A dictionary containing settings for the ``restructuredtext`` markup filter from
-the :doc:`django.contrib.markup application </ref/contrib/markup>`. They override
-the default writer settings. See the Docutils restructuredtext `writer settings
-docs`_ for details.
-
-.. _writer settings docs: http://docutils.sourceforge.net/docs/user/config.html#html4css1-writer
-
.. setting:: ROOT_URLCONF
ROOT_URLCONF
@@ -1537,8 +1493,6 @@ randomly-generated ``SECRET_KEY`` to each new project.
SECURE_PROXY_SSL_HEADER
-----------------------
-.. versionadded:: 1.4
-
Default: ``None``
A tuple representing a HTTP header/value combination that signifies a request
@@ -1600,6 +1554,11 @@ available in ``request.META``.)
SEND_BROKEN_LINK_EMAILS
-----------------------
+.. deprecated:: 1.6
+ Since :class:`~django.middleware.common.BrokenLinkEmailsMiddleware`
+ was split from :class:`~django.middleware.common.CommonMiddleware`,
+ this setting no longer serves a purpose.
+
Default: ``False``
Whether to send an email to the :setting:`MANAGERS` each time somebody visits
@@ -1631,144 +1590,6 @@ Default: ``'root@localhost'``
The email address that error messages come from, such as those sent to
:setting:`ADMINS` and :setting:`MANAGERS`.
-.. setting:: SESSION_COOKIE_AGE
-
-SESSION_COOKIE_AGE
-------------------
-
-Default: ``1209600`` (2 weeks, in seconds)
-
-The age of session cookies, in seconds. See :doc:`/topics/http/sessions`.
-
-.. setting:: SESSION_COOKIE_DOMAIN
-
-SESSION_COOKIE_DOMAIN
----------------------
-
-Default: ``None``
-
-The domain to use for session cookies. Set this to a string such as
-``".example.com"`` for cross-domain cookies, or use ``None`` for a standard
-domain cookie. See the :doc:`/topics/http/sessions`.
-
-.. setting:: SESSION_COOKIE_HTTPONLY
-
-SESSION_COOKIE_HTTPONLY
------------------------
-
-Default: ``True``
-
-Whether to use HTTPOnly flag on the session cookie. If this is set to
-``True``, client-side JavaScript will not to be able to access the
-session cookie.
-
-HTTPOnly_ is a flag included in a Set-Cookie HTTP response header. It
-is not part of the :rfc:`2109` standard for cookies, and it isn't honored
-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: https://www.owasp.org/index.php/HTTPOnly
-
-.. versionchanged:: 1.4
- The default value of the setting was changed from ``False`` to ``True``.
-
-.. setting:: SESSION_COOKIE_NAME
-
-SESSION_COOKIE_NAME
--------------------
-
-Default: ``'sessionid'``
-
-The name of the cookie to use for sessions. This can be whatever you want (but
-should be different from :setting:`LANGUAGE_COOKIE_NAME`).
-See the :doc:`/topics/http/sessions`.
-
-.. setting:: SESSION_COOKIE_PATH
-
-SESSION_COOKIE_PATH
--------------------
-
-Default: ``'/'``
-
-The path set on the session cookie. This should either match the URL path of your
-Django installation or be parent of that path.
-
-This is useful if you have multiple Django instances running under the same
-hostname. They can use different cookie paths, and each instance will only see
-its own session cookie.
-
-.. setting:: SESSION_CACHE_ALIAS
-
-SESSION_CACHE_ALIAS
--------------------
-
-Default: ``default``
-
-If you're using :ref:`cache-based session storage <cached-sessions-backend>`,
-this selects the cache to use.
-
-.. setting:: SESSION_COOKIE_SECURE
-
-SESSION_COOKIE_SECURE
----------------------
-
-Default: ``False``
-
-Whether to use a secure cookie for the session cookie. If this is set to
-``True``, the cookie will be marked as "secure," which means browsers may
-ensure that the cookie is only sent under an HTTPS connection.
-See the :doc:`/topics/http/sessions`.
-
-.. setting:: SESSION_ENGINE
-
-SESSION_ENGINE
---------------
-
-Default: ``django.contrib.sessions.backends.db``
-
-Controls where Django stores session data. Valid values are:
-
-* ``'django.contrib.sessions.backends.db'``
-* ``'django.contrib.sessions.backends.file'``
-* ``'django.contrib.sessions.backends.cache'``
-* ``'django.contrib.sessions.backends.cached_db'``
-* ``'django.contrib.sessions.backends.signed_cookies'``
-
-See :doc:`/topics/http/sessions`.
-
-.. setting:: SESSION_EXPIRE_AT_BROWSER_CLOSE
-
-SESSION_EXPIRE_AT_BROWSER_CLOSE
--------------------------------
-
-Default: ``False``
-
-Whether to expire the session when the user closes his or her browser.
-See the :doc:`/topics/http/sessions`.
-
-.. setting:: SESSION_FILE_PATH
-
-SESSION_FILE_PATH
------------------
-
-Default: ``None``
-
-If you're using file-based session storage, this sets the directory in
-which Django will store session data. See :doc:`/topics/http/sessions`. When
-the default value (``None``) is used, Django will use the standard temporary
-directory for the system.
-
-.. setting:: SESSION_SAVE_EVERY_REQUEST
-
-SESSION_SAVE_EVERY_REQUEST
---------------------------
-
-Default: ``False``
-
-Whether to save the session data on every request. See
-:doc:`/topics/http/sessions`.
-
.. setting:: SHORT_DATE_FORMAT
SHORT_DATE_FORMAT
@@ -1802,79 +1623,12 @@ See also :setting:`DATE_FORMAT` and :setting:`SHORT_DATE_FORMAT`.
SIGNING_BACKEND
---------------
-.. versionadded:: 1.4
-
Default: 'django.core.signing.TimestampSigner'
The backend used for signing cookies and other data.
See also the :doc:`/topics/signing` documentation.
-.. setting:: SITE_ID
-
-SITE_ID
--------
-
-Default: Not defined
-
-The ID, as an integer, of the current site in the ``django_site`` database
-table. This is used so that application data can hook into specific site(s)
-and a single database can manage content for multiple sites.
-
-See :doc:`/ref/contrib/sites`.
-
-.. _site framework docs: ../sites/
-
-.. setting:: STATIC_ROOT
-
-STATIC_ROOT
------------
-
-Default: ``''`` (Empty string)
-
-The absolute path to the directory where :djadmin:`collectstatic` will collect
-static files for deployment.
-
-Example: ``"/var/www/example.com/static/"``
-
-If the :doc:`staticfiles</ref/contrib/staticfiles>` contrib app is enabled
-(default) the :djadmin:`collectstatic` management command will collect static
-files into this directory. See the howto on :doc:`managing static
-files</howto/static-files>` for more details about usage.
-
-.. warning::
-
- This should be an (initially empty) destination directory for collecting
- your static files from their permanent locations into one directory for
- ease of deployment; it is **not** a place to store your static files
- permanently. You should do that in directories that will be found by
- :doc:`staticfiles</ref/contrib/staticfiles>`'s
- :setting:`finders<STATICFILES_FINDERS>`, which by default, are
- ``'static/'`` app sub-directories and any directories you include in
- :setting:`STATICFILES_DIRS`).
-
-See :doc:`staticfiles reference</ref/contrib/staticfiles>` and
-:setting:`STATIC_URL`.
-
-.. setting:: STATIC_URL
-
-STATIC_URL
-----------
-
-Default: ``None``
-
-URL to use when referring to static files located in :setting:`STATIC_ROOT`.
-
-Example: ``"/static/"`` or ``"http://static.example.com/"``
-
-If not ``None``, this will be used as the base path for
-:ref:`media definitions<form-media-paths>` and the
-:doc:`staticfiles app</ref/contrib/staticfiles>`.
-
-It must end in a slash if set to a non-empty value.
-
-See :setting:`STATIC_ROOT`.
-
.. setting:: TEMPLATE_CONTEXT_PROCESSORS
TEMPLATE_CONTEXT_PROCESSORS
@@ -1894,10 +1648,6 @@ A tuple of callables that are used to populate the context in ``RequestContext``
These callables take a request object as their argument and return a dictionary
of items to be merged into the context.
-.. versionadded:: 1.4
- The ``django.core.context_processors.tz`` context processor
- was added in this release.
-
.. setting:: TEMPLATE_DEBUG
TEMPLATE_DEBUG
@@ -1963,9 +1713,7 @@ TEST_RUNNER
Default: ``'django.test.simple.DjangoTestSuiteRunner'``
The name of the class to use for starting the test suite. See
-:doc:`/topics/testing`.
-
-.. _Testing Django Applications: ../testing/
+:ref:`other-testing-frameworks`.
.. setting:: THOUSAND_SEPARATOR
@@ -2003,7 +1751,13 @@ See also :setting:`DATE_FORMAT` and :setting:`DATETIME_FORMAT`.
TIME_INPUT_FORMATS
------------------
-Default: ``('%H:%M:%S', '%H:%M')``
+Default::
+
+ (
+ '%H:%M:%S', # '14:30:59'
+ '%H:%M:%S.%f', # '14:30:59.000200'
+ '%H:%M', # '14:30'
+ )
A tuple of formats that will be accepted when inputting data on a time field.
Formats will be tried in order, using the first valid one. Note that these
@@ -2015,6 +1769,10 @@ precedence and will be applied instead.
See also :setting:`DATE_INPUT_FORMATS` and :setting:`DATETIME_INPUT_FORMATS`.
+.. versionchanged:: 1.6
+
+Input format with microseconds has been added.
+
.. _datetime: http://docs.python.org/library/datetime.html#strftime-strptime-behavior
.. setting:: TIME_ZONE
@@ -2024,15 +1782,14 @@ TIME_ZONE
Default: ``'America/Chicago'``
-.. versionchanged:: 1.4
- The meaning of this setting now depends on the value of :setting:`USE_TZ`.
+A string representing the time zone for this installation, or ``None``. See
+the `list of time zones`_.
-A string representing the time zone for this installation, or
-``None``. `See available choices`_. (Note that list of available
-choices lists more than one on the same line; you'll want to use just
-one of the choices for a given time zone. For instance, one line says
-``'Europe/London GB GB-Eire'``, but you should use the first bit of
-that -- ``'Europe/London'`` -- as your :setting:`TIME_ZONE` setting.)
+.. note::
+ Since Django was first released with the :setting:`TIME_ZONE` set to
+ ``'America/Chicago'``, the global setting (used if nothing is defined in
+ your project's ``settings.py``) remains ``'America/Chicago'`` for backwards
+ compatibility. New project templates default to ``'UTC'``.
Note that this isn't necessarily the time zone of the server. For example, one
server may serve multiple Django-powered sites, each with a separate time zone
@@ -2065,7 +1822,7 @@ to ensure your processes are running in the correct environment.
If you're running Django on Windows, :setting:`TIME_ZONE` must be set to
match the system time zone.
-.. _See available choices: http://www.postgresql.org/docs/8.1/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
+.. _list of time zones: http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
.. _pytz: http://pytz.sourceforge.net/
@@ -2074,6 +1831,12 @@ to ensure your processes are running in the correct environment.
TRANSACTIONS_MANAGED
--------------------
+.. deprecated:: 1.6
+
+ This setting was deprecated because its name is very misleading. Use the
+ :setting:`AUTOCOMMIT <DATABASE-AUTOCOMMIT>` key in :setting:`DATABASES`
+ entries instead.
+
Default: ``False``
Set this to ``True`` if you want to :ref:`disable Django's transaction
@@ -2143,8 +1906,6 @@ See also :setting:`DECIMAL_SEPARATOR`, :setting:`NUMBER_GROUPING` and
USE_TZ
------
-.. versionadded:: 1.4
-
Default: ``False``
A boolean that specifies if datetimes will be timezone-aware by default or not.
@@ -2175,8 +1936,6 @@ which sets this header is in use.
WSGI_APPLICATION
----------------
-.. versionadded:: 1.4
-
Default: ``None``
The full Python path of the WSGI application object that Django's built-in
@@ -2185,7 +1944,7 @@ startproject <startproject>` management command will create a simple
``wsgi.py`` file with an ``application`` callable in it, and point this setting
to that ``application``.
-If not set, the return value of :func:`django.core.wsgi.get_wsgi_application`
+If not set, the return value of ``django.core.wsgi.get_wsgi_application()``
will be used. In this case, the behavior of :djadmin:`runserver` will be
identical to previous Django versions.
@@ -2220,8 +1979,41 @@ The default value for the X-Frame-Options header used by
:class:`~django.middleware.clickjacking.XFrameOptionsMiddleware`. See the
:doc:`clickjacking protection </ref/clickjacking/>` documentation.
-Deprecated settings
-===================
+
+Admindocs
+=========
+
+Settings for :mod:`django.contrib.admindocs`.
+
+.. setting:: ADMIN_FOR
+
+ADMIN_FOR
+---------
+
+Default: ``()`` (Empty tuple)
+
+Used for admin-site settings modules, this should be a tuple of settings
+modules (in the format ``'foo.bar.baz'``) for which this site is an admin.
+
+The admin site uses this in its automatically-introspected documentation of
+models, views and template tags.
+
+
+Auth
+====
+
+Settings for :mod:`django.contrib.auth`.
+
+.. setting:: AUTHENTICATION_BACKENDS
+
+AUTHENTICATION_BACKENDS
+-----------------------
+
+Default: ``('django.contrib.auth.backends.ModelBackend',)``
+
+A tuple of authentication backend classes (as strings) to use when attempting to
+authenticate a user. See the :ref:`authentication backends documentation
+<authentication-backends>` for details.
.. setting:: AUTH_PROFILE_MODULE
@@ -2237,29 +2029,697 @@ AUTH_PROFILE_MODULE
Default: Not defined
The site-specific user profile model used by this site. See
-:ref:`auth-profiles`.
+:ref:`User profiles <auth-profiles>`.
-.. setting:: IGNORABLE_404_ENDS
+.. setting:: AUTH_USER_MODEL
+
+AUTH_USER_MODEL
+---------------
-IGNORABLE_404_ENDS
+Default: 'auth.User'
+
+The model to use to represent a User. See :ref:`auth-custom-user`.
+
+.. setting:: LOGIN_REDIRECT_URL
+
+LOGIN_REDIRECT_URL
------------------
-.. deprecated:: 1.4
- This setting has been superseded by :setting:`IGNORABLE_404_URLS`.
+Default: ``'/accounts/profile/'``
-.. setting:: IGNORABLE_404_STARTS
+The URL where requests are redirected after login when the
+``contrib.auth.login`` view gets no ``next`` parameter.
-IGNORABLE_404_STARTS
---------------------
+This is used by the :func:`~django.contrib.auth.decorators.login_required`
+decorator, for example.
-.. deprecated:: 1.4
- This setting has been superseded by :setting:`IGNORABLE_404_URLS`.
+.. versionchanged:: 1.5
-.. setting:: URL_VALIDATOR_USER_AGENT
+This setting now also accepts view function names and
+:ref:`named URL patterns <naming-url-patterns>` which can be used to reduce
+configuration duplication since you no longer have to define the URL in two
+places (``settings`` and URLconf).
+For backward compatibility reasons the default remains unchanged.
-URL_VALIDATOR_USER_AGENT
-------------------------
+.. setting:: LOGIN_URL
-.. deprecated:: 1.5
- This value was used as the ``User-Agent`` header when checking if a URL
- exists, a feature that was removed due to security and performance issues.
+LOGIN_URL
+---------
+
+Default: ``'/accounts/login/'``
+
+The URL where requests are redirected for login, especially when using the
+:func:`~django.contrib.auth.decorators.login_required` decorator.
+
+.. versionchanged:: 1.5
+
+This setting now also accepts view function names and
+:ref:`named URL patterns <naming-url-patterns>` which can be used to reduce
+configuration duplication since you no longer have to define the URL in two
+places (``settings`` and URLconf).
+For backward compatibility reasons the default remains unchanged.
+
+.. setting:: LOGOUT_URL
+
+LOGOUT_URL
+----------
+
+Default: ``'/accounts/logout/'``
+
+LOGIN_URL counterpart.
+
+.. setting:: PASSWORD_RESET_TIMEOUT_DAYS
+
+PASSWORD_RESET_TIMEOUT_DAYS
+---------------------------
+
+Default: ``3``
+
+The number of days a password reset link is valid for. Used by the
+:mod:`django.contrib.auth` password reset mechanism.
+
+.. setting:: PASSWORD_HASHERS
+
+PASSWORD_HASHERS
+----------------
+
+See :ref:`auth_password_storage`.
+
+Default::
+
+ ('django.contrib.auth.hashers.PBKDF2PasswordHasher',
+ 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
+ 'django.contrib.auth.hashers.BCryptPasswordHasher',
+ 'django.contrib.auth.hashers.SHA1PasswordHasher',
+ 'django.contrib.auth.hashers.MD5PasswordHasher',
+ 'django.contrib.auth.hashers.UnsaltedMD5PasswordHasher',
+ 'django.contrib.auth.hashers.CryptPasswordHasher',)
+
+
+.. _settings-comments:
+
+Comments
+========
+
+Settings for :mod:`django.contrib.comments`.
+
+.. setting:: COMMENTS_HIDE_REMOVED
+
+COMMENTS_HIDE_REMOVED
+---------------------
+
+If ``True`` (default), removed comments will be excluded from comment
+lists/counts (as taken from template tags). Otherwise, the template author is
+responsible for some sort of a "this comment has been removed by the site staff"
+message.
+
+.. setting:: COMMENT_MAX_LENGTH
+
+COMMENT_MAX_LENGTH
+------------------
+
+The maximum length of the comment field, in characters. Comments longer than
+this will be rejected. Defaults to 3000.
+
+.. setting:: COMMENTS_APP
+
+COMMENTS_APP
+------------
+
+An app which provides :doc:`customization of the comments framework
+</ref/contrib/comments/custom>`. Use the same dotted-string notation
+as in :setting:`INSTALLED_APPS`. Your custom :setting:`COMMENTS_APP`
+must also be listed in :setting:`INSTALLED_APPS`.
+
+.. setting:: PROFANITIES_LIST
+
+PROFANITIES_LIST
+----------------
+
+Default: ``()`` (Empty tuple)
+
+A tuple of profanities, as strings, that will be forbidden in comments when
+``COMMENTS_ALLOW_PROFANITIES`` is ``False``.
+
+
+.. _settings-messages:
+
+Messages
+========
+
+Settings for :mod:`django.contrib.messages`.
+
+.. setting:: MESSAGE_LEVEL
+
+MESSAGE_LEVEL
+-------------
+
+Default: ``messages.INFO``
+
+Sets the minimum message level that will be recorded by the messages
+framework. See :ref:`message levels <message-level>` for more details.
+
+.. admonition:: Important
+
+ If you override ``MESSAGE_LEVEL`` in your settings file and rely on any of
+ the built-in constants, you must import the constants module directly to
+ avoid the potential for circular imports, e.g.::
+
+ from django.contrib.messages import constants as message_constants
+ MESSAGE_LEVEL = message_constants.DEBUG
+
+ If desired, you may specify the numeric values for the constants directly
+ according to the values in the above :ref:`constants table
+ <message-level-constants>`.
+
+.. setting:: MESSAGE_STORAGE
+
+MESSAGE_STORAGE
+---------------
+
+Default: ``'django.contrib.messages.storage.fallback.FallbackStorage'``
+
+Controls where Django stores message data. Valid values are:
+
+* ``'django.contrib.messages.storage.fallback.FallbackStorage'``
+* ``'django.contrib.messages.storage.session.SessionStorage'``
+* ``'django.contrib.messages.storage.cookie.CookieStorage'``
+
+See :ref:`message storage backends <message-storage-backends>` for more details.
+
+.. setting:: MESSAGE_TAGS
+
+MESSAGE_TAGS
+------------
+
+Default::
+
+ {messages.DEBUG: 'debug',
+ messages.INFO: 'info',
+ messages.SUCCESS: 'success',
+ messages.WARNING: 'warning',
+ messages.ERROR: 'error',}
+
+This sets the mapping of message level to message tag, which is typically
+rendered as a CSS class in HTML. If you specify a value, it will extend
+the default. This means you only have to specify those values which you need
+to override. See :ref:`message-displaying` above for more details.
+
+.. admonition:: Important
+
+ If you override ``MESSAGE_TAGS`` in your settings file and rely on any of
+ the built-in constants, you must import the ``constants`` module directly to
+ avoid the potential for circular imports, e.g.::
+
+ from django.contrib.messages import constants as message_constants
+ MESSAGE_TAGS = {message_constants.INFO: ''}
+
+ If desired, you may specify the numeric values for the constants directly
+ according to the values in the above :ref:`constants table
+ <message-level-constants>`.
+
+.. _messages-session_cookie_domain:
+
+SESSION_COOKIE_DOMAIN
+---------------------
+
+Default: ``None``
+
+The storage backends that use cookies -- ``CookieStorage`` and
+``FallbackStorage`` -- use the value of :setting:`SESSION_COOKIE_DOMAIN` in
+setting their cookies.
+
+
+.. _settings-sessions:
+
+Sessions
+========
+
+Settings for :mod:`django.contrib.sessions`.
+
+.. setting:: SESSION_CACHE_ALIAS
+
+SESSION_CACHE_ALIAS
+-------------------
+
+Default: ``default``
+
+If you're using :ref:`cache-based session storage <cached-sessions-backend>`,
+this selects the cache to use.
+
+.. setting:: SESSION_COOKIE_AGE
+
+SESSION_COOKIE_AGE
+------------------
+
+Default: ``1209600`` (2 weeks, in seconds)
+
+The age of session cookies, in seconds.
+
+.. setting:: SESSION_COOKIE_DOMAIN
+
+SESSION_COOKIE_DOMAIN
+---------------------
+
+Default: ``None``
+
+The domain to use for session cookies. Set this to a string such as
+``".example.com"`` (note the leading dot!) for cross-domain cookies, or use
+``None`` for a standard domain cookie.
+
+Be cautious when updating this setting on a production site. If you update
+this setting to enable cross-domain cookies on a site that previously used
+standard domain cookies, existing user cookies will be set to the old
+domain. This may result in them being unable to log in as long as these cookies
+persist.
+
+.. setting:: SESSION_COOKIE_HTTPONLY
+
+SESSION_COOKIE_HTTPONLY
+-----------------------
+
+Default: ``True``
+
+Whether to use HTTPOnly flag on the session cookie. If this is set to
+``True``, client-side JavaScript will not to be able to access the
+session cookie.
+
+HTTPOnly_ is a flag included in a Set-Cookie HTTP response header. It
+is not part of the :rfc:`2109` standard for cookies, and it isn't honored
+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: https://www.owasp.org/index.php/HTTPOnly
+
+.. setting:: SESSION_COOKIE_NAME
+
+SESSION_COOKIE_NAME
+-------------------
+
+Default: ``'sessionid'``
+
+The name of the cookie to use for sessions. This can be whatever you want (but
+should be different from :setting:`LANGUAGE_COOKIE_NAME`).
+
+.. setting:: SESSION_COOKIE_PATH
+
+SESSION_COOKIE_PATH
+-------------------
+
+Default: ``'/'``
+
+The path set on the session cookie. This should either match the URL path of your
+Django installation or be parent of that path.
+
+This is useful if you have multiple Django instances running under the same
+hostname. They can use different cookie paths, and each instance will only see
+its own session cookie.
+
+.. setting:: SESSION_COOKIE_SECURE
+
+SESSION_COOKIE_SECURE
+---------------------
+
+Default: ``False``
+
+Whether to use a secure cookie for the session cookie. If this is set to
+``True``, the cookie will be marked as "secure," which means browsers may
+ensure that the cookie is only sent under an HTTPS connection.
+
+.. setting:: SESSION_ENGINE
+
+SESSION_ENGINE
+--------------
+
+Default: ``django.contrib.sessions.backends.db``
+
+Controls where Django stores session data. Valid values are:
+
+* ``'django.contrib.sessions.backends.db'``
+* ``'django.contrib.sessions.backends.file'``
+* ``'django.contrib.sessions.backends.cache'``
+* ``'django.contrib.sessions.backends.cached_db'``
+* ``'django.contrib.sessions.backends.signed_cookies'``
+
+See :ref:`configuring-sessions` for more details.
+
+.. setting:: SESSION_EXPIRE_AT_BROWSER_CLOSE
+
+SESSION_EXPIRE_AT_BROWSER_CLOSE
+-------------------------------
+
+Default: ``False``
+
+Whether to expire the session when the user closes his or her browser. See
+:ref:`browser-length-vs-persistent-sessions`.
+
+.. setting:: SESSION_FILE_PATH
+
+SESSION_FILE_PATH
+-----------------
+
+Default: ``None``
+
+If you're using file-based session storage, this sets the directory in
+which Django will store session data. When the default value (``None``) is
+used, Django will use the standard temporary directory for the system.
+
+
+.. setting:: SESSION_SAVE_EVERY_REQUEST
+
+SESSION_SAVE_EVERY_REQUEST
+--------------------------
+
+Default: ``False``
+
+Whether to save the session data on every request. If this is ``False``
+(default), then the session data will only be saved if it has been modified --
+that is, if any of its dictionary values have been assigned or deleted.
+
+
+Sites
+=====
+
+Settings for :mod:`django.contrib.sites`.
+
+.. setting:: SITE_ID
+
+SITE_ID
+-------
+
+Default: Not defined
+
+The ID, as an integer, of the current site in the ``django_site`` database
+table. This is used so that application data can hook into specific sites
+and a single database can manage content for multiple sites.
+
+
+.. _settings-staticfiles:
+
+Static files
+============
+
+Settings for :mod:`django.contrib.staticfiles`.
+
+.. setting:: STATIC_ROOT
+
+STATIC_ROOT
+-----------
+
+Default: ``''`` (Empty string)
+
+The absolute path to the directory where :djadmin:`collectstatic` will collect
+static files for deployment.
+
+Example: ``"/var/www/example.com/static/"``
+
+If the :doc:`staticfiles</ref/contrib/staticfiles>` contrib app is enabled
+(default) the :djadmin:`collectstatic` management command will collect static
+files into this directory. See the howto on :doc:`managing static
+files</howto/static-files/index>` for more details about usage.
+
+.. warning::
+
+ This should be an (initially empty) destination directory for collecting
+ your static files from their permanent locations into one directory for
+ ease of deployment; it is **not** a place to store your static files
+ permanently. You should do that in directories that will be found by
+ :doc:`staticfiles</ref/contrib/staticfiles>`'s
+ :setting:`finders<STATICFILES_FINDERS>`, which by default, are
+ ``'static/'`` app sub-directories and any directories you include in
+ :setting:`STATICFILES_DIRS`).
+
+.. setting:: STATIC_URL
+
+STATIC_URL
+----------
+
+Default: ``None``
+
+URL to use when referring to static files located in :setting:`STATIC_ROOT`.
+
+Example: ``"/static/"`` or ``"http://static.example.com/"``
+
+If not ``None``, this will be used as the base path for
+:ref:`media definitions<form-media-paths>` and the
+:doc:`staticfiles app</ref/contrib/staticfiles>`.
+
+It must end in a slash if set to a non-empty value.
+
+.. setting:: STATICFILES_DIRS
+
+STATICFILES_DIRS
+----------------
+
+Default: ``[]``
+
+This setting defines the additional locations the staticfiles app will traverse
+if the ``FileSystemFinder`` finder is enabled, e.g. if you use the
+:djadmin:`collectstatic` or :djadmin:`findstatic` management command or use the
+static file serving view.
+
+This should be set to a list or tuple of strings that contain full paths to
+your additional files directory(ies) e.g.::
+
+ STATICFILES_DIRS = (
+ "/home/special.polls.com/polls/static",
+ "/home/polls.com/polls/static",
+ "/opt/webfiles/common",
+ )
+
+Prefixes (optional)
+~~~~~~~~~~~~~~~~~~~
+
+In case you want to refer to files in one of the locations with an additional
+namespace, you can **optionally** provide a prefix as ``(prefix, path)``
+tuples, e.g.::
+
+ STATICFILES_DIRS = (
+ # ...
+ ("downloads", "/opt/webfiles/stats"),
+ )
+
+Example:
+
+Assuming you have :setting:`STATIC_URL` set ``'/static/'``, the
+:djadmin:`collectstatic` management command would collect the "stats" files
+in a ``'downloads'`` subdirectory of :setting:`STATIC_ROOT`.
+
+This would allow you to refer to the local file
+``'/opt/webfiles/stats/polls_20101022.tar.gz'`` with
+``'/static/downloads/polls_20101022.tar.gz'`` in your templates, e.g.:
+
+.. code-block:: html+django
+
+ <a href="{{ STATIC_URL }}downloads/polls_20101022.tar.gz">
+
+.. setting:: STATICFILES_STORAGE
+
+STATICFILES_STORAGE
+-------------------
+
+Default: ``'django.contrib.staticfiles.storage.StaticFilesStorage'``
+
+The file storage engine to use when collecting static files with the
+:djadmin:`collectstatic` management command.
+
+A ready-to-use instance of the storage backend defined in this setting
+can be found at ``django.contrib.staticfiles.storage.staticfiles_storage``.
+
+For an example, see :ref:`staticfiles-from-cdn`.
+
+.. setting:: STATICFILES_FINDERS
+
+STATICFILES_FINDERS
+-------------------
+
+Default::
+
+ ("django.contrib.staticfiles.finders.FileSystemFinder",
+ "django.contrib.staticfiles.finders.AppDirectoriesFinder")
+
+The list of finder backends that know how to find static files in
+various locations.
+
+The default will find files stored in the :setting:`STATICFILES_DIRS` setting
+(using ``django.contrib.staticfiles.finders.FileSystemFinder``) and in a
+``static`` subdirectory of each app (using
+``django.contrib.staticfiles.finders.AppDirectoriesFinder``)
+
+One finder is disabled by default:
+``django.contrib.staticfiles.finders.DefaultStorageFinder``. If added to
+your :setting:`STATICFILES_FINDERS` setting, it will look for static files in
+the default file storage as defined by the :setting:`DEFAULT_FILE_STORAGE`
+setting.
+
+.. note::
+
+ When using the ``AppDirectoriesFinder`` finder, make sure your apps
+ can be found by staticfiles. Simply add the app to the
+ :setting:`INSTALLED_APPS` setting of your site.
+
+Static file finders are currently considered a private interface, and this
+interface is thus undocumented.
+
+Core Settings Topical Index
+===========================
+
+Cache
+-----
+* :setting:`CACHES`
+* :setting:`CACHE_MIDDLEWARE_ALIAS`
+* :setting:`CACHE_MIDDLEWARE_ANONYMOUS_ONLY`
+* :setting:`CACHE_MIDDLEWARE_KEY_PREFIX`
+* :setting:`CACHE_MIDDLEWARE_SECONDS`
+
+Database
+--------
+* :setting:`DATABASES`
+* :setting:`DATABASE_ROUTERS`
+* :setting:`DEFAULT_INDEX_TABLESPACE`
+* :setting:`DEFAULT_TABLESPACE`
+* :setting:`TRANSACTIONS_MANAGED`
+
+Debugging
+---------
+* :setting:`DEBUG`
+* :setting:`DEBUG_PROPAGATE_EXCEPTIONS`
+
+Email
+-----
+* :setting:`ADMINS`
+* :setting:`DEFAULT_CHARSET`
+* :setting:`DEFAULT_FROM_EMAIL`
+* :setting:`EMAIL_BACKEND`
+* :setting:`EMAIL_FILE_PATH`
+* :setting:`EMAIL_HOST`
+* :setting:`EMAIL_HOST_PASSWORD`
+* :setting:`EMAIL_HOST_USER`
+* :setting:`EMAIL_PORT`
+* :setting:`EMAIL_SUBJECT_PREFIX`
+* :setting:`EMAIL_USE_TLS`
+* :setting:`MANAGERS`
+* :setting:`SEND_BROKEN_LINK_EMAILS`
+* :setting:`SERVER_EMAIL`
+
+Error reporting
+---------------
+* :setting:`DEFAULT_EXCEPTION_REPORTER_FILTER`
+* :setting:`IGNORABLE_404_URLS`
+* :setting:`MANAGERS`
+* :setting:`SEND_BROKEN_LINK_EMAILS`
+
+File uploads
+------------
+* :setting:`DEFAULT_FILE_STORAGE`
+* :setting:`FILE_CHARSET`
+* :setting:`FILE_UPLOAD_HANDLERS`
+* :setting:`FILE_UPLOAD_MAX_MEMORY_SIZE`
+* :setting:`FILE_UPLOAD_PERMISSIONS`
+* :setting:`FILE_UPLOAD_TEMP_DIR`
+* :setting:`MEDIA_ROOT`
+* :setting:`MEDIA_URL`
+
+Globalization (i18n/l10n)
+-------------------------
+* :setting:`DATE_FORMAT`
+* :setting:`DATE_INPUT_FORMATS`
+* :setting:`DATETIME_FORMAT`
+* :setting:`DATETIME_INPUT_FORMATS`
+* :setting:`DECIMAL_SEPARATOR`
+* :setting:`FIRST_DAY_OF_WEEK`
+* :setting:`FORMAT_MODULE_PATH`
+* :setting:`LANGUAGE_CODE`
+* :setting:`LANGUAGE_COOKIE_NAME`
+* :setting:`LANGUAGES`
+* :setting:`LOCALE_PATHS`
+* :setting:`MONTH_DAY_FORMAT`
+* :setting:`NUMBER_GROUPING`
+* :setting:`SHORT_DATE_FORMAT`
+* :setting:`SHORT_DATETIME_FORMAT`
+* :setting:`THOUSAND_SEPARATOR`
+* :setting:`TIME_FORMAT`
+* :setting:`TIME_INPUT_FORMATS`
+* :setting:`TIME_ZONE`
+* :setting:`USE_I18N`
+* :setting:`USE_L10N`
+* :setting:`USE_THOUSAND_SEPARATOR`
+* :setting:`USE_TZ`
+* :setting:`YEAR_MONTH_FORMAT`
+
+HTTP
+----
+* :setting:`DEFAULT_CHARSET`
+* :setting:`DEFAULT_CONTENT_TYPE`
+* :setting:`DISALLOWED_USER_AGENTS`
+* :setting:`FORCE_SCRIPT_NAME`
+* :setting:`INTERNAL_IPS`
+* :setting:`MIDDLEWARE_CLASSES`
+* :setting:`SECURE_PROXY_SSL_HEADER`
+* :setting:`SIGNING_BACKEND`
+* :setting:`USE_ETAGS`
+* :setting:`USE_X_FORWARDED_HOST`
+* :setting:`WSGI_APPLICATION`
+
+Logging
+-------
+* :setting:`LOGGING`
+* :setting:`LOGGING_CONFIG`
+
+Models
+------
+* :setting:`ABSOLUTE_URL_OVERRIDES`
+* :setting:`FIXTURE_DIRS`
+* :setting:`INSTALLED_APPS`
+
+Security
+--------
+* Cross Site Request Forgery protection
+
+ * :setting:`CSRF_COOKIE_DOMAIN`
+ * :setting:`CSRF_COOKIE_NAME`
+ * :setting:`CSRF_COOKIE_PATH`
+ * :setting:`CSRF_COOKIE_SECURE`
+ * :setting:`CSRF_FAILURE_VIEW`
+
+* :setting:`SECRET_KEY`
+* :setting:`X_FRAME_OPTIONS`
+
+Serialization
+-------------
+* :setting:`DEFAULT_CHARSET`
+* :setting:`SERIALIZATION_MODULES`
+
+Templates
+---------
+* :setting:`ALLOWED_INCLUDE_ROOTS`
+* :setting:`TEMPLATE_CONTEXT_PROCESSORS`
+* :setting:`TEMPLATE_DEBUG`
+* :setting:`TEMPLATE_DIRS`
+* :setting:`TEMPLATE_LOADERS`
+* :setting:`TEMPLATE_STRING_IF_INVALID`
+
+Testing
+-------
+* Database
+
+ * :setting:`TEST_CHARSET`
+ * :setting:`TEST_COLLATION`
+ * :setting:`TEST_DEPENDENCIES`
+ * :setting:`TEST_MIRROR`
+ * :setting:`TEST_NAME`
+ * :setting:`TEST_CREATE`
+ * :setting:`TEST_USER`
+ * :setting:`TEST_USER_CREATE`
+ * :setting:`TEST_PASSWD`
+ * :setting:`TEST_TBLSPACE`
+ * :setting:`TEST_TBLSPACE_TMP`
+
+* :setting:`TEST_RUNNER`
+
+URLs
+----
+* :setting:`APPEND_SLASH`
+* :setting:`PREPEND_WWW`
+* :setting:`ROOT_URLCONF`
diff --git a/docs/ref/signals.txt b/docs/ref/signals.txt
index 0db540370d..ca472bd60e 100644
--- a/docs/ref/signals.txt
+++ b/docs/ref/signals.txt
@@ -12,7 +12,7 @@ A list of all the signals that Django sends.
The :doc:`comment framework </ref/contrib/comments/index>` sends a :doc:`set
of comment-related signals </ref/contrib/comments/signals>`.
- The :doc:`authentication framework </topics/auth>` sends :ref:`signals when
+ The :doc:`authentication framework </topics/auth/index>` sends :ref:`signals when
a user is logged in / out <topics-auth-signals>`.
Model signals
@@ -27,9 +27,8 @@ module system.
.. warning::
Many of these signals are sent by various model methods like
- :meth:`~django.db.models.Model.__init__` or
- :meth:`~django.db.models.Model.save` that you can overwrite in your own
- code.
+ ``__init__()`` or :meth:`~django.db.models.Model.save` that you can
+ override in your own code.
If you override these methods on your model, you must call the parent class'
methods for this signals to be sent.
@@ -47,7 +46,7 @@ pre_init
.. ^^^^^^^ this :module: hack keeps Sphinx from prepending the module.
Whenever you instantiate a Django model, this signal is sent at the beginning
-of the model's :meth:`~django.db.models.Model.__init__` method.
+of the model's ``__init__()`` method.
Arguments sent with this signal:
@@ -55,12 +54,10 @@ Arguments sent with this signal:
The model class that just had an instance created.
``args``
- A list of positional arguments passed to
- :meth:`~django.db.models.Model.__init__`:
+ A list of positional arguments passed to ``__init__()``:
``kwargs``
- A dictionary of keyword arguments passed to
- :meth:`~django.db.models.Model.__init__`:.
+ A dictionary of keyword arguments passed to ``__init__()``:
For example, the :doc:`tutorial </intro/tutorial01>` has this line::
@@ -74,7 +71,7 @@ Argument Value
``sender`` ``Poll`` (the class itself)
``args`` ``[]`` (an empty list because there were no positional
- arguments passed to ``__init__``.)
+ arguments passed to ``__init__()``.)
``kwargs`` ``{'question': "What's up?", 'pub_date': datetime.now()}``
========== ===============================================================
@@ -85,7 +82,7 @@ post_init
.. data:: django.db.models.signals.post_init
:module:
-Like pre_init, but this one is sent when the :meth:`~django.db.models.Model.__init__`: method finishes.
+Like pre_init, but this one is sent when the ``__init__()`` method finishes.
Arguments sent with this signal:
@@ -212,24 +209,24 @@ m2m_changed
.. data:: django.db.models.signals.m2m_changed
:module:
-Sent when a :class:`ManyToManyField` is changed on a model instance.
-Strictly speaking, this is not a model signal since it is sent by the
-:class:`ManyToManyField`, but since it complements the
+Sent when a :class:`~django.db.models.ManyToManyField` is changed on a model
+instance. Strictly speaking, this is not a model signal since it is sent by the
+:class:`~django.db.models.ManyToManyField`, but since it complements the
:data:`pre_save`/:data:`post_save` and :data:`pre_delete`/:data:`post_delete`
when it comes to tracking changes to models, it is included here.
Arguments sent with this signal:
``sender``
- The intermediate model class describing the :class:`ManyToManyField`.
- This class is automatically created when a many-to-many field is
- defined; you can access it using the ``through`` attribute on the
- many-to-many field.
+ The intermediate model class describing the
+ :class:`~django.db.models.ManyToManyField`. This class is automatically
+ created when a many-to-many field is defined; you can access it using the
+ ``through`` attribute on the many-to-many field.
``instance``
The instance whose many-to-many relation is updated. This can be an
- instance of the ``sender``, or of the class the :class:`ManyToManyField`
- is related to.
+ instance of the ``sender``, or of the class the
+ :class:`~django.db.models.ManyToManyField` is related to.
``action``
A string indicating the type of update that is done on the relation.
@@ -303,8 +300,9 @@ Argument Value
``action`` ``"pre_add"`` (followed by a separate signal with ``"post_add"``)
-``reverse`` ``False`` (``Pizza`` contains the :class:`ManyToManyField`,
- so this call modifies the forward relation)
+``reverse`` ``False`` (``Pizza`` contains the
+ :class:`~django.db.models.ManyToManyField`, so this call
+ modifies the forward relation)
``model`` ``Topping`` (the class of the objects added to the
``Pizza``)
@@ -329,8 +327,9 @@ Argument Value
``action`` ``"pre_remove"`` (followed by a separate signal with ``"post_remove"``)
-``reverse`` ``True`` (``Pizza`` contains the :class:`ManyToManyField`,
- so this call modifies the reverse relation)
+``reverse`` ``True`` (``Pizza`` contains the
+ :class:`~django.db.models.ManyToManyField`, so this call
+ modifies the reverse relation)
``model`` ``Pizza`` (the class of the objects removed from the
``Topping``)
@@ -407,6 +406,10 @@ Arguments sent with this signal:
For example, the :mod:`django.contrib.auth` app only prompts to create a
superuser when ``interactive`` is ``True``.
+``db``
+ The database alias used for synchronization. Defaults to the ``default``
+ database.
+
For example, ``yourapp/management/__init__.py`` could be written like::
from django.db.models.signals import post_syncdb
@@ -437,9 +440,8 @@ Sent when Django begins processing an HTTP request.
Arguments sent with this signal:
``sender``
- The handler class -- e.g.
- :class:`django.core.handlers.wsgi.WsgiHandler` -- that handled
- the request.
+ The handler class -- e.g. ``django.core.handlers.wsgi.WsgiHandler`` -- that
+ handled the request.
request_finished
----------------
@@ -449,6 +451,18 @@ request_finished
Sent when Django finishes processing an HTTP request.
+.. note::
+
+ When a view returns a :ref:`streaming response <httpresponse-streaming>`,
+ this signal is sent only after the entire response is consumed by the
+ client (strictly speaking, by the WSGI gateway).
+
+.. versionchanged:: 1.5
+
+ Before Django 1.5, this signal was fired before sending the content to the
+ client. In order to accomodate streaming responses, it is now fired after
+ sending the content.
+
Arguments sent with this signal:
``sender``
@@ -476,18 +490,16 @@ Test signals
.. module:: django.test.signals
:synopsis: Signals sent during testing.
-Signals only sent when :doc:`running tests </topics/testing>`.
+Signals only sent when :ref:`running tests <running-tests>`.
setting_changed
---------------
-.. versionadded:: 1.4
-
.. data:: django.test.signals.setting_changed
:module:
This signal is sent when the value of a setting is changed through the
-:meth:`django.test.TestCase.setting` context manager or the
+``django.test.TestCase.settings()`` context manager or the
:func:`django.test.utils.override_settings` decorator/context manager.
It's actually sent twice: when the new value is applied ("setup") and when the
@@ -549,8 +561,8 @@ Arguments sent with this signal:
``sender``
The database wrapper class -- i.e.
- :class:`django.db.backends.postgresql_psycopg2.DatabaseWrapper` or
- :class:`django.db.backends.mysql.DatabaseWrapper`, etc.
+ ``django.db.backends.postgresql_psycopg2.DatabaseWrapper`` or
+ ``django.db.backends.mysql.DatabaseWrapper``, etc.
``connection``
The database connection that was opened. This can be used in a
diff --git a/docs/ref/template-response.txt b/docs/ref/template-response.txt
index d9b7130362..5c13ec7d96 100644
--- a/docs/ref/template-response.txt
+++ b/docs/ref/template-response.txt
@@ -56,11 +56,11 @@ Attributes
Methods
-------
-.. method:: SimpleTemplateResponse.__init__(template, context=None, mimetype=None, status=None, content_type=None)
+.. method:: SimpleTemplateResponse.__init__(template, context=None, content_type=None, status=None)
Instantiates a
:class:`~django.template.response.SimpleTemplateResponse` object
- with the given template, context, MIME type and HTTP status.
+ with the given template, context, content type, and HTTP status.
``template``
The full name of a template, or a sequence of template names.
@@ -75,12 +75,15 @@ Methods
The HTTP Status code for the response.
``content_type``
- An alias for ``mimetype``. Historically, this parameter was only called
- ``mimetype``, but since this is actually the value included in the HTTP
- ``Content-Type`` header, it can also include the character set encoding,
- which makes it more than just a MIME type specification. If ``mimetype``
- is specified (not ``None``), that value is used. Otherwise,
- ``content_type`` is used. If neither is given,
+
+ .. versionchanged:: 1.5
+
+ Historically, this parameter was only called ``mimetype`` (now
+ deprecated), but since this is actually the value included in the HTTP
+ ``Content-Type`` header, it can also include the character set
+ encoding, which makes it more than just a MIME type specification. If
+ ``mimetype`` is specified (not ``None``), that value is used.
+ Otherwise, ``content_type`` is used. If neither is given,
:setting:`DEFAULT_CONTENT_TYPE` is used.
@@ -117,19 +120,18 @@ Methods
rendered :class:`~django.template.response.SimpleTemplateResponse`
instance.
- If the callback returns a value that is not `None`, this will be
+ If the callback returns a value that is not ``None``, this will be
used as the response instead of the original response object (and
will be passed to the next post rendering callback etc.)
-.. method:: SimpleTemplateResponse.render():
+.. method:: SimpleTemplateResponse.render()
- Sets :attr:`response.content` to the result obtained by
+ Sets ``response.content`` to the result obtained by
:attr:`SimpleTemplateResponse.rendered_content`, runs all post-rendering
callbacks, and returns the resulting response object.
- :meth:`~SimpleTemplateResponse.render()` will only have an effect
- the first time it is called. On subsequent calls, it will return
- the result obtained from the first call.
+ ``render()`` will only have an effect the first time it is called. On
+ subsequent calls, it will return the result obtained from the first call.
TemplateResponse objects
@@ -145,7 +147,7 @@ TemplateResponse objects
Methods
-------
-.. method:: TemplateResponse.__init__(request, template, context=None, mimetype=None, status=None, content_type=None, current_app=None)
+.. method:: TemplateResponse.__init__(request, template, context=None, content_type=None, status=None, current_app=None)
Instantiates an ``TemplateResponse`` object with the given
template, context, MIME type and HTTP status.
@@ -166,12 +168,15 @@ Methods
The HTTP Status code for the response.
``content_type``
- An alias for ``mimetype``. Historically, this parameter was only called
- ``mimetype``, but since this is actually the value included in the HTTP
- ``Content-Type`` header, it can also include the character set encoding,
- which makes it more than just a MIME type specification. If ``mimetype``
- is specified (not ``None``), that value is used. Otherwise,
- ``content_type`` is used. If neither is given,
+
+ .. versionchanged:: 1.5
+
+ Historically, this parameter was only called ``mimetype`` (now
+ deprecated), but since this is actually the value included in the HTTP
+ ``Content-Type`` header, it can also include the character set
+ encoding, which makes it more than just a MIME type specification. If
+ ``mimetype`` is specified (not ``None``), that value is used.
+ Otherwise, ``content_type`` is used. If neither is given,
:setting:`DEFAULT_CONTENT_TYPE` is used.
``current_app``
@@ -188,24 +193,23 @@ returned to the client, it must be rendered. The rendering process takes the
intermediate representation of template and context, and turns it into the
final byte stream that can be served to the client.
-There are three circumstances under which a TemplateResponse will be
+There are three circumstances under which a ``TemplateResponse`` will be
rendered:
-* When the TemplateResponse instance is explicitly rendered, using
+* When the ``TemplateResponse`` instance is explicitly rendered, using
the :meth:`SimpleTemplateResponse.render()` method.
* When the content of the response is explicitly set by assigning
- :attr:`response.content`.
+ ``response.content``.
* After passing through template response middleware, but before
passing through response middleware.
-A TemplateResponse can only be rendered once. The first call to
-:meth:`SimpleTemplateResponse.render` sets the content of the
-response; subsequent rendering calls do not change the response
-content.
+A ``TemplateResponse`` can only be rendered once. The first call to
+:meth:`SimpleTemplateResponse.render` sets the content of the response;
+subsequent rendering calls do not change the response content.
-However, when :attr:`response.content` is explicitly assigned, the
+However, when ``response.content`` is explicitly assigned, the
change is always applied. If you want to force the content to be
re-rendered, you can re-evaluate the rendered content, and assign
the content of the response manually::
diff --git a/docs/ref/templates/api.txt b/docs/ref/templates/api.txt
index db57d2de96..0162f78eed 100644
--- a/docs/ref/templates/api.txt
+++ b/docs/ref/templates/api.txt
@@ -221,13 +221,12 @@ straight lookups. Here are some things to keep in mind:
self.database_record.delete()
sensitive_function.alters_data = True
-* .. versionadded:: 1.4
- Occasionally you may want to turn off this feature for other reasons,
- and tell the template system to leave a variable un-called no matter
- what. To do so, set a ``do_not_call_in_templates`` attribute on the
- callable with the value ``True``. The template system then will act as
- if your variable is not callable (allowing you to access attributes of
- the callable, for example).
+* Occasionally you may want to turn off this feature for other reasons,
+ and tell the template system to leave a variable un-called no matter
+ what. To do so, set a ``do_not_call_in_templates`` attribute on the
+ callable with the value ``True``. The template system then will act as
+ if your variable is not callable (allowing you to access attributes of
+ the callable, for example).
.. _invalid-template-variables:
@@ -558,15 +557,17 @@ Note that these paths should use Unix-style forward slashes, even on Windows.
The Python API
~~~~~~~~~~~~~~
-Django has two ways to load templates from files:
+.. module:: django.template.loader
-.. function:: django.template.loader.get_template(template_name)
+``django.template.loader`` has two functions to load templates from files:
+
+.. function:: get_template(template_name)
``get_template`` returns the compiled template (a ``Template`` object) for
the template with the given name. If the template doesn't exist, it raises
``django.template.TemplateDoesNotExist``.
-.. function:: django.template.loader.select_template(template_name_list)
+.. function:: select_template(template_name_list)
``select_template`` is just like ``get_template``, except it takes a list
of template names. Of the list, it returns the first template that exists.
@@ -631,11 +632,19 @@ by editing your :setting:`TEMPLATE_LOADERS` setting. :setting:`TEMPLATE_LOADERS`
should be a tuple of strings, where each string represents a template loader
class. Here are the template loaders that come with Django:
+.. currentmodule:: django.template.loaders
+
``django.template.loaders.filesystem.Loader``
+
+.. class:: filesystem.Loader
+
Loads templates from the filesystem, according to :setting:`TEMPLATE_DIRS`.
This loader is enabled by default.
``django.template.loaders.app_directories.Loader``
+
+.. class:: app_directories.Loader
+
Loads templates from Django apps on the filesystem. For each app in
:setting:`INSTALLED_APPS`, the loader looks for a ``templates``
subdirectory. If the directory exists, Django looks for templates in there.
@@ -670,12 +679,18 @@ class. Here are the template loaders that come with Django:
This loader is enabled by default.
``django.template.loaders.eggs.Loader``
+
+.. class:: eggs.Loader
+
Just like ``app_directories`` above, but it loads templates from Python
eggs rather than from the filesystem.
This loader is disabled by default.
``django.template.loaders.cached.Loader``
+
+.. class:: cached.Loader
+
By default, the templating system will read and compile your templates every
time they need to be rendered. While the Django templating system is quite
fast, the overhead from reading and compiling templates can add up.
diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt
index 57ef0cfb27..123e114c4a 100644
--- a/docs/ref/templates/builtins.txt
+++ b/docs/ref/templates/builtins.txt
@@ -147,9 +147,8 @@ You can use any number of values in a ``{% cycle %}`` tag, separated by spaces.
Values enclosed in single (``'``) or double quotes (``"``) are treated as
string literals, while values without quotes are treated as template variables.
-Note that the variables included in the cycle will not be escaped.
-This is because template tags do not escape their content. Any HTML or
-Javascript code contained in the printed variable will be rendered
+Note that currently the variables included in the cycle will not be escaped.
+Any HTML or Javascript code contained in the printed variable will be rendered
as-is, which could potentially lead to security issues.
For backwards compatibility, the ``{% cycle %}`` tag supports the much inferior
@@ -172,7 +171,7 @@ just declare the cycle, but not output the first value, you can add a
{% for obj in some_list %}
{% cycle 'row1' 'row2' as rowcolors silent %}
- <tr class="{{ rowcolors }}">{% include "subtemplate.html " %}</tr>
+ <tr class="{{ rowcolors }}">{% include "subtemplate.html" %}</tr>
{% endfor %}
This will output a list of ``<tr>`` elements with ``class``
@@ -190,6 +189,22 @@ call to ``{% cycle %}`` doesn't specify silent::
{% cycle 'row1' 'row2' as rowcolors silent %}
{% cycle rowcolors %}
+.. versionchanged:: 1.6
+
+To improve safety, future versions of ``cycle`` will automatically escape
+their output. You're encouraged to activate this behavior by loading
+``cycle`` from the ``future`` template library::
+
+ {% load cycle from future %}
+
+When using the ``future`` version, you can disable auto-escaping with::
+
+ {% for o in some_list %}
+ <tr class="{% autoescape off %}{% cycle rowvalue1 rowvalue2 %}{% endautoescape %}">
+ ...
+ </tr>
+ {% endfor %}
+
.. templatetag:: debug
debug
@@ -257,28 +272,44 @@ This is equivalent to::
{% if var1 %}
{{ var1|safe }}
- {% else %}{% if var2 %}
+ {% elif var2 %}
{{ var2|safe }}
- {% else %}{% if var3 %}
+ {% elif var3 %}
{{ var3|safe }}
- {% endif %}{% endif %}{% endif %}
+ {% endif %}
You can also use a literal string as a fallback value in case all
passed variables are False::
{% firstof var1 var2 var3 "fallback value" %}
-Note that the variables included in the firstof tag will not be
-escaped. This is because template tags do not escape their content.
-Any HTML or Javascript code contained in the printed variable will be
-rendered as-is, which could potentially lead to security issues. If you
-need to escape the variables in the firstof tag, you must do so
-explicitly::
+Note that currently the variables included in the firstof tag will not be
+escaped. Any HTML or Javascript code contained in the printed variable will be
+rendered as-is, which could potentially lead to security issues. If you need
+to escape the variables in the firstof tag, you must do so explicitly::
{% filter force_escape %}
{% firstof var1 var2 var3 "fallback value" %}
{% endfilter %}
+.. versionchanged:: 1.6
+
+To improve safety, future versions of ``firstof`` will automatically escape
+their output. You're encouraged to activate this behavior by loading
+``firstof`` from the ``future`` template library::
+
+ {% load firstof from future %}
+
+When using the ``future`` version, you can disable auto-escaping with::
+
+ {% autoescape off %}
+ {% firstof var1 var2 var3 "<strong>fallback value</strong>" %}
+ {% endautoescape %}
+
+Or if only some variables should be escaped, you can use::
+
+ {% firstof var1 var2|safe var3 "<strong>fallback value</strong>"|safe %}
+
.. templatetag:: for
for
@@ -377,14 +408,10 @@ block are output::
In the above, if ``athlete_list`` is not empty, the number of athletes will be
displayed by the ``{{ athlete_list|length }}`` variable.
-As you can see, the ``if`` tag may take one or several `` {% elif %}``
+As you can see, the ``if`` tag may take one or several ``{% elif %}``
clauses, as well as an ``{% else %}`` clause that will be displayed if all
previous conditions fail. These clauses are optional.
-.. versionadded:: 1.4
-
-The ``if`` tag now supports ``{% elif %}`` clauses.
-
Boolean operators
^^^^^^^^^^^^^^^^^
@@ -743,8 +770,6 @@ escaped, because it's not a format character::
This would display as "It is the 4th of September".
-.. versionchanged:: 1.4
-
.. note::
The format passed can also be one of the predefined ones
@@ -864,7 +889,7 @@ an attribute "description," you could use::
{% regroup cities by country.description as country_list %}
Or, if ``country`` is a field with ``choices``, it will have a
-:meth:`^django.db.models.Model.get_FOO_display` method available as an
+:meth:`~django.db.models.Model.get_FOO_display` method available as an
attribute, allowing you to group on the display string rather than the
``choices`` key::
@@ -1079,11 +1104,11 @@ value to a maximum value, and then applies that ratio to a constant.
For example::
<img src="bar.png" alt="Bar"
- height="10" width="{% widthratio this_value max_value 100 %}" />
+ height="10" width="{% widthratio this_value max_value max_width %}" />
-Above, if ``this_value`` is 175 and ``max_value`` is 200, the image in the
-above example will be 88 pixels wide (because 175/200 = .875; .875 * 100 = 87.5
-which is rounded up to 88).
+If ``this_value`` is 175, ``max_value`` is 200, and ``max_width`` is 100, the
+image in the above example will be 88 pixels wide
+(because 175/200 = .875; .875 * 100 = 87.5 which is rounded up to 88).
.. templatetag:: with
@@ -1289,10 +1314,6 @@ Z Time zone offset in seconds. The ``-43200`` to ``4320
UTC is always positive.
================ ======================================== =====================
-.. versionadded:: 1.4
-
-The ``e`` and ``o`` format specification characters were added in Django 1.4.
-
For example::
{{ value|date:"D d M Y" }}
@@ -1915,7 +1936,7 @@ slice
Returns a slice of the list.
Uses the same syntax as Python's list slicing. See
-http://diveintopython.net/native_data_types/lists.html#odbchelper.list.slice
+http://www.diveintopython3.net/native-datatypes.html#slicinglists
for an introduction.
Example::
@@ -2069,8 +2090,6 @@ If ``value`` is ``"my first post"``, the output will be ``"My First Post"``.
truncatechars
^^^^^^^^^^^^^
-.. versionadded:: 1.4
-
Truncates a string if it is longer than the specified number of characters.
Truncated strings will end with a translatable ellipsis sequence ("...").
@@ -2200,11 +2219,6 @@ It also supports domain-only links ending in one of the original top level
domains (``.com``, ``.edu``, ``.gov``, ``.int``, ``.mil``, ``.net``, and
``.org``). For example, ``djangoproject.com`` gets converted.
-.. versionchanged:: 1.4
-
-Until Django 1.4, only the ``.com``, ``.net`` and ``.org`` suffixes were
-supported for domain-only links.
-
Links can have trailing punctuation (periods, commas, close-parens) and leading
punctuation (opening parens), and ``urlize`` will still do the right thing.
@@ -2334,8 +2348,6 @@ See :ref:`topic-l10n-templates`.
tz
^^
-.. versionadded:: 1.4
-
This library provides control over time zone conversions in templates.
Like ``l10n``, you only need to load the library using ``{% load tz %}``,
but you'll usually also set :setting:`USE_TZ` to ``True`` so that conversion
@@ -2356,16 +2368,6 @@ django.contrib.humanize
A set of Django template filters useful for adding a "human touch" to data. See
:doc:`/ref/contrib/humanize`.
-django.contrib.markup
-^^^^^^^^^^^^^^^^^^^^^
-
-A collection of template filters that implement these common markup languages:
-
-* Textile
-* Markdown
-* reST (reStructuredText)
-
-See the :doc:`markup documentation </ref/contrib/markup>`.
django.contrib.webdesign
^^^^^^^^^^^^^^^^^^^^^^^^
@@ -2416,8 +2418,10 @@ slightly different call::
The :mod:`staticfiles<django.contrib.staticfiles>` contrib app also ships
with a :ttag:`static template tag<staticfiles-static>` which uses
``staticfiles'`` :setting:`STATICFILES_STORAGE` to build the URL of the
- given path. Use that instead if you have an advanced use case such as
- :ref:`using a cloud service to serve static files<staticfiles-from-cdn>`::
+ given path (rather than simply using :func:`urlparse.urljoin` with the
+ :setting:`STATIC_URL` setting and the given path). Use that instead if you
+ have an advanced use case such as :ref:`using a cloud service to serve
+ static files<staticfiles-from-cdn>`::
{% load static from staticfiles %}
<img src="{% static "images/hi.jpg" %}" alt="Hi!" />
diff --git a/docs/ref/unicode.txt b/docs/ref/unicode.txt
index 784ff33398..bd5bdc96a9 100644
--- a/docs/ref/unicode.txt
+++ b/docs/ref/unicode.txt
@@ -20,14 +20,14 @@ able to store certain characters in the database, and information will be lost.
* MySQL users, refer to the `MySQL manual`_ (section 9.1.3.2 for MySQL 5.1)
for details on how to set or alter the database character set encoding.
-* PostgreSQL users, refer to the `PostgreSQL manual`_ (section 21.2.2 in
- PostgreSQL 8) for details on creating databases with the correct encoding.
+* PostgreSQL users, refer to the `PostgreSQL manual`_ (section 22.3.2 in
+ PostgreSQL 9) for details on creating databases with the correct encoding.
* SQLite users, there is nothing you need to do. SQLite always uses UTF-8
for internal encoding.
.. _MySQL manual: http://dev.mysql.com/doc/refman/5.1/en/charset-database.html
-.. _PostgreSQL manual: http://www.postgresql.org/docs/8.2/static/multibyte.html#AEN24104
+.. _PostgreSQL manual: http://www.postgresql.org/docs/current/static/multibyte.html
All of Django's database backends automatically convert Unicode strings into
the appropriate encoding for talking to the database. They also automatically
@@ -68,7 +68,7 @@ Python 2 with unicode literals or Python 3::
See also :doc:`Python 3 compatibility </topics/python3>`.
-.. admonition:: Warning
+.. warning::
A bytestring does not carry any information with it about its encoding.
For that reason, we have to make an assumption, and Django assumes that all
diff --git a/docs/ref/urlresolvers.txt b/docs/ref/urlresolvers.txt
index 528f172061..87c0605a11 100644
--- a/docs/ref/urlresolvers.txt
+++ b/docs/ref/urlresolvers.txt
@@ -71,8 +71,6 @@ You can use ``kwargs`` instead of ``args``. For example::
reverse_lazy()
--------------
-.. versionadded:: 1.4
-
A lazily evaluated version of `reverse()`_.
.. function:: reverse_lazy(viewname, [urlconf=None, args=None, kwargs=None, current_app=None])
diff --git a/docs/ref/urls.txt b/docs/ref/urls.txt
index b9a0199984..e68edc8254 100644
--- a/docs/ref/urls.txt
+++ b/docs/ref/urls.txt
@@ -4,14 +4,6 @@
.. module:: django.conf.urls
-.. versionchanged:: 1.4
- Starting with Django 1.4 functions ``patterns``, ``url``, ``include`` plus
- the ``handler*`` symbols described below live in the ``django.conf.urls``
- module.
-
- Until Django 1.3 they were located in ``django.conf.urls.defaults``. You
- still can import them from there but it will be removed in Django 1.6.
-
patterns()
----------
@@ -31,12 +23,12 @@ The ``optional_dictionary`` and ``optional_name`` parameters are described in
:ref:`Passing extra options to view functions <views-extra-options>`.
.. note::
- Because `patterns()` is a function call, it accepts a maximum of 255
+ Because ``patterns()`` is a function call, it accepts a maximum of 255
arguments (URL patterns, in this case). This is a limit for all Python
function calls. This is rarely a problem in practice, because you'll
- typically structure your URL patterns modularly by using `include()`
+ typically structure your URL patterns modularly by using ``include()``
sections. However, on the off-chance you do hit the 255-argument limit,
- realize that `patterns()` returns a Python list, so you can split up the
+ realize that ``patterns()`` returns a Python list, so you can split up the
construction of the list.
::
@@ -52,6 +44,20 @@ The ``optional_dictionary`` and ``optional_name`` parameters are described in
patterns you can construct. The only limit is that you can only create 254
at a time (the 255th argument is the initial prefix argument).
+static()
+--------
+
+.. function:: static.static(prefix, view='django.views.static.serve', **kwargs)
+
+Helper function to return a URL pattern for serving files in debug mode::
+
+ from django.conf import settings
+ from django.conf.urls.static import static
+
+ urlpatterns = patterns('',
+ # ... the rest of your URLconf goes here ...
+ ) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
+
url()
-----
@@ -94,7 +100,6 @@ include()
application and instance namespaces.
:arg module: URLconf module (or module name)
- :type module: Module or string
:arg namespace: Instance namespace for the URL entries being included
:type namespace: string
:arg app_name: Application namespace for the URL entries being included
@@ -122,9 +127,6 @@ value should suffice.
See the documentation about :ref:`the 403 (HTTP Forbidden) view
<http_forbidden_view>` for more information.
-.. versionadded:: 1.4
- ``handler403`` is new in Django 1.4.
-
handler404
----------
@@ -153,4 +155,3 @@ value should suffice.
See the documentation about :ref:`the 500 (HTTP Internal Server Error) view
<http_internal_server_error_view>` for more information.
-
diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt
index 2f12c3a96c..a7d5b6690e 100644
--- a/docs/ref/utils.txt
+++ b/docs/ref/utils.txt
@@ -138,15 +138,13 @@ results. Instead do::
``django.utils.dateparse``
==========================
-.. versionadded:: 1.4
-
.. module:: django.utils.dateparse
:synopsis: Functions to parse datetime objects.
The functions defined in this module share the following properties:
-- They raise :exc:`ValueError` if their input is well formatted but isn't a
- valid date or time.
+- They raise :exc:`~exceptions.ValueError` if their input is well formatted but
+ isn't a valid date or time.
- They return ``None`` if it isn't well formatted at all.
- They accept up to picosecond resolution in input, but they truncate it to
microseconds, since that's what Python supports.
@@ -192,8 +190,7 @@ The functions defined in this module share the following properties:
Like ``decorator_from_middleware``, but returns a function
that accepts the arguments to be passed to the middleware_class.
For example, the :func:`~django.views.decorators.cache.cache_page`
- decorator is created from the
- :class:`~django.middleware.cache.CacheMiddleware` like this::
+ decorator is created from the ``CacheMiddleware`` like this::
cache_page = decorator_from_middleware_with_args(CacheMiddleware)
@@ -284,15 +281,15 @@ The functions defined in this module share the following properties:
.. function:: smart_str(s, encoding='utf-8', strings_only=False, errors='strict')
Alias of :func:`smart_bytes` on Python 2 and :func:`smart_text` on Python
- 3. This function returns a :class:`str` or a lazy string.
+ 3. This function returns a ``str`` or a lazy string.
- For instance, this is suitable for writing to :attr:`sys.stdout` on
+ For instance, this is suitable for writing to :data:`sys.stdout` on
Python 2 and 3.
.. function:: force_str(s, encoding='utf-8', strings_only=False, errors='strict')
Alias of :func:`force_bytes` on Python 2 and :func:`force_text` on Python
- 3. This function always returns a :class:`str`.
+ 3. This function always returns a ``str``.
.. function:: iri_to_uri(iri)
@@ -532,7 +529,7 @@ escaping HTML.
.. code-block:: python
- format_html(u"%{0} <b>{1}</b> {2}",
+ format_html(u"{0} <b>{1}</b> {2}",
mark_safe(some_html), some_text, some_other_text)
This has the advantage that you don't need to apply :func:`escape` to each
@@ -544,6 +541,19 @@ escaping HTML.
through :func:`conditional_escape` which (ultimately) calls
:func:`~django.utils.encoding.force_text` on the values.
+.. function:: format_html_join(sep, format_string, args_generator)
+
+ A wrapper of :func:`format_html`, for the common case of a group of
+ arguments that need to be formatted using the same format string, and then
+ joined using ``sep``. ``sep`` is also passed through
+ :func:`conditional_escape`.
+
+ ``args_generator`` should be an iterator that returns the sequence of
+ ``args`` that will be passed to :func:`format_html`. For example::
+
+ format_html_join('\n', "<li>{0} {1}</li>", ((u.first_name, u.last_name)
+ for u in users))
+
.. function:: strip_tags(value)
Removes anything that looks like an html tag from the string, that is
@@ -558,11 +568,11 @@ escaping HTML.
.. function:: remove_tags(value, tags)
- Removes a list of [X]HTML tag names from the output.
+ Removes a space-separated list of [X]HTML tag names from the output.
For example::
- remove_tags(value, ["b", "span"])
+ remove_tags(value, "b span")
If ``value`` is ``"<b>Joel</b> <button>is</button> a <span>slug</span>"`` the
return value will be ``"Joel <button>is</button> a slug"``.
@@ -626,12 +636,34 @@ escaping HTML.
.. function:: base36_to_int(s)
Converts a base 36 string to an integer. On Python 2 the output is
- guaranteed to be an :class:`int` and not a :class:`long`.
+ guaranteed to be an ``int`` and not a ``long``.
.. function:: int_to_base36(i)
Converts a positive integer to a base 36 string. On Python 2 ``i`` must be
- smaller than :attr:`sys.maxint`.
+ smaller than :data:`sys.maxint`.
+
+``django.utils.module_loading``
+===============================
+
+.. module:: django.utils.module_loading
+ :synopsis: Functions for working with Python modules.
+
+Functions for working with Python modules.
+
+.. function:: import_by_path(dotted_path, error_prefix='')
+
+ Imports a dotted module path and returns the attribute/class designated by
+ the last name in the path. Raises
+ :exc:`~django.core.exceptions.ImproperlyConfigured` if something goes
+ wrong. For example::
+
+ from django.utils.module_loading import import_by_path
+ import_by_path = import_by_path('django.utils.module_loading.import_by_path')
+
+ is equivalent to::
+
+ from django.utils.module_loading import import_by_path
``django.utils.safestring``
===========================
@@ -649,12 +681,12 @@ appropriate entities.
.. versionadded:: 1.5
- A :class:`bytes` subclass that has been specifically marked as "safe"
+ A ``bytes`` subclass that has been specifically marked as "safe"
(requires no further escaping) for HTML output purposes.
.. class:: SafeString
- A :class:`str` subclass that has been specifically marked as "safe"
+ A ``str`` subclass that has been specifically marked as "safe"
(requires no further escaping) for HTML output purposes. This is
:class:`SafeBytes` on Python 2 and :class:`SafeText` on Python 3.
@@ -662,7 +694,7 @@ appropriate entities.
.. versionadded:: 1.5
- A :class:`str` (in Python 3) or :class:`unicode` (in Python 2) subclass
+ A ``str`` (in Python 3) or ``unicode`` (in Python 2) subclass
that has been specifically marked as "safe" for HTML output purposes.
.. class:: SafeUnicode
@@ -788,8 +820,6 @@ For a complete discussion on the usage of the following see the
.. function:: override(language, deactivate=False)
- .. versionadded:: 1.4
-
A Python context manager that uses
:func:`django.utils.translation.activate` to fetch the translation object
for a given language, installing it as the translation object for the
@@ -812,8 +842,6 @@ For a complete discussion on the usage of the following see the
.. function:: get_language_from_request(request, check_path=False)
- .. versionchanged:: 1.4
-
Analyzes the request to find what language the user wants the system to show.
Only languages listed in settings.LANGUAGES are taken into account. If the user
requests a sublanguage where we have a main language, we send out the main
@@ -838,8 +866,6 @@ For a complete discussion on the usage of the following see the
``django.utils.timezone``
=========================
-.. versionadded:: 1.4
-
.. module:: django.utils.timezone
:synopsis: Timezone support.
diff --git a/docs/ref/validators.txt b/docs/ref/validators.txt
index b68d6f2772..92e257ca85 100644
--- a/docs/ref/validators.txt
+++ b/docs/ref/validators.txt
@@ -96,7 +96,7 @@ to, or in lieu of custom ``field.clean()`` methods.
------------------
.. data:: validate_email
- A :class:`RegexValidator` instance that ensures a value looks like an
+ An ``EmailValidator`` instance that ensures a value looks like an
email address.
``validate_slug``
@@ -115,15 +115,13 @@ to, or in lieu of custom ``field.clean()`` methods.
``validate_ipv6_address``
-------------------------
-.. versionadded:: 1.4
.. data:: validate_ipv6_address
- Uses :mod:`django.utils.ipv6` to check the validity of an IPv6 address.
+ Uses ``django.utils.ipv6`` to check the validity of an IPv6 address.
``validate_ipv46_address``
--------------------------
-.. versionadded:: 1.4
.. data:: validate_ipv46_address
diff --git a/docs/ref/views.txt b/docs/ref/views.txt
new file mode 100644
index 0000000000..3753f83f07
--- /dev/null
+++ b/docs/ref/views.txt
@@ -0,0 +1,48 @@
+==============
+Built-in Views
+==============
+
+.. module:: django.views
+ :synopsis: Django's built-in views.
+
+Several of Django's built-in views are documented in
+:doc:`/topics/http/views` as well as elsewhere in the documentation.
+
+Serving files in development
+----------------------------
+
+.. function:: static.serve(request, path, document_root, show_indexes=False)
+
+There may be files other than your project's static assets that, for
+convenience, you'd like to have Django serve for you in local development.
+The :func:`~django.views.static.serve` view can be used to serve any directory
+you give it. (This view is **not** hardened for production use and should be
+used only as a development aid; you should serve these files in production
+using a real front-end webserver).
+
+The most likely example is user-uploaded content in :setting:`MEDIA_ROOT`.
+``django.contrib.staticfiles`` is intended for static assets and has no
+built-in handling for user-uploaded files, but you can have Django serve your
+:setting:`MEDIA_ROOT` by appending something like this to your URLconf::
+
+ from django.conf import settings
+
+ # ... the rest of your URLconf goes here ...
+
+ if settings.DEBUG:
+ urlpatterns += patterns('',
+ url(r'^media/(?P<path>.*)$', 'django.views.static.serve', {
+ 'document_root': settings.MEDIA_ROOT,
+ }),
+ )
+
+Note, the snippet assumes your :setting:`MEDIA_URL` has a value of
+``'/media/'``. This will call the :func:`~django.views.static.serve` view,
+passing in the path from the URLconf and the (required) ``document_root``
+parameter.
+
+Since it can become a bit cumbersome to define this URL pattern, Django
+ships with a small URL helper function :func:`~django.conf.urls.static.static`
+that takes as parameters the prefix such as :setting:`MEDIA_URL` and a dotted
+path to a view, such as ``'django.views.static.serve'``. Any other function
+parameter will be transparently passed to the view.
diff --git a/docs/releases/0.96.txt b/docs/releases/0.96.txt
index a608629957..bbec1c3eaf 100644
--- a/docs/releases/0.96.txt
+++ b/docs/releases/0.96.txt
@@ -34,8 +34,7 @@ exceptions if you attempt to use an older version.
If you're currently unable to upgrade your copy of ``MySQLdb`` to meet
this requirement, a separate, backwards-compatible backend, called
"mysql_old", has been added to Django. To use this backend, change
-the :setting:`DATABASE_ENGINE` setting in your Django settings file from
-this::
+the ``DATABASE_ENGINE`` setting in your Django settings file from this::
DATABASE_ENGINE = "mysql"
@@ -49,7 +48,7 @@ provided only to ease this transition, and is considered deprecated;
aside from any necessary security fixes, it will not be actively
maintained, and it will be removed in a future release of Django.
-Also, note that some features, like the new :setting:`DATABASE_OPTIONS`
+Also, note that some features, like the new ``DATABASE_OPTIONS``
setting (see the :doc:`databases documentation </ref/databases>` for details),
are only available on the "mysql" backend, and will not be made available for
"mysql_old".
@@ -220,7 +219,7 @@ supported :doc:`serialization formats </topics/serialization>`, that will be
loaded into your database at the start of your tests. This makes testing with
real data much easier.
-See :doc:`the testing documentation </topics/testing>` for the full details.
+See :doc:`the testing documentation </topics/testing/index>` for the full details.
Improvements to the admin interface
-----------------------------------
diff --git a/docs/releases/1.0-porting-guide.txt b/docs/releases/1.0-porting-guide.txt
index 29e40b2ebe..644350525c 100644
--- a/docs/releases/1.0-porting-guide.txt
+++ b/docs/releases/1.0-porting-guide.txt
@@ -277,8 +277,9 @@ Handle uploaded files using the new API
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Replace use of uploaded files -- that is, entries in ``request.FILES`` -- as
-simple dictionaries with the new :class:`~django.core.files.UploadedFile`. The
-old dictionary syntax no longer works.
+simple dictionaries with the new
+:class:`~django.core.files.uploadedfile.UploadedFile`. The old dictionary
+syntax no longer works.
Thus, in a view like::
@@ -410,7 +411,7 @@ U.S. local flavor
~~~~~~~~~~~~~~~~~
``django.contrib.localflavor.usa`` has been renamed to
-:mod:`django.contrib.localflavor.us`. This change was made to match the naming
+``django.contrib.localflavor.us``. This change was made to match the naming
scheme of other local flavors. To migrate your code, all you need to do is
change the imports.
@@ -439,9 +440,10 @@ Settings
Better exceptions
~~~~~~~~~~~~~~~~~
-The old :exc:`EnvironmentError` has split into an :exc:`ImportError` when
-Django fails to find the settings module and a :exc:`RuntimeError` when you try
-to reconfigure settings after having already used them
+The old :exc:`~exceptions.EnvironmentError` has split into an
+:exc:`~exceptions.ImportError` when Django fails to find the settings module
+and a :exc:`~exceptions.RuntimeError` when you try to reconfigure settings
+after having already used them.
:setting:`LOGIN_URL` has moved
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -476,8 +478,8 @@ Smaller model changes
Different exception from ``get()``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Managers now return a :exc:`MultipleObjectsReturned` exception
-instead of :exc:`AssertionError`:
+Managers now return a :exc:`~django.core.exceptions.MultipleObjectsReturned`
+exception instead of :exc:`~exceptions.AssertionError`:
Old (0.96)::
@@ -548,7 +550,7 @@ need to reload your data. Do this after you have made the change to using
**Back up your database first!**
For SQLite, this means making a copy of the single file that stores the
- database (the name of that file is the :setting:`DATABASE_NAME` in your
+ database (the name of that file is the ``DATABASE_NAME`` in your
settings.py file).
To upgrade each application to use a ``DecimalField``, you can do the
@@ -641,8 +643,8 @@ The generic relation classes -- ``GenericForeignKey`` and ``GenericRelation``
Testing
-------
-:meth:`django.test.Client.login` has changed
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+:meth:`django.test.client.Client.login` has changed
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Old (0.96)::
@@ -720,7 +722,7 @@ To update your code:
1. Use :class:`django.utils.datastructures.SortedDict` wherever you were
using ``django.newforms.forms.SortedDictFromList``.
-2. Because :meth:`django.utils.datastructures.SortedDict.copy` doesn't
+2. Because ``django.utils.datastructures.SortedDict.copy`` doesn't
return a deepcopy as ``SortedDictFromList.copy()`` did, you will need
to update your code if you were relying on a deepcopy. Do this by using
``copy.deepcopy`` directly.
@@ -769,4 +771,3 @@ Old (0.96) New (1.0)
``backend.uses_case_insensitive_names`` ``connection.features.uses_case_insensitive_names``
``backend.uses_custom_queryset`` ``connection.features.uses_custom_queryset``
======================================= ===================================================
-
diff --git a/docs/releases/1.1-alpha-1.txt b/docs/releases/1.1-alpha-1.txt
index 10b0d5d71e..b20b4103b8 100644
--- a/docs/releases/1.1-alpha-1.txt
+++ b/docs/releases/1.1-alpha-1.txt
@@ -32,11 +32,13 @@ Aggregate support
It's now possible to run SQL aggregate queries (i.e. ``COUNT()``, ``MAX()``,
``MIN()``, etc.) from within Django's ORM. You can choose to either return the
results of the aggregate directly, or else annotate the objects in a
-:class:`QuerySet` with the results of the aggregate query.
+:class:`~django.db.models.query.QuerySet` with the results of the aggregate
+query.
-This feature is available as new :meth:`QuerySet.aggregate()`` and
-:meth:`QuerySet.annotate()`` methods, and is covered in detail in :doc:`the ORM
-aggregation documentation </topics/db/aggregation>`
+This feature is available as new
+:meth:`~django.db.models.query.QuerySet.aggregate` and
+:meth:`~django.db.models.query.QuerySet.annotate` methods, and is covered in
+detail in :doc:`the ORM aggregation documentation </topics/db/aggregation>`.
Query expressions
~~~~~~~~~~~~~~~~~
@@ -51,7 +53,7 @@ Performance improvements
.. currentmodule:: django.test
-Tests written using Django's :doc:`testing framework </topics/testing>` now run
+Tests written using Django's :doc:`testing framework </topics/testing/index>` now run
dramatically faster (as much as 10 times faster in many cases).
This was accomplished through the introduction of transaction-based tests: when
diff --git a/docs/releases/1.1-beta-1.txt b/docs/releases/1.1-beta-1.txt
index 9bac3a53f1..88d8ce5f35 100644
--- a/docs/releases/1.1-beta-1.txt
+++ b/docs/releases/1.1-beta-1.txt
@@ -36,7 +36,7 @@ A number of features have been added to Django's model layer:
You can now control whether or not Django creates database tables for a model
using the :attr:`~Options.managed` model option. This defaults to ``True``,
meaning that Django will create the appropriate database tables in
-:djadmin:`syncdb` and remove them as part of :djadmin:`reset` command. That
+:djadmin:`syncdb` and remove them as part of ``reset`` command. That
is, Django *manages* the database table's lifecycle.
If you set this to ``False``, however, no database table creating or deletion
@@ -102,7 +102,7 @@ Testing improvements
.. currentmodule:: django.test.client
A couple of small but very useful improvements have been made to the
-:doc:`testing framework </topics/testing>`:
+:doc:`testing framework </topics/testing/index>`:
* The test :class:`Client` now can automatically follow redirects with the
``follow`` argument to :meth:`Client.get` and :meth:`Client.post`. This
diff --git a/docs/releases/1.1.txt b/docs/releases/1.1.txt
index 852644dee4..ca8e1fff2a 100644
--- a/docs/releases/1.1.txt
+++ b/docs/releases/1.1.txt
@@ -37,7 +37,7 @@ If you are using a 32-bit platform, you're off the hook; you'll observe no
differences as a result of this change.
However, **users on 64-bit platforms may experience some problems** using the
-:djadmin:`reset` management command. Prior to this change, 64-bit platforms
+``reset`` management command. Prior to this change, 64-bit platforms
would generate a 64-bit, 16 character digest in the constraint name; for
example::
@@ -48,14 +48,14 @@ Following this change, all platforms, regardless of word size, will generate a
ALTER TABLE myapp_sometable ADD CONSTRAINT object_id_refs_id_32091d1e FOREIGN KEY ...
-As a result of this change, you will not be able to use the :djadmin:`reset`
+As a result of this change, you will not be able to use the ``reset``
management command on any table made by a 64-bit machine. This is because the
the new generated name will not match the historically generated name; as a
result, the SQL constructed by the reset command will be invalid.
If you need to reset an application that was created with 64-bit constraints,
you will need to manually drop the old constraint prior to invoking
-:djadmin:`reset`.
+``reset``.
Test cases are now run in a transaction
---------------------------------------
@@ -120,9 +120,8 @@ has been saved.
Changes to how model formsets are saved
---------------------------------------
-.. currentmodule:: django.forms.models
-
-In Django 1.1, :class:`BaseModelFormSet` now calls :meth:`ModelForm.save()`.
+In Django 1.1, :class:`~django.forms.models.BaseModelFormSet` now calls
+``ModelForm.save()``.
This is backwards-incompatible if you were modifying ``self.initial`` in a model
formset's ``__init__``, or if you relied on the internal ``_total_form_count``
@@ -132,7 +131,7 @@ public methods.
Fixed the ``join`` filter's escaping behavior
---------------------------------------------
-The :ttag:`join` filter no longer escapes the literal value that is
+The :tfilter:`join` filter no longer escapes the literal value that is
passed in for the connector.
This is backwards incompatible for the special situation of the literal string
@@ -146,7 +145,7 @@ Permanent redirects and the ``redirect_to()`` generic view
----------------------------------------------------------
Django 1.1 adds a ``permanent`` argument to the
-:func:`django.views.generic.simple.redirect_to()` view. This is technically
+``django.views.generic.simple.redirect_to()`` view. This is technically
backwards-incompatible if you were using the ``redirect_to`` view with a
format-string key called 'permanent', which is highly unlikely.
@@ -198,19 +197,21 @@ Aggregate support
It's now possible to run SQL aggregate queries (i.e. ``COUNT()``, ``MAX()``,
``MIN()``, etc.) from within Django's ORM. You can choose to either return the
results of the aggregate directly, or else annotate the objects in a
-:class:`QuerySet` with the results of the aggregate query.
+:class:`~django.db.models.query.QuerySet` with the results of the aggregate
+query.
-This feature is available as new :meth:`QuerySet.aggregate()`` and
-:meth:`QuerySet.annotate()`` methods, and is covered in detail in :doc:`the ORM
-aggregation documentation </topics/db/aggregation>`.
+This feature is available as new
+:meth:`~django.db.models.query.QuerySet.aggregate` and
+:meth:`~django.db.models.query.QuerySet.annotate` methods, and is covered in
+detail in :doc:`the ORM aggregation documentation </topics/db/aggregation>`.
Query expressions
~~~~~~~~~~~~~~~~~
Queries can now refer to a another field on the query and can traverse
relationships to refer to fields on related models. This is implemented in the
-new :class:`F` object; for full details, including examples, consult the
-:ref:`documentation for F expressions <query-expressions>`.
+new :class:`~django.db.models.F` object; for full details, including examples,
+consult the :ref:`documentation for F expressions <query-expressions>`.
Model improvements
------------------
@@ -223,7 +224,7 @@ A number of features have been added to Django's model layer:
You can now control whether or not Django manages the life-cycle of the database
tables for a model using the :attr:`~Options.managed` model option. This
defaults to ``True``, meaning that Django will create the appropriate database
-tables in :djadmin:`syncdb` and remove them as part of the :djadmin:`reset`
+tables in :djadmin:`syncdb` and remove them as part of the ``reset``
command. That is, Django *manages* the database table's lifecycle.
If you set this to ``False``, however, no database table creating or deletion
@@ -264,14 +265,14 @@ Testing improvements
--------------------
A few notable improvements have been made to the :doc:`testing framework
-</topics/testing>`.
+</topics/testing/index>`.
Test performance improvements
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. currentmodule:: django.test
-Tests written using Django's :doc:`testing framework </topics/testing>` now run
+Tests written using Django's :doc:`testing framework </topics/testing/index>` now run
dramatically faster (as much as 10 times faster in many cases).
This was accomplished through the introduction of transaction-based tests: when
diff --git a/docs/releases/1.2-alpha-1.txt b/docs/releases/1.2-alpha-1.txt
index 5a8f8fc5f5..8c905f6ef0 100644
--- a/docs/releases/1.2-alpha-1.txt
+++ b/docs/releases/1.2-alpha-1.txt
@@ -285,16 +285,16 @@ This affects the following settings:
========================================= ==========================
Old setting New Setting
========================================= ==========================
-:setting:`DATABASE_ENGINE` :setting:`ENGINE`
-:setting:`DATABASE_HOST` :setting:`HOST`
-:setting:`DATABASE_NAME` :setting:`NAME`
-:setting:`DATABASE_OPTIONS` :setting:`OPTIONS`
-:setting:`DATABASE_PASSWORD` :setting:`PASSWORD`
-:setting:`DATABASE_PORT` :setting:`PORT`
-:setting:`DATABASE_USER` :setting:`USER`
-:setting:`TEST_DATABASE_CHARSET` :setting:`TEST_CHARSET`
-:setting:`TEST_DATABASE_COLLATION` :setting:`TEST_COLLATION`
-:setting:`TEST_DATABASE_NAME` :setting:`TEST_NAME`
+`DATABASE_ENGINE` :setting:`ENGINE <DATABASE-ENGINE>`
+`DATABASE_HOST` :setting:`HOST`
+`DATABASE_NAME` :setting:`NAME`
+`DATABASE_OPTIONS` :setting:`OPTIONS`
+`DATABASE_PASSWORD` :setting:`PASSWORD`
+`DATABASE_PORT` :setting:`PORT`
+`DATABASE_USER` :setting:`USER`
+`TEST_DATABASE_CHARSET` :setting:`TEST_CHARSET`
+`TEST_DATABASE_COLLATION` :setting:`TEST_COLLATION`
+`TEST_DATABASE_NAME` :setting:`TEST_NAME`
========================================= ==========================
These changes are also required if you have manually created a database
@@ -428,7 +428,7 @@ Support for multiple databases
Django 1.2 adds the ability to use :doc:`more than one database
</topics/db/multi-db>` in your Django project. Queries can be
-issued at a specific database with the `using()` method on
+issued at a specific database with the ``using()`` method on
querysets; individual objects can be saved to a specific database
by providing a ``using`` argument when you save the instance.
diff --git a/docs/releases/1.2-beta-1.txt b/docs/releases/1.2-beta-1.txt
index 99cd274957..abb0f3bbb9 100644
--- a/docs/releases/1.2-beta-1.txt
+++ b/docs/releases/1.2-beta-1.txt
@@ -47,7 +47,7 @@ should be updated to use the new :ref:`class-based runners
Syndication feeds
-----------------
-The :class:`django.contrib.syndication.feeds.Feed` class is being
+The ``django.contrib.syndication.feeds.Feed`` class is being
replaced by the :class:`django.contrib.syndication.views.Feed` class.
The old ``feeds.Feed`` class is deprecated. The new class has an
almost identical API, but allows instances to be used as views.
@@ -91,7 +91,7 @@ added in Django 1.2 alpha but not documented with the alpha release.
The default authentication backends shipped with Django do not
currently make use of this, but third-party authentication backends
-are free to do so. See the :doc:`authentication docs </topics/auth>`
+are free to do so. See the :doc:`authentication docs </topics/auth/index>`
for more information.
@@ -104,7 +104,7 @@ class will check the backend for permissions, just as the normal
``User`` does. This is intended to help centralize permission
handling; apps can always delegate the question of whether something
is allowed or not to the authorization/authentication system. See the
-:doc:`authentication docs </topics/auth>` for more details.
+:doc:`authentication docs </topics/auth/index>` for more details.
``select_related()`` improvements
diff --git a/docs/releases/1.2.4.txt b/docs/releases/1.2.4.txt
index cd4ab76f55..b74ea9aef2 100644
--- a/docs/releases/1.2.4.txt
+++ b/docs/releases/1.2.4.txt
@@ -76,7 +76,7 @@ GeoDjango
=========
The function-based :setting:`TEST_RUNNER` previously used to execute
-the GeoDjango test suite, :func:`django.contrib.gis.tests.run_gis_tests`,
+the GeoDjango test suite, ``django.contrib.gis.tests.run_gis_tests``,
was finally deprecated in favor of a class-based test runner,
:class:`django.contrib.gis.tests.GeoDjangoTestSuiteRunner`, added in this
release.
diff --git a/docs/releases/1.2.txt b/docs/releases/1.2.txt
index 334f9eb94b..ad39062ed1 100644
--- a/docs/releases/1.2.txt
+++ b/docs/releases/1.2.txt
@@ -123,9 +123,9 @@ Support for multiple databases
Django 1.2 adds the ability to use :doc:`more than one database
</topics/db/multi-db>` in your Django project. Queries can be issued at a
-specific database with the `using()` method on ``QuerySet`` objects. Individual
-objects can be saved to a specific database by providing a ``using`` argument
-when you call ``save()``.
+specific database with the ``using()`` method on ``QuerySet`` objects.
+Individual objects can be saved to a specific database by providing a ``using``
+argument when you call ``save()``.
Model validation
----------------
@@ -165,7 +165,7 @@ A foundation for specifying permissions at the per-object level has been added.
Although there is no implementation of this in core, a custom authentication
backend can provide this implementation and it will be used by
:class:`django.contrib.auth.models.User`. See the :doc:`authentication docs
-</topics/auth>` for more information.
+</topics/auth/index>` for more information.
Permissions for anonymous users
-------------------------------
@@ -175,7 +175,7 @@ If you provide a custom auth backend with ``supports_anonymous_user`` set to
User already did. This is useful for centralizing permission handling - apps
can always delegate the question of whether something is allowed or not to
the authorization/authentication backend. See the :doc:`authentication
-docs </topics/auth>` for more details.
+docs </topics/auth/index>` for more details.
Relaxed requirements for usernames
----------------------------------
@@ -345,10 +345,10 @@ in 1.2 is support for multiple spatial databases. As a result,
the following :ref:`spatial database backends <spatial-backends>`
are now included:
-* :mod:`django.contrib.gis.db.backends.postgis`
-* :mod:`django.contrib.gis.db.backends.mysql`
-* :mod:`django.contrib.gis.db.backends.oracle`
-* :mod:`django.contrib.gis.db.backends.spatialite`
+* ``django.contrib.gis.db.backends.postgis``
+* ``django.contrib.gis.db.backends.mysql``
+* ``django.contrib.gis.db.backends.oracle``
+* ``django.contrib.gis.db.backends.spatialite``
GeoDjango now supports the rich capabilities added
in the `PostGIS 1.5 release <http://postgis.refractions.net/documentation/manual-1.5/>`_.
@@ -765,7 +765,7 @@ over the next few release cycles.
Code taking advantage of any of the features below will raise a
``PendingDeprecationWarning`` in Django 1.2. This warning will be
silent by default, but may be turned on using Python's :mod:`warnings`
-module, or by running Python with a ``-Wd`` or `-Wall` flag.
+module, or by running Python with a ``-Wd`` or ``-Wall`` flag.
In Django 1.3, these warnings will become a ``DeprecationWarning``,
which is *not* silent. In Django 1.4 support for these features will
@@ -819,16 +819,16 @@ This affects the following settings:
========================================= ==========================
Old setting New Setting
========================================= ==========================
-:setting:`DATABASE_ENGINE` :setting:`ENGINE`
-:setting:`DATABASE_HOST` :setting:`HOST`
-:setting:`DATABASE_NAME` :setting:`NAME`
-:setting:`DATABASE_OPTIONS` :setting:`OPTIONS`
-:setting:`DATABASE_PASSWORD` :setting:`PASSWORD`
-:setting:`DATABASE_PORT` :setting:`PORT`
-:setting:`DATABASE_USER` :setting:`USER`
-:setting:`TEST_DATABASE_CHARSET` :setting:`TEST_CHARSET`
-:setting:`TEST_DATABASE_COLLATION` :setting:`TEST_COLLATION`
-:setting:`TEST_DATABASE_NAME` :setting:`TEST_NAME`
+`DATABASE_ENGINE` :setting:`ENGINE <DATABASE-ENGINE>`
+`DATABASE_HOST` :setting:`HOST`
+`DATABASE_NAME` :setting:`NAME`
+`DATABASE_OPTIONS` :setting:`OPTIONS`
+`DATABASE_PASSWORD` :setting:`PASSWORD`
+`DATABASE_PORT` :setting:`PORT`
+`DATABASE_USER` :setting:`USER`
+`TEST_DATABASE_CHARSET` :setting:`TEST_CHARSET`
+`TEST_DATABASE_COLLATION` :setting:`TEST_COLLATION`
+`TEST_DATABASE_NAME` :setting:`TEST_NAME`
========================================= ==========================
These changes are also required if you have manually created a database
@@ -850,7 +850,7 @@ has been deprecated.
If you are currently using the ``postgresql`` backend, you should
migrate to using the ``postgresql_psycopg2`` backend. To update your
code, install the ``psycopg2`` library and change the
-:setting:`DATABASE_ENGINE` setting to use
+:setting:`ENGINE <DATABASE-ENGINE>` setting to use
``django.db.backends.postgresql_psycopg2``.
CSRF response-rewriting middleware
@@ -986,7 +986,7 @@ should be updated to use the new :ref:`class-based runners
``Feed`` in ``django.contrib.syndication.feeds``
------------------------------------------------
-The :class:`django.contrib.syndication.feeds.Feed` class has been
+The ``django.contrib.syndication.feeds.Feed`` class has been
replaced by the :class:`django.contrib.syndication.views.Feed` class.
The old ``feeds.Feed`` class is deprecated, and will be removed in
Django 1.4.
diff --git a/docs/releases/1.3-alpha-1.txt b/docs/releases/1.3-alpha-1.txt
index 2f5124e52b..c71736dc60 100644
--- a/docs/releases/1.3-alpha-1.txt
+++ b/docs/releases/1.3-alpha-1.txt
@@ -61,19 +61,18 @@ Django 1.3 ships with a new contrib app ``'django.contrib.staticfiles'``
to help developers handle the static media files (images, CSS, Javascript,
etc.) that are needed to render a complete web page.
-In previous versions of Django, it was common to place static assets in
-:setting:`MEDIA_ROOT` along with user-uploaded files, and serve them both at
-:setting:`MEDIA_URL`. Part of the purpose of introducing the ``staticfiles``
-app is to make it easier to keep static files separate from user-uploaded
-files. For this reason, you will probably want to make your
-:setting:`MEDIA_ROOT` and :setting:`MEDIA_URL` different from your
-:setting:`STATICFILES_ROOT` and :setting:`STATICFILES_URL`. You will need to
-arrange for serving of files in :setting:`MEDIA_ROOT` yourself;
-``staticfiles`` does not deal with user-uploaded media at all.
+In previous versions of Django, it was common to place static assets
+in :setting:`MEDIA_ROOT` along with user-uploaded files, and serve
+them both at :setting:`MEDIA_URL`. Part of the purpose of introducing
+the ``staticfiles`` app is to make it easier to keep static files
+separate from user-uploaded files. Static assets should now go in
+``static/`` subdirectories of your apps or in other static assets
+directories listed in :setting:`STATICFILES_DIRS`, and will be served
+at :setting:`STATIC_URL`.
See the :doc:`reference documentation of the app </ref/contrib/staticfiles>`
for more details or learn how to :doc:`manage static files
-</howto/static-files>`.
+</howto/static-files/index>`.
``unittest2`` support
~~~~~~~~~~~~~~~~~~~~~
@@ -106,16 +105,14 @@ you just won't get any of the nice new unittest2 features.
Transaction context managers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Users of Python 2.5 and above may now use :ref:`transaction management functions
-<transaction-management-functions>` as `context managers`_. For example::
+Users of Python 2.5 and above may now use transaction management functions as
+`context managers`_. For example::
with transaction.autocommit():
# ...
.. _context managers: http://docs.python.org/glossary.html#term-context-manager
-For more information, see :ref:`transaction-management-functions`.
-
Configurable delete-cascade
~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -151,13 +148,13 @@ process has been on adding lots of smaller, long standing feature
requests. These include:
* Improved tools for accessing and manipulating the current Site via
- :func:`django.contrib.sites.models.get_current_site`.
+ ``django.contrib.sites.models.get_current_site()``.
* A :class:`~django.test.client.RequestFactory` for mocking
requests in tests.
* A new test assertion --
- :meth:`~django.test.client.Client.assertNumQueries` -- making it
+ :meth:`~django.test.TestCase.assertNumQueries` -- making it
easier to test the database activity associated with a view.
@@ -280,7 +277,7 @@ over the next few release cycles.
Code taking advantage of any of the features below will raise a
``PendingDeprecationWarning`` in Django 1.3. This warning will be
silent by default, but may be turned on using Python's :mod:`warnings`
-module, or by running Python with a ``-Wd`` or `-Wall` flag.
+module, or by running Python with a ``-Wd`` or ``-Wall`` flag.
In Django 1.4, these warnings will become a ``DeprecationWarning``,
which is *not* silent. In Django 1.5 support for these features will
@@ -312,37 +309,35 @@ As a result of the introduction of class-based generic views, the
function-based generic views provided by Django have been deprecated.
The following modules and the views they contain have been deprecated:
-* :mod:`django.views.generic.create_update`
-* :mod:`django.views.generic.date_based`
-* :mod:`django.views.generic.list_detail`
-* :mod:`django.views.generic.simple`
+* ``django.views.generic.create_update``
+* ``django.views.generic.date_based``
+* ``django.views.generic.list_detail``
+* ``django.views.generic.simple``
Test client response ``template`` attribute
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Django's :ref:`test client <test-client>` returns
:class:`~django.test.client.Response` objects annotated with extra testing
-information. In Django versions prior to 1.3, this included a
-:attr:`~django.test.client.Response.template` attribute containing information
-about templates rendered in generating the response: either None, a single
-:class:`~django.template.Template` object, or a list of
-:class:`~django.template.Template` objects. This inconsistency in return values
-(sometimes a list, sometimes not) made the attribute difficult to work with.
+information. In Django versions prior to 1.3, this included a ``template``
+attribute containing information about templates rendered in generating the
+response: either None, a single :class:`~django.template.Template` object, or a
+list of :class:`~django.template.Template` objects. This inconsistency in
+return values (sometimes a list, sometimes not) made the attribute difficult
+to work with.
-In Django 1.3 the :attr:`~django.test.client.Response.template` attribute is
-deprecated in favor of a new :attr:`~django.test.client.Response.templates`
-attribute, which is always a list, even if it has only a single element or no
-elements.
+In Django 1.3 the ``template`` attribute is deprecated in favor of a new
+:attr:`~django.test.client.Response.templates` attribute, which is always a
+list, even if it has only a single element or no elements.
``DjangoTestRunner``
~~~~~~~~~~~~~~~~~~~~
As a result of the introduction of support for unittest2, the features
-of :class:`django.test.simple.DjangoTestRunner` (including fail-fast
+of ``django.test.simple.DjangoTestRunner`` (including fail-fast
and Ctrl-C test termination) have been made redundant. In view of this
-redundancy, :class:`~django.test.simple.DjangoTestRunner` has been
-turned into an empty placeholder class, and will be removed entirely
-in Django 1.5.
+redundancy, ``DjangoTestRunner`` has been turned into an empty placeholder
+class, and will be removed entirely in Django 1.5.
The Django 1.3 roadmap
======================
diff --git a/docs/releases/1.3-beta-1.txt b/docs/releases/1.3-beta-1.txt
index 02c038f459..aa7800bb94 100644
--- a/docs/releases/1.3-beta-1.txt
+++ b/docs/releases/1.3-beta-1.txt
@@ -37,7 +37,7 @@ Based on feedback from the community this release adds two new options to the
See the :doc:`staticfiles reference documentation </ref/contrib/staticfiles>`
for more details, or learn :doc:`how to manage static files
-</howto/static-files>`.
+</howto/static-files/index>`.
Translation comments
~~~~~~~~~~~~~~~~~~~~
@@ -61,7 +61,7 @@ Permissions for inactive users
If you provide a custom auth backend with ``supports_inactive_user`` set to
``True``, an inactive user model will check the backend for permissions.
This is useful for further centralizing the permission handling. See the
-:doc:`authentication docs </topics/auth>` for more details.
+:doc:`authentication docs </topics/auth/index>` for more details.
Backwards-incompatible changes in 1.3 alpha 2
=============================================
@@ -140,12 +140,11 @@ attribute.
Changes to ``USStateField``
===========================
-The :mod:`django.contrib.localflavor` application contains collections
+The ``django.contrib.localflavor`` application contains collections
of code relevant to specific countries or cultures. One such is
-:class:`~django.contrib.localflavor.us.models.USStateField`, which
-provides a field for storing the two-letter postal abbreviation of a
-U.S. state. This field has consistently caused problems, however,
-because it is often used to store the state portion of a U.S postal
+``USStateField``, which provides a field for storing the two-letter postal
+abbreviation of a U.S. state. This field has consistently caused problems,
+however, because it is often used to store the state portion of a U.S postal
address, but not all "states" recognized by the U.S Postal Service are
actually states of the U.S. or even U.S. territory. Several
compromises over the list of choices resulted in some users feeling
@@ -155,29 +154,29 @@ too few.
In Django 1.3 we're taking a new approach to this problem, implemented
as a pair of changes:
-* The choice list for `USStateField` has changed. Previously, it
+* The choice list for ``USStateField`` has changed. Previously, it
consisted of the 50 U.S. states, the District of Columbia and
U.S. overseas territories. As of Django 1.3 it includes all previous
choices, plus the U.S. Armed Forces postal codes.
* A new model field,
- :class:`django.contrib.localflavor.us.models.USPostalCodeField`, has
+ ``django.contrib.localflavor.us.models.USPostalCodeField``, has
been added which draws its choices from a list of all postal
abbreviations recognized by the U.S Postal Service. This includes
- all abbreviations recognized by `USStateField`, plus three
+ all abbreviations recognized by ``USStateField``, plus three
independent nations -- the Federated States of Micronesia, the
Republic of the Marshall Islands and the Republic of Palau -- which
are serviced under treaty by the U.S. postal system. A new form
- widget, :class:`django.contrib.localflavor.us.forms.USPSSelect`, is
+ widget, ``django.contrib.localflavor.us.forms.USPSSelect``, is
also available and provides the same set of choices.
Additionally, several finer-grained choice tuples are provided which
allow mixing and matching of subsets of the U.S. states and
territories, and other locations serviced by the U.S. postal
-system. Consult the :mod:`django.contrib.localflavor` documentation
+system. Consult the ``django.contrib.localflavor`` documentation
for more details.
-The change to `USStateField` is technically backwards-incompatible for
+The change to ``USStateField`` is technically backwards-incompatible for
users who expect this field to exclude Armed Forces locations. If you
need to support U.S. mailing addresses without Armed Forces locations,
see the list of choice tuples available in the localflavor
diff --git a/docs/releases/1.3.txt b/docs/releases/1.3.txt
index c507f1bed6..9a41f903f8 100644
--- a/docs/releases/1.3.txt
+++ b/docs/releases/1.3.txt
@@ -115,7 +115,7 @@ at :setting:`STATIC_URL`.
See the :doc:`reference documentation of the app </ref/contrib/staticfiles>`
for more details or learn how to :doc:`manage static files
-</howto/static-files>`.
+</howto/static-files/index>`.
unittest2 support
~~~~~~~~~~~~~~~~~
@@ -148,16 +148,14 @@ you just won't get any of the nice new unittest2 features.
Transaction context managers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Users of Python 2.5 and above may now use :ref:`transaction management functions
-<transaction-management-functions>` as `context managers`_. For example::
+Users of Python 2.5 and above may now use transaction management functions as
+`context managers`_. For example::
with transaction.autocommit():
# ...
.. _context managers: http://docs.python.org/glossary.html#term-context-manager
-For more information, see :ref:`transaction-management-functions`.
-
Configurable delete-cascade
~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -254,7 +252,7 @@ Permissions for inactive users
If you provide a custom auth backend with ``supports_inactive_user``
set to ``True``, an inactive ``User`` instance will check the backend
for permissions. This is useful for further centralizing the
-permission handling. See the :doc:`authentication docs </topics/auth>`
+permission handling. See the :doc:`authentication docs </topics/auth/index>`
for more details.
GeoDjango
@@ -367,9 +365,8 @@ In earlier Django versions, when a model instance containing a
file from the backend storage. This opened the door to several data-loss
scenarios, including rolled-back transactions and fields on different models
referencing the same file. In Django 1.3, when a model is deleted the
-:class:`~django.db.models.FileField`'s
-:func:`~django.db.models.FileField.delete` method won't be called. If you
-need cleanup of orphaned files, you'll need to handle it yourself (for
+:class:`~django.db.models.FileField`'s ``delete()`` method won't be called. If
+you need cleanup of orphaned files, you'll need to handle it yourself (for
instance, with a custom management command that can be run manually or
scheduled to run periodically via e.g. cron).
@@ -665,7 +662,7 @@ over the next few release cycles.
Code taking advantage of any of the features below will raise a
``PendingDeprecationWarning`` in Django 1.3. This warning will be
silent by default, but may be turned on using Python's :mod:`warnings`
-module, or by running Python with a ``-Wd`` or `-Wall` flag.
+module, or by running Python with a ``-Wd`` or ``-Wall`` flag.
In Django 1.4, these warnings will become a ``DeprecationWarning``,
which is *not* silent. In Django 1.5 support for these features will
@@ -700,40 +697,35 @@ As a result of the introduction of class-based generic views, the
function-based generic views provided by Django have been deprecated.
The following modules and the views they contain have been deprecated:
-* :mod:`django.views.generic.create_update`
-
-* :mod:`django.views.generic.date_based`
-
-* :mod:`django.views.generic.list_detail`
-
-* :mod:`django.views.generic.simple`
+* ``django.views.generic.create_update``
+* ``django.views.generic.date_based``
+* ``django.views.generic.list_detail``
+* ``django.views.generic.simple``
Test client response ``template`` attribute
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Django's :ref:`test client <test-client>` returns
:class:`~django.test.client.Response` objects annotated with extra testing
-information. In Django versions prior to 1.3, this included a
-:attr:`~django.test.client.Response.template` attribute containing information
-about templates rendered in generating the response: either None, a single
-:class:`~django.template.Template` object, or a list of
-:class:`~django.template.Template` objects. This inconsistency in return values
-(sometimes a list, sometimes not) made the attribute difficult to work with.
+information. In Django versions prior to 1.3, this included a ``template``
+attribute containing information about templates rendered in generating the
+response: either None, a single :class:`~django.template.Template` object, or a
+list of :class:`~django.template.Template` objects. This inconsistency in
+return values (sometimes a list, sometimes not) made the attribute difficult
+to work with.
-In Django 1.3 the :attr:`~django.test.client.Response.template` attribute is
-deprecated in favor of a new :attr:`~django.test.client.Response.templates`
-attribute, which is always a list, even if it has only a single element or no
-elements.
+In Django 1.3 the ``template`` attribute is deprecated in favor of a new
+:attr:`~django.test.client.Response.templates` attribute, which is always a
+list, even if it has only a single element or no elements.
``DjangoTestRunner``
~~~~~~~~~~~~~~~~~~~~
As a result of the introduction of support for unittest2, the features
-of :class:`django.test.simple.DjangoTestRunner` (including fail-fast
+of ``django.test.simple.DjangoTestRunner`` (including fail-fast
and Ctrl-C test termination) have been made redundant. In view of this
-redundancy, :class:`~django.test.simple.DjangoTestRunner` has been
-turned into an empty placeholder class, and will be removed entirely
-in Django 1.5.
+redundancy, ``DjangoTestRunner`` has been turned into an empty placeholder
+class, and will be removed entirely in Django 1.5.
Changes to :ttag:`url` and :ttag:`ssi`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -805,9 +797,8 @@ GeoDjango
~~~~~~~~~
* The function-based :setting:`TEST_RUNNER` previously used to execute
- the GeoDjango test suite,
- :func:`django.contrib.gis.tests.run_gis_tests`, was deprecated for
- the class-based runner,
+ the GeoDjango test suite, ``django.contrib.gis.tests.run_gis_tests``, was
+ deprecated for the class-based runner,
:class:`django.contrib.gis.tests.GeoDjangoTestSuiteRunner`.
* Previously, calling
@@ -886,11 +877,10 @@ identical to their old versions; only the module location has changed.
Removal of ``XMLField``
~~~~~~~~~~~~~~~~~~~~~~~
-When Django was first released, Django included an
-:class:`~django.db.models.XMLField` that performed automatic XML validation
-for any field input. However, this validation function hasn't been
-performed since the introduction of ``newforms``, prior to the 1.0 release.
-As a result, ``XMLField`` as currently implemented is functionally
+When Django was first released, Django included an ``XMLField`` that performed
+automatic XML validation for any field input. However, this validation function
+hasn't been performed since the introduction of ``newforms``, prior to the 1.0
+release. As a result, ``XMLField`` as currently implemented is functionally
indistinguishable from a simple :class:`~django.db.models.TextField`.
For this reason, Django 1.3 has fast-tracked the deprecation of
diff --git a/docs/releases/1.4-alpha-1.txt b/docs/releases/1.4-alpha-1.txt
index b5ec782f09..cb218ac698 100644
--- a/docs/releases/1.4-alpha-1.txt
+++ b/docs/releases/1.4-alpha-1.txt
@@ -357,8 +357,8 @@ Extended IPv6 support
The previously added support for IPv6 addresses when using the runserver
management command in Django 1.3 has now been further extended by adding
-a :class:`~django.db.models.fields.GenericIPAddressField` model field,
-a :class:`~django.forms.fields.GenericIPAddressField` form field and
+a :class:`~django.db.models.GenericIPAddressField` model field,
+a :class:`~django.forms.GenericIPAddressField` form field and
the validators :data:`~django.core.validators.validate_ipv46_address` and
:data:`~django.core.validators.validate_ipv6_address`
@@ -503,8 +503,8 @@ Django 1.4 also includes several smaller improvements worth noting:
* In the documentation, a helpful :doc:`security overview </topics/security>`
page.
-* The :func:`django.contrib.auth.models.check_password` function has been moved
- to the :mod:`django.contrib.auth.utils` module. Importing it from the old
+* The ``django.contrib.auth.models.check_password`` function has been moved
+ to the ``django.contrib.auth.utils`` module. Importing it from the old
location will still work, but you should update your imports.
* The :djadmin:`collectstatic` management command gained a ``--clear`` option
@@ -578,7 +578,7 @@ If you've previously used a URL path for ``ADMIN_MEDIA_PREFIX`` (e.g.
``/media/``) simply make sure :setting:`STATIC_URL` and :setting:`STATIC_ROOT`
are configured and your web server serves the files correctly. The development
server continues to serve the admin files just like before. Don't hesitate to
-consult the :doc:`static files howto </howto/static-files>` for further
+consult the :doc:`static files howto </howto/static-files/index>` for further
details.
In case your ``ADMIN_MEDIA_PREFIX`` is set to an specific domain (e.g.
@@ -813,11 +813,12 @@ For more details, see the documentation about
Until Django 1.3, it was possible to exclude some URLs from Django's
:doc:`404 error reporting</howto/error-reporting>` by adding prefixes to
-:setting:`IGNORABLE_404_STARTS` and suffixes to :setting:`IGNORABLE_404_ENDS`.
+``IGNORABLE_404_STARTS`` and suffixes to ``IGNORABLE_404_ENDS``.
In Django 1.4, these two settings are superseded by
-:setting:`IGNORABLE_404_URLS`, which is a list of compiled regular expressions.
-Django won't send an email for 404 errors on URLs that match any of them.
+:setting:`IGNORABLE_404_URLS`, which is a list of compiled regular
+expressions. Django won't send an email for 404 errors on URLs that match any
+of them.
Furthermore, the previous settings had some rather arbitrary default values::
@@ -827,12 +828,12 @@ Furthermore, the previous settings had some rather arbitrary default values::
It's not Django's role to decide if your website has a legacy ``/cgi-bin/``
section or a ``favicon.ico``. As a consequence, the default values of
-:setting:`IGNORABLE_404_URLS`, :setting:`IGNORABLE_404_STARTS` and
-:setting:`IGNORABLE_404_ENDS` are all now empty.
+:setting:`IGNORABLE_404_URLS`, ``IGNORABLE_404_STARTS``, and
+``IGNORABLE_404_ENDS`` are all now empty.
-If you have customized :setting:`IGNORABLE_404_STARTS` or
-:setting:`IGNORABLE_404_ENDS`, or if you want to keep the old default value,
-you should add the following lines in your settings file::
+If you have customized ``IGNORABLE_404_STARTS`` or ``IGNORABLE_404_ENDS``, or
+if you want to keep the old default value, you should add the following lines
+in your settings file::
import re
IGNORABLE_404_URLS = (
@@ -877,7 +878,7 @@ removed.
The ``open`` method of the base Storage class took an obscure parameter
``mixin`` which allowed you to dynamically change the base classes of the
returned file object. This has been removed. In the rare case you relied on the
-`mixin` parameter, you can easily achieve the same by overriding the `open`
+``mixin`` parameter, you can easily achieve the same by overriding the ``open``
method, e.g.::
from django.core.files import File
diff --git a/docs/releases/1.4-beta-1.txt b/docs/releases/1.4-beta-1.txt
index 88f32ea15f..84b1bf6987 100644
--- a/docs/releases/1.4-beta-1.txt
+++ b/docs/releases/1.4-beta-1.txt
@@ -395,8 +395,8 @@ Extended IPv6 support
The previously added support for IPv6 addresses when using the runserver
management command in Django 1.3 has now been further extended by adding
-a :class:`~django.db.models.fields.GenericIPAddressField` model field,
-a :class:`~django.forms.fields.GenericIPAddressField` form field and
+a :class:`~django.db.models.GenericIPAddressField` model field,
+a :class:`~django.forms.GenericIPAddressField` form field and
the validators :data:`~django.core.validators.validate_ipv46_address` and
:data:`~django.core.validators.validate_ipv6_address`
@@ -563,8 +563,8 @@ Django 1.4 also includes several smaller improvements worth noting:
* In the documentation, a helpful :doc:`security overview </topics/security>`
page.
-* The :func:`django.contrib.auth.models.check_password` function has been moved
- to the :mod:`django.contrib.auth.utils` module. Importing it from the old
+* The ``django.contrib.auth.models.check_password`` function has been moved
+ to the ``django.contrib.auth.utils`` module. Importing it from the old
location will still work, but you should update your imports.
* The :djadmin:`collectstatic` management command gained a ``--clear`` option
@@ -646,7 +646,7 @@ If you've previously used a URL path for ``ADMIN_MEDIA_PREFIX`` (e.g.
``/media/``) simply make sure :setting:`STATIC_URL` and :setting:`STATIC_ROOT`
are configured and your web server serves the files correctly. The development
server continues to serve the admin files just like before. Don't hesitate to
-consult the :doc:`static files howto </howto/static-files>` for further
+consult the :doc:`static files howto </howto/static-files/index>` for further
details.
In case your ``ADMIN_MEDIA_PREFIX`` is set to an specific domain (e.g.
@@ -881,11 +881,12 @@ For more details, see the documentation about
Until Django 1.3, it was possible to exclude some URLs from Django's
:doc:`404 error reporting</howto/error-reporting>` by adding prefixes to
-:setting:`IGNORABLE_404_STARTS` and suffixes to :setting:`IGNORABLE_404_ENDS`.
+``IGNORABLE_404_STARTS`` and suffixes to ``IGNORABLE_404_ENDS``.
In Django 1.4, these two settings are superseded by
-:setting:`IGNORABLE_404_URLS`, which is a list of compiled regular expressions.
-Django won't send an email for 404 errors on URLs that match any of them.
+:setting:`IGNORABLE_404_URLS`, which is a list of compiled regular
+expressions. Django won't send an email for 404 errors on URLs that match any
+of them.
Furthermore, the previous settings had some rather arbitrary default values::
@@ -895,12 +896,12 @@ Furthermore, the previous settings had some rather arbitrary default values::
It's not Django's role to decide if your website has a legacy ``/cgi-bin/``
section or a ``favicon.ico``. As a consequence, the default values of
-:setting:`IGNORABLE_404_URLS`, :setting:`IGNORABLE_404_STARTS` and
-:setting:`IGNORABLE_404_ENDS` are all now empty.
+:setting:`IGNORABLE_404_URLS`, ``IGNORABLE_404_STARTS``, and
+``IGNORABLE_404_ENDS`` are all now empty.
-If you have customized :setting:`IGNORABLE_404_STARTS` or
-:setting:`IGNORABLE_404_ENDS`, or if you want to keep the old default value,
-you should add the following lines in your settings file::
+If you have customized ``IGNORABLE_404_STARTS`` or ``IGNORABLE_404_ENDS``, or
+if you want to keep the old default value, you should add the following lines
+in your settings file::
import re
IGNORABLE_404_URLS = (
@@ -945,7 +946,7 @@ removed.
The ``open`` method of the base Storage class took an obscure parameter
``mixin`` which allowed you to dynamically change the base classes of the
returned file object. This has been removed. In the rare case you relied on the
-`mixin` parameter, you can easily achieve the same by overriding the `open`
+``mixin`` parameter, you can easily achieve the same by overriding the ``open``
method, e.g.::
from django.core.files import File
diff --git a/docs/releases/1.4.txt b/docs/releases/1.4.txt
index 01532cc04c..83a5f54fc7 100644
--- a/docs/releases/1.4.txt
+++ b/docs/releases/1.4.txt
@@ -37,8 +37,8 @@ Other notable new features in Django 1.4 include:
the ability to `bulk insert <#model-objects-bulk-create-in-the-orm>`_
large datasets for improved performance, and
`QuerySet.prefetch_related`_, a method to batch-load related objects
- in areas where :meth:`~django.db.models.QuerySet.select_related` doesn't
- work.
+ in areas where :meth:`~django.db.models.query.QuerySet.select_related`
+ doesn't work.
* Some nice security additions, including `improved password hashing`_
(featuring PBKDF2_ and bcrypt_ support), new `tools for cryptographic
@@ -526,8 +526,8 @@ Extended IPv6 support
~~~~~~~~~~~~~~~~~~~~~
Django 1.4 can now better handle IPv6 addresses with the new
-:class:`~django.db.models.fields.GenericIPAddressField` model field,
-:class:`~django.forms.fields.GenericIPAddressField` form field and
+:class:`~django.db.models.GenericIPAddressField` model field,
+:class:`~django.forms.GenericIPAddressField` form field and
the validators :data:`~django.core.validators.validate_ipv46_address` and
:data:`~django.core.validators.validate_ipv6_address`.
@@ -585,7 +585,7 @@ Django 1.4 also includes several smaller improvements worth noting:
* In the documentation, a helpful :doc:`security overview </topics/security>`
page.
-* The :func:`django.contrib.auth.models.check_password` function has been moved
+* The ``django.contrib.auth.models.check_password`` function has been moved
to the :mod:`django.contrib.auth.hashers` module. Importing it from the old
location will still work, but you should update your imports.
@@ -708,7 +708,7 @@ If you've previously used a URL path for ``ADMIN_MEDIA_PREFIX`` (e.g.
``/media/``) simply make sure :setting:`STATIC_URL` and :setting:`STATIC_ROOT`
are configured and your Web server serves those files correctly. The
development server continues to serve the admin files just like before. Read
-the :doc:`static files howto </howto/static-files>` for more details.
+the :doc:`static files howto </howto/static-files/index>` for more details.
If your ``ADMIN_MEDIA_PREFIX`` is set to an specific domain (e.g.
``http://media.example.com/admin/``), make sure to also set your
@@ -888,10 +888,10 @@ object, Django raises an exception.
``MySQLdb``-specific exceptions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The MySQL backend historically has raised :class:`MySQLdb.OperationalError`
+The MySQL backend historically has raised ``MySQLdb.OperationalError``
when a query triggered an exception. We've fixed this bug, and we now raise
-:class:`django.db.utils.DatabaseError` instead. If you were testing for
-:class:`MySQLdb.OperationalError`, you'll need to update your ``except``
+:exc:`django.db.DatabaseError` instead. If you were testing for
+``MySQLdb.OperationalError``, you'll need to update your ``except``
clauses.
Database connection's thread-locality
@@ -966,11 +966,12 @@ For more details, see the documentation about
Until Django 1.3, it was possible to exclude some URLs from Django's
:doc:`404 error reporting</howto/error-reporting>` by adding prefixes to
-:setting:`IGNORABLE_404_STARTS` and suffixes to :setting:`IGNORABLE_404_ENDS`.
+``IGNORABLE_404_STARTS`` and suffixes to ``IGNORABLE_404_ENDS``.
In Django 1.4, these two settings are superseded by
-:setting:`IGNORABLE_404_URLS`, which is a list of compiled regular expressions.
-Django won't send an email for 404 errors on URLs that match any of them.
+:setting:`IGNORABLE_404_URLS`, which is a list of compiled regular
+expressions. Django won't send an email for 404 errors on URLs that match any
+of them.
Furthermore, the previous settings had some rather arbitrary default values::
@@ -980,12 +981,12 @@ Furthermore, the previous settings had some rather arbitrary default values::
It's not Django's role to decide if your website has a legacy ``/cgi-bin/``
section or a ``favicon.ico``. As a consequence, the default values of
-:setting:`IGNORABLE_404_URLS`, :setting:`IGNORABLE_404_STARTS` and
-:setting:`IGNORABLE_404_ENDS` are all now empty.
+:setting:`IGNORABLE_404_URLS`, ``IGNORABLE_404_STARTS``, and
+``IGNORABLE_404_ENDS`` are all now empty.
-If you have customized :setting:`IGNORABLE_404_STARTS` or
-:setting:`IGNORABLE_404_ENDS`, or if you want to keep the old default value,
-you should add the following lines in your settings file::
+If you have customized ``IGNORABLE_404_STARTS`` or ``IGNORABLE_404_ENDS``, or
+if you want to keep the old default value, you should add the following lines
+in your settings file::
import re
IGNORABLE_404_URLS = (
@@ -1039,7 +1040,7 @@ removed.
The ``open`` method of the base Storage class used to take an obscure parameter
``mixin`` that allowed you to dynamically change the base classes of the
returned file object. This has been removed. In the rare case you relied on the
-`mixin` parameter, you can easily achieve the same by overriding the `open`
+``mixin`` parameter, you can easily achieve the same by overriding the ``open``
method, like this::
from django.core.files import File
@@ -1092,8 +1093,8 @@ wild, because they would confuse browsers too.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
It's now possible to check whether a template was used within a block of
-code with :meth:`~django.test.test.TestCase.assertTemplateUsed` and
-:meth:`~django.test.test.TestCase.assertTemplateNotUsed`. And they
+code with :meth:`~django.test.TestCase.assertTemplateUsed` and
+:meth:`~django.test.TestCase.assertTemplateNotUsed`. And they
can be used as a context manager::
with self.assertTemplateUsed('index.html'):
diff --git a/docs/releases/1.5-alpha-1.txt b/docs/releases/1.5-alpha-1.txt
index 8fbeafc68b..2588b85306 100644
--- a/docs/releases/1.5-alpha-1.txt
+++ b/docs/releases/1.5-alpha-1.txt
@@ -1,6 +1,6 @@
-============================================
-Django 1.5 release notes - UNDER DEVELOPMENT
-============================================
+==============================
+Django 1.5 alpha release notes
+==============================
October 25, 2012.
@@ -227,7 +227,9 @@ GeoDjango
:meth:`~django.contrib.gis.geos.GEOSGeometry.project()` methods
(so-called linear referencing).
-* The wkb and hex properties of `GEOSGeometry` objects preserve the Z dimension.
+* The ``wkb`` and ``hex`` properties of
+ :class:`~django.contrib.gis.geos.GEOSGeometry` objects preserve the Z
+ dimension.
* Support for PostGIS 2.0 has been added and support for GDAL < 1.5 has been
dropped.
@@ -283,8 +285,8 @@ Django 1.5 also includes several smaller improvements worth noting:
* An instance of :class:`~django.core.urlresolvers.ResolverMatch` is stored on
the request as ``resolver_match``.
-* By default, all logging messages reaching the `django` logger when
- :setting:`DEBUG` is `True` are sent to the console (unless you redefine the
+* By default, all logging messages reaching the ``django`` logger when
+ :setting:`DEBUG` is ``True`` are sent to the console (unless you redefine the
logger in your :setting:`LOGGING` setting).
* When using :class:`~django.template.RequestContext`, it is now possible to
@@ -301,8 +303,9 @@ Django 1.5 also includes several smaller improvements worth noting:
whenever a user fails to login successfully. See
:data:`~django.contrib.auth.signals.user_login_failed`
-* The loaddata management command now supports an `ignorenonexistent` option to
- ignore data for fields that no longer exist.
+* The loaddata management command now supports an
+ :djadminopt:`--ignorenonexistent` option to ignore data for fields that no
+ longer exist.
* :meth:`~django.test.SimpleTestCase.assertXMLEqual` and
:meth:`~django.test.SimpleTestCase.assertXMLNotEqual` new assertions allow
@@ -391,12 +394,12 @@ System version of :mod:`simplejson` no longer used
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
As explained below, Django 1.5 deprecates
-:mod:`django.utils.simplejson` in favor of Python 2.6's built-in :mod:`json`
+``django.utils.simplejson`` in favor of Python 2.6's built-in :mod:`json`
module. In theory, this change is harmless. Unfortunately, because of
incompatibilities between versions of :mod:`simplejson`, it may trigger errors
in some circumstances.
-JSON-related features in Django 1.4 always used :mod:`django.utils.simplejson`.
+JSON-related features in Django 1.4 always used ``django.utils.simplejson``.
This module was actually:
- A system version of :mod:`simplejson`, if one was available (ie. ``import
@@ -423,7 +426,7 @@ More information on these incompatibilities is available in `ticket #18023`_.
The net result is that, if you have installed :mod:`simplejson` and your code
uses Django's serialization internals directly -- for instance
-:class:`django.core.serializers.json.DjangoJSONEncoder`, the switch from
+``django.core.serializers.json.DjangoJSONEncoder``, the switch from
:mod:`simplejson` to :mod:`json` could break your code. (In general, changes to
internals aren't documented; we're making an exception here.)
@@ -449,8 +452,8 @@ When using :doc:`object pagination </topics/pagination>`,
the ``previous_page_number()`` and ``next_page_number()`` methods of the
:class:`~django.core.paginator.Page` object did not check if the returned
number was inside the existing page range.
-It does check it now and raises an :exc:`InvalidPage` exception when the number
-is either too low or too high.
+It does check it now and raises an :exc:`~django.core.paginator.InvalidPage`
+exception when the number is either too low or too high.
Behavior of autocommit database option on PostgreSQL changed
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -546,8 +549,9 @@ Miscellaneous
* :class:`django.forms.ModelMultipleChoiceField` now returns an empty
``QuerySet`` as the empty value instead of an empty list.
-* :func:`~django.utils.http.int_to_base36` properly raises a :exc:`TypeError`
- instead of :exc:`ValueError` for non-integer inputs.
+* :func:`~django.utils.http.int_to_base36` properly raises a
+ :exc:`~exceptions.TypeError` instead of :exc:`~exceptions.ValueError` for
+ non-integer inputs.
* The ``slugify`` template filter is now available as a standard python
function at :func:`django.utils.text.slugify`. Similarly, ``remove_tags`` is
@@ -555,7 +559,7 @@ Miscellaneous
* Uploaded files are no longer created as executable by default. If you need
them to be executable change :setting:`FILE_UPLOAD_PERMISSIONS` to your
- needs. The new default value is `0666` (octal) and the current umask value
+ needs. The new default value is ``0666`` (octal) and the current umask value
is first masked out.
* The :ref:`F() expressions <query-expressions>` supported bitwise operators by
@@ -584,8 +588,8 @@ the :setting:`AUTH_PROFILE_MODULE` setting, and the
:meth:`~django.contrib.auth.models.User.get_profile()` method for accessing
the user profile model, should not be used any longer.
-Streaming behavior of :class:`HttpResponse`
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Streaming behavior of :class:`~django.http.HttpResponse`
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Django 1.5 deprecates the ability to stream a response by passing an iterator
to :class:`~django.http.HttpResponse`. If you rely on this behavior, switch to
@@ -600,7 +604,7 @@ In Django 1.7 and above, the iterator will be consumed immediately by
Since Django 1.5 drops support for Python 2.5, we can now rely on the
:mod:`json` module being available in Python's standard library, so we've
removed our own copy of :mod:`simplejson`. You should now import :mod:`json`
-instead :mod:`django.utils.simplejson`.
+instead of ``django.utils.simplejson``.
Unfortunately, this change might have unwanted side-effects, because of
incompatibilities between versions of :mod:`simplejson` -- see the backwards-
@@ -618,10 +622,9 @@ Define a ``__str__`` method and apply the
``django.utils.itercompat.product``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The :func:`~django.utils.itercompat.product` function has been deprecated. Use
+The ``django.utils.itercompat.product`` function has been deprecated. Use
the built-in :func:`itertools.product` instead.
-
``django.utils.markup``
~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/docs/releases/1.5-beta-1.txt b/docs/releases/1.5-beta-1.txt
index f3bfc2a8fa..57b13ea6ce 100644
--- a/docs/releases/1.5-beta-1.txt
+++ b/docs/releases/1.5-beta-1.txt
@@ -225,7 +225,9 @@ GeoDjango
:meth:`~django.contrib.gis.geos.GEOSGeometry.project()` methods
(so-called linear referencing).
-* The wkb and hex properties of `GEOSGeometry` objects preserve the Z dimension.
+* The ``wkb`` and ``hex`` properties of
+ :class:`~django.contrib.gis.geos.GEOSGeometry` objects preserve the Z
+ dimension.
* Support for PostGIS 2.0 has been added and support for GDAL < 1.5 has been
dropped.
@@ -281,8 +283,8 @@ Django 1.5 also includes several smaller improvements worth noting:
* An instance of :class:`~django.core.urlresolvers.ResolverMatch` is stored on
the request as ``resolver_match``.
-* By default, all logging messages reaching the `django` logger when
- :setting:`DEBUG` is `True` are sent to the console (unless you redefine the
+* By default, all logging messages reaching the ``django`` logger when
+ :setting:`DEBUG` is ``True`` are sent to the console (unless you redefine the
logger in your :setting:`LOGGING` setting).
* When using :class:`~django.template.RequestContext`, it is now possible to
@@ -299,8 +301,9 @@ Django 1.5 also includes several smaller improvements worth noting:
whenever a user fails to login successfully. See
:data:`~django.contrib.auth.signals.user_login_failed`
-* The loaddata management command now supports an `ignorenonexistent` option to
- ignore data for fields that no longer exist.
+* The loaddata management command now supports an
+ :djadminopt:`--ignorenonexistent` option to ignore data for fields that no
+ longer exist.
* :meth:`~django.test.SimpleTestCase.assertXMLEqual` and
:meth:`~django.test.SimpleTestCase.assertXMLNotEqual` new assertions allow
@@ -416,12 +419,12 @@ System version of :mod:`simplejson` no longer used
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:ref:`As explained below <simplejson-deprecation-beta-1>`, Django 1.5 deprecates
-:mod:`django.utils.simplejson` in favor of Python 2.6's built-in :mod:`json`
+``django.utils.simplejson`` in favor of Python 2.6's built-in :mod:`json`
module. In theory, this change is harmless. Unfortunately, because of
incompatibilities between versions of :mod:`simplejson`, it may trigger errors
in some circumstances.
-JSON-related features in Django 1.4 always used :mod:`django.utils.simplejson`.
+JSON-related features in Django 1.4 always used ``django.utils.simplejson``.
This module was actually:
- A system version of :mod:`simplejson`, if one was available (ie. ``import
@@ -448,7 +451,7 @@ More information on these incompatibilities is available in `ticket #18023`_.
The net result is that, if you have installed :mod:`simplejson` and your code
uses Django's serialization internals directly -- for instance
-:class:`django.core.serializers.json.DjangoJSONEncoder`, the switch from
+``django.core.serializers.json.DjangoJSONEncoder``, the switch from
:mod:`simplejson` to :mod:`json` could break your code. (In general, changes to
internals aren't documented; we're making an exception here.)
@@ -474,8 +477,8 @@ When using :doc:`object pagination </topics/pagination>`,
the ``previous_page_number()`` and ``next_page_number()`` methods of the
:class:`~django.core.paginator.Page` object did not check if the returned
number was inside the existing page range.
-It does check it now and raises an :exc:`InvalidPage` exception when the number
-is either too low or too high.
+It does check it now and raises an :exc:`~django.core.paginator.InvalidPage`
+exception when the number is either too low or too high.
Behavior of autocommit database option on PostgreSQL changed
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -585,8 +588,9 @@ Miscellaneous
* :class:`django.forms.ModelMultipleChoiceField` now returns an empty
``QuerySet`` as the empty value instead of an empty list.
-* :func:`~django.utils.http.int_to_base36` properly raises a :exc:`TypeError`
- instead of :exc:`ValueError` for non-integer inputs.
+* :func:`~django.utils.http.int_to_base36` properly raises a
+ :exc:`~exceptions.TypeError` instead of :exc:`~exceptions.ValueError` for
+ non-integer inputs.
* The ``slugify`` template filter is now available as a standard python
function at :func:`django.utils.text.slugify`. Similarly, ``remove_tags`` is
@@ -594,7 +598,7 @@ Miscellaneous
* Uploaded files are no longer created as executable by default. If you need
them to be executable change :setting:`FILE_UPLOAD_PERMISSIONS` to your
- needs. The new default value is `0666` (octal) and the current umask value
+ needs. The new default value is ``0666`` (octal) and the current umask value
is first masked out.
* The :ref:`F() expressions <query-expressions>` supported bitwise operators by
@@ -636,8 +640,8 @@ the :setting:`AUTH_PROFILE_MODULE` setting, and the
:meth:`~django.contrib.auth.models.User.get_profile()` method for accessing
the user profile model, should not be used any longer.
-Streaming behavior of :class:`HttpResponse`
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Streaming behavior of :class:`~django.http.HttpResponse`
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Django 1.5 deprecates the ability to stream a response by passing an iterator
to :class:`~django.http.HttpResponse`. If you rely on this behavior, switch to
@@ -653,7 +657,7 @@ In Django 1.7 and above, the iterator will be consumed immediately by
Since Django 1.5 drops support for Python 2.5, we can now rely on the
:mod:`json` module being available in Python's standard library, so we've
removed our own copy of :mod:`simplejson`. You should now import :mod:`json`
-instead :mod:`django.utils.simplejson`.
+instead of ``django.utils.simplejson``.
Unfortunately, this change might have unwanted side-effects, because of
incompatibilities between versions of :mod:`simplejson` -- see the
@@ -671,7 +675,7 @@ Define a ``__str__`` method and apply the
``django.utils.itercompat.product``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The :func:`~django.utils.itercompat.product` function has been deprecated. Use
+The ``django.utils.itercompat.product`` function has been deprecated. Use
the built-in :func:`itertools.product` instead.
``django.utils.markup``
diff --git a/docs/releases/1.5.1.txt b/docs/releases/1.5.1.txt
new file mode 100644
index 0000000000..99998616d2
--- /dev/null
+++ b/docs/releases/1.5.1.txt
@@ -0,0 +1,28 @@
+==========================
+Django 1.5.1 release notes
+==========================
+
+*March 28, 2013*
+
+This is Django 1.5.1, a bugfix release for Django 1.5. It's completely backwards
+compatible with Django 1.5, but includes a handful of fixes.
+
+The biggest fix is for a memory leak introduced in Django 1.5. Under certain
+circumstances, repeated iteration over querysets could leak memory - sometimes
+quite a bit of it. If you'd like more information, the details are in
+`our ticket tracker`__ (and in `a related issue`__ in Python itself).
+
+__ https://code.djangoproject.com/ticket/19895
+__ http://bugs.python.org/issue17468
+
+If you've noticed memory problems under Django 1.5, upgrading to 1.5.1 should
+fix those issues.
+
+Django 1.5.1 also includes a couple smaller fixes:
+
+* Module-level warnings emitted during tests are no longer silently hidden
+ (`#18985`__).
+* Prevented filtering on password hashes in the user admin (`#20078`__).
+
+__ https://code.djangoproject.com/ticket/18985
+__ https://code.djangoproject.com/ticket/20078
diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt
index 6ac5120617..c54373ee41 100644
--- a/docs/releases/1.5.txt
+++ b/docs/releases/1.5.txt
@@ -1,6 +1,6 @@
-============================================
-Django 1.5 release notes - UNDER DEVELOPMENT
-============================================
+========================
+Django 1.5 release notes
+========================
Welcome to Django 1.5!
@@ -67,7 +67,7 @@ can simply remove that line under Django 1.5
Python compatibility
====================
-Django 1.5 requires Python 2.6.5 or above, though we **highly recommended**
+Django 1.5 requires Python 2.6.5 or above, though we **highly recommend**
Python 2.7.3 or above. Support for Python 2.5 and below has been dropped.
This change should affect only a small number of Django users, as most
@@ -94,11 +94,19 @@ applications that support both platforms.
However, we're labeling this support "experimental" for now: although it's
received extensive testing via our automated test suite, it's received very
little real-world testing. We've done our best to eliminate bugs, but we can't
-be sure we covered all possible uses of Django. Further, Django's more than a
-web framework; it's an ecosystem of pluggable components. At this point, very
-few third-party applications have been ported to Python 3, so it's unlikely
-that a real-world application will have all its dependencies satisfied under
-Python 3.
+be sure we covered all possible uses of Django.
+
+Some features of Django aren't available because they depend on third-party
+software that hasn't been ported to Python 3 yet, including:
+
+- the MySQL database backend (depends on MySQLdb)
+- :class:`~django.db.models.ImageField` (depends on PIL)
+- :class:`~django.test.LiveServerTestCase` (depends on Selenium WebDriver)
+
+Further, Django's more than a web framework; it's an ecosystem of pluggable
+components. At this point, very few third-party applications have been ported
+to Python 3, so it's unlikely that a real-world application will have all its
+dependencies satisfied under Python 3.
Thus, we're recommending that Django 1.5 not be used in production under Python
3. Instead, use this opportunity to begin :doc:`porting applications to Python 3
@@ -136,9 +144,9 @@ keyword argument ``update_fields``. By using this argument it is possible to
save only a select list of model's fields. This can be useful for performance
reasons or when trying to avoid overwriting concurrent changes.
-Deferred instances (those loaded by .only() or .defer()) will automatically
-save just the loaded fields. If any field is set manually after load, that
-field will also get updated on save.
+Deferred instances (those loaded by ``.only()`` or ``.defer()``) will
+automatically save just the loaded fields. If any field is set manually after
+load, that field will also get updated on save.
See the :meth:`Model.save() <django.db.models.Model.save()>` documentation for
more details.
@@ -214,7 +222,9 @@ GeoDjango
:meth:`~django.contrib.gis.geos.GEOSGeometry.project()` methods
(so-called linear referencing).
-* The wkb and hex properties of `GEOSGeometry` objects preserve the Z dimension.
+* The ``wkb`` and ``hex`` properties of
+ :class:`~django.contrib.gis.geos.GEOSGeometry` objects preserve the Z
+ dimension.
* Support for PostGIS 2.0 has been added and support for GDAL < 1.5 has been
dropped.
@@ -245,6 +255,11 @@ Django 1.5 also includes several smaller improvements worth noting:
from :ref:`call_command <call-command>`. Any exception raised by the command
(mostly :ref:`CommandError <ref-command-exceptions>`) is propagated.
+ Moreover, when you output errors or messages in your custom commands, you
+ should now use ``self.stdout.write('message')`` and
+ ``self.stderr.write('error')`` (see the note on
+ :ref:`management commands output <management-commands-output>`).
+
* The dumpdata management command outputs one row at a time, preventing
out-of-memory errors when dumping large datasets.
@@ -279,8 +294,8 @@ Django 1.5 also includes several smaller improvements worth noting:
* An instance of :class:`~django.core.urlresolvers.ResolverMatch` is stored on
the request as ``resolver_match``.
-* By default, all logging messages reaching the `django` logger when
- :setting:`DEBUG` is `True` are sent to the console (unless you redefine the
+* By default, all logging messages reaching the ``django`` logger when
+ :setting:`DEBUG` is ``True`` are sent to the console (unless you redefine the
logger in your :setting:`LOGGING` setting).
* When using :class:`~django.template.RequestContext`, it is now possible to
@@ -297,8 +312,9 @@ Django 1.5 also includes several smaller improvements worth noting:
whenever a user fails to login successfully. See
:data:`~django.contrib.auth.signals.user_login_failed`
-* The loaddata management command now supports an `ignorenonexistent` option to
- ignore data for fields that no longer exist.
+* The loaddata management command now supports an
+ :djadminopt:`--ignorenonexistent` option to ignore data for fields that no
+ longer exist.
* :meth:`~django.test.SimpleTestCase.assertXMLEqual` and
:meth:`~django.test.SimpleTestCase.assertXMLNotEqual` new assertions allow
@@ -341,6 +357,16 @@ Backwards incompatible changes in 1.5
deprecation timeline for a given feature, its removal may appear as a
backwards incompatible change.
+``ALLOWED_HOSTS`` required in production
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The new :setting:`ALLOWED_HOSTS` setting validates the request's ``Host``
+header and protects against host-poisoning attacks. This setting is now
+required whenever :setting:`DEBUG` is ``False``, or else
+:meth:`django.http.HttpRequest.get_host()` will raise
+:exc:`~django.core.exceptions.SuspiciousOperation`. For more details see the
+:setting:`full documentation<ALLOWED_HOSTS>` for the new setting.
+
Managers on abstract models
~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -403,6 +429,19 @@ attribute. Developers wishing to access the raw POST data for these cases,
should use the :attr:`request.body <django.http.HttpRequest.body>` attribute
instead.
+:data:`~django.core.signals.request_finished` signal
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Django used to send the :data:`~django.core.signals.request_finished` signal
+as soon as the view function returned a response. This interacted badly with
+:ref:`streaming responses <httpresponse-streaming>` that delay content
+generation.
+
+This signal is now sent after the content is fully consumed by the WSGI
+gateway. This might be backwards incompatible if you rely on the signal being
+fired before sending the response content to the client. If you do, you should
+consider using a middleware instead.
+
OPTIONS, PUT and DELETE requests in the test client
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -429,12 +468,12 @@ System version of :mod:`simplejson` no longer used
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:ref:`As explained below <simplejson-deprecation>`, Django 1.5 deprecates
-:mod:`django.utils.simplejson` in favor of Python 2.6's built-in :mod:`json`
+``django.utils.simplejson`` in favor of Python 2.6's built-in :mod:`json`
module. In theory, this change is harmless. Unfortunately, because of
incompatibilities between versions of :mod:`simplejson`, it may trigger errors
in some circumstances.
-JSON-related features in Django 1.4 always used :mod:`django.utils.simplejson`.
+JSON-related features in Django 1.4 always used ``django.utils.simplejson``.
This module was actually:
- A system version of :mod:`simplejson`, if one was available (ie. ``import
@@ -461,7 +500,7 @@ More information on these incompatibilities is available in `ticket #18023`_.
The net result is that, if you have installed :mod:`simplejson` and your code
uses Django's serialization internals directly -- for instance
-:class:`django.core.serializers.json.DjangoJSONEncoder`, the switch from
+``django.core.serializers.json.DjangoJSONEncoder``, the switch from
:mod:`simplejson` to :mod:`json` could break your code. (In general, changes to
internals aren't documented; we're making an exception here.)
@@ -487,8 +526,8 @@ When using :doc:`object pagination </topics/pagination>`,
the ``previous_page_number()`` and ``next_page_number()`` methods of the
:class:`~django.core.paginator.Page` object did not check if the returned
number was inside the existing page range.
-It does check it now and raises an :exc:`InvalidPage` exception when the number
-is either too low or too high.
+It does check it now and raises an :exc:`~django.core.paginator.InvalidPage`
+exception when the number is either too low or too high.
Behavior of autocommit database option on PostgreSQL changed
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -592,14 +631,34 @@ your routers allow synchronizing content types and permissions to only one of
them. See the docs on the :ref:`behavior of contrib apps with multiple
databases <contrib_app_multiple_databases>` for more information.
+XML deserializer will not parse documents with a DTD
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In order to prevent exposure to denial-of-service attacks related to external
+entity references and entity expansion, the XML model deserializer now refuses
+to parse XML documents containing a DTD (DOCTYPE definition). Since the XML
+serializer does not output a DTD, this will not impact typical usage, only
+cases where custom-created XML documents are passed to Django's model
+deserializer.
+
+Formsets default ``max_num``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A (default) value of ``None`` for the ``max_num`` argument to a formset factory
+no longer defaults to allowing any number of forms in the formset. Instead, in
+order to prevent memory-exhaustion attacks, it now defaults to a limit of 1000
+forms. This limit can be raised by explicitly setting a higher value for
+``max_num``.
+
Miscellaneous
~~~~~~~~~~~~~
* :class:`django.forms.ModelMultipleChoiceField` now returns an empty
``QuerySet`` as the empty value instead of an empty list.
-* :func:`~django.utils.http.int_to_base36` properly raises a :exc:`TypeError`
- instead of :exc:`ValueError` for non-integer inputs.
+* :func:`~django.utils.http.int_to_base36` properly raises a
+ :exc:`~exceptions.TypeError` instead of :exc:`~exceptions.ValueError` for
+ non-integer inputs.
* The ``slugify`` template filter is now available as a standard python
function at :func:`django.utils.text.slugify`. Similarly, ``remove_tags`` is
@@ -607,7 +666,7 @@ Miscellaneous
* Uploaded files are no longer created as executable by default. If you need
them to be executable change :setting:`FILE_UPLOAD_PERMISSIONS` to your
- needs. The new default value is `0666` (octal) and the current umask value
+ needs. The new default value is ``0666`` (octal) and the current umask value
is first masked out.
* The :ref:`F() expressions <query-expressions>` supported bitwise operators by
@@ -631,10 +690,39 @@ Miscellaneous
Attempting to load it with ``{% load adminmedia %}`` will fail. If your
templates still contain that line you must remove it.
+* Because of an implementation oversight, it was possible to use
+ :doc:`django.contrib.redirects </ref/contrib/redirects>` without enabling
+ :doc:`django.contrib.sites </ref/contrib/sites>`. This isn't allowed any
+ longer. If you're using ``django.contrib.redirects``, make sure
+ :setting:`INSTALLED_APPS` contains ``django.contrib.sites``.
+
+* :meth:`BoundField.label_tag <django.forms.BoundField.label_tag>` now
+ escapes its ``contents`` argument. To avoid the HTML escaping, use
+ :func:`django.utils.safestring.mark_safe` on the argument before passing it.
+
Features deprecated in 1.5
==========================
-.. _simplejson-deprecation:
+``django.contrib.localflavor``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The localflavor contrib app has been split into separate packages.
+``django.contrib.localflavor`` itself will be removed in Django 1.6, after an
+:ref:`accelerated deprecation <localflavor-deprecation-policy>`. The docs
+provide :ref:`migration instructions <localflavor-how-to-migrate>`.
+
+The new packages are available :ref:`on Github <localflavor-packages>`. The
+core team cannot efficiently maintain these packages in the long term — it
+spans just a dozen countries at this time; similar to translations, maintenance
+will be handed over to interested members of the community.
+
+``django.contrib.markup``
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The markup contrib module has been deprecated and will follow an accelerated
+deprecation schedule. Direct use of python markup libraries or 3rd party tag
+libraries is preferred to Django maintaining this functionality in the
+framework.
:setting:`AUTH_PROFILE_MODULE`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -649,8 +737,8 @@ the :setting:`AUTH_PROFILE_MODULE` setting, and the
:meth:`~django.contrib.auth.models.User.get_profile()` method for accessing
the user profile model, should not be used any longer.
-Streaming behavior of :class:`HttpResponse`
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Streaming behavior of :class:`~django.http.HttpResponse`
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Django 1.5 deprecates the ability to stream a response by passing an iterator
to :class:`~django.http.HttpResponse`. If you rely on this behavior, switch to
@@ -660,13 +748,15 @@ to :class:`~django.http.HttpResponse`. If you rely on this behavior, switch to
In Django 1.7 and above, the iterator will be consumed immediately by
:class:`~django.http.HttpResponse`.
+.. _simplejson-deprecation:
+
``django.utils.simplejson``
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Since Django 1.5 drops support for Python 2.5, we can now rely on the
:mod:`json` module being available in Python's standard library, so we've
removed our own copy of :mod:`simplejson`. You should now import :mod:`json`
-instead :mod:`django.utils.simplejson`.
+instead of ``django.utils.simplejson``.
Unfortunately, this change might have unwanted side-effects, because of
incompatibilities between versions of :mod:`simplejson` -- see the
@@ -684,17 +774,9 @@ Define a ``__str__`` method and apply the
``django.utils.itercompat.product``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The :func:`~django.utils.itercompat.product` function has been deprecated. Use
+The ``django.utils.itercompat.product`` function has been deprecated. Use
the built-in :func:`itertools.product` instead.
-``django.utils.markup``
-~~~~~~~~~~~~~~~~~~~~~~~
-
-The markup contrib module has been deprecated and will follow an accelerated
-deprecation schedule. Direct use of python markup libraries or 3rd party tag
-libraries is preferred to Django maintaining this functionality in the
-framework.
-
``cleanup`` management command
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/docs/releases/1.6.txt b/docs/releases/1.6.txt
index 1f57913397..2f06756a1b 100644
--- a/docs/releases/1.6.txt
+++ b/docs/releases/1.6.txt
@@ -2,6 +2,24 @@
Django 1.6 release notes - UNDER DEVELOPMENT
============================================
+.. note::
+
+ Dedicated to Malcolm Tredinnick
+
+ On March 17, 2013, the Django project and the free software community lost
+ a very dear friend and developer.
+
+ Malcolm was a long-time contributor to Django, a model community member, a
+ brilliant mind, and a friend. His contributions to Django — and to many other
+ open source projects — are nearly impossible to enumerate. Many on the core
+ Django team had their first patches reviewed by him; his mentorship enriched
+ us. His consideration, patience, and dedication will always be an inspiration
+ to us.
+
+ This release of Django is for Malcolm.
+
+ -- The Django Developers
+
Welcome to Django 1.6!
These release notes cover the `new features`_, as well as some `backwards
@@ -17,17 +35,173 @@ deprecation process for some features`_.
What's new in Django 1.6
========================
+Simplified default project and app templates
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The default templates used by :djadmin:`startproject` and :djadmin:`startapp`
+have been simplified and modernized. The :doc:`admin
+</ref/contrib/admin/index>` is now enabled by default in new projects; the
+:doc:`sites </ref/contrib/sites>` framework no longer is. :ref:`Language
+detection <how-django-discovers-language-preference>` and :ref:`clickjacking
+prevention <clickjacking-prevention>` are turned on.
+
+If the default templates don't suit your tastes, you can use :ref:`custom
+project and app templates <custom-app-and-project-templates>`.
+
+Improved transaction management
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Django's transaction management was overhauled. Database-level autocommit is
+now turned on by default. This makes transaction handling more explicit and
+should improve performance. The existing APIs were deprecated, and new APIs
+were introduced, as described in the :doc:`transaction management docs
+</topics/db/transactions>`.
+
+Please review carefully the list of :ref:`known backwards-incompatibilities
+<transactions-upgrading-from-1.5>` to determine if you need to make changes in
+your code.
+
+Persistent database connections
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Django now supports reusing the same database connection for several requests.
+This avoids the overhead of re-establishing a connection at the beginning of
+each request.
+
+By default, database connections will kept open for 10 minutes. This behavior
+is controlled by the :setting:`CONN_MAX_AGE` setting. To restore the previous
+behavior of closing the connection at the end of each request, set
+:setting:`CONN_MAX_AGE` to ``0``. See :ref:`persistent-database-connections`
+for details.
+
+Time zone aware aggregation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The support for :doc:`time zones </topics/i18n/timezones>` introduced in
+Django 1.4 didn't work well with :meth:`QuerySet.dates()
+<django.db.models.query.QuerySet.dates>`: aggregation was always performed in
+UTC. This limitation was lifted in Django 1.6. Use :meth:`QuerySet.datetimes()
+<django.db.models.query.QuerySet.datetimes>` to perform time zone aware
+aggregation on a :class:`~django.db.models.DateTimeField`.
+
+Support for savepoints in SQLite
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Django 1.6 adds support for savepoints in SQLite, with some :ref:`limitations
+<savepoints-in-sqlite>`.
+
+``BinaryField`` model field
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A new :class:`django.db.models.BinaryField` model field allows to store raw
+binary data in the database.
+
Minor features
~~~~~~~~~~~~~~
* Authentication backends can raise ``PermissionDenied`` to immediately fail
the authentication chain.
+* The HttpOnly flag can be set on the CSRF cookie with
+ :setting:`CSRF_COOKIE_HTTPONLY`.
+
* The ``assertQuerysetEqual()`` now checks for undefined order and raises
``ValueError`` if undefined order is spotted. The order is seen as
undefined if the given ``QuerySet`` isn't ordered and there are more than
one ordered values to compare against.
+* Added :meth:`~django.db.models.query.QuerySet.earliest` for symmetry with
+ :meth:`~django.db.models.query.QuerySet.latest`.
+
+* In addition to :lookup:`year`, :lookup:`month` and :lookup:`day`, the ORM
+ now supports :lookup:`hour`, :lookup:`minute` and :lookup:`second` lookups.
+
+* Django now wraps all PEP-249 exceptions.
+
+* The default widgets for :class:`~django.forms.EmailField`,
+ :class:`~django.forms.URLField`, :class:`~django.forms.IntegerField`,
+ :class:`~django.forms.FloatField` and :class:`~django.forms.DecimalField` use
+ the new type attributes available in HTML5 (type='email', type='url',
+ type='number'). Note that due to erratic support of the ``number`` input type
+ with localized numbers in current browsers, Django only uses it when numeric
+ fields are not localized.
+
+* The ``number`` argument for :ref:`lazy plural translations
+ <lazy-plural-translations>` can be provided at translation time rather than
+ at definition time.
+
+* For custom management commands: Verification of the presence of valid
+ settings in commands that ask for it by using the
+ :attr:`~django.core.management.BaseCommand.can_import_settings` internal
+ option is now performed independently from handling of the locale that
+ should be active during the execution of the command. The latter can now be
+ influenced by the new
+ :attr:`~django.core.management.BaseCommand.leave_locale_alone` internal
+ option. See :ref:`management-commands-and-locales` for more details.
+
+* The :attr:`~django.views.generic.edit.DeletionMixin.success_url` of
+ :class:`~django.views.generic.edit.DeletionMixin` is now interpolated with
+ its ``object``\'s ``__dict__``.
+
+* :class:`~django.http.HttpResponseRedirect` and
+ :class:`~django.http.HttpResponsePermanentRedirect` now provide an ``url``
+ attribute (equivalent to the URL the response will redirect to).
+
+* The ``MemcachedCache`` cache backend now uses the latest :mod:`pickle`
+ protocol available.
+
+* Added :class:`~django.contrib.messages.views.SuccessMessageMixin` which
+ provides a ``success_message`` attribute for
+ :class:`~django.views.generic.edit.FormView` based classes.
+
+* Added the :attr:`django.db.models.ForeignKey.db_constraint` and
+ :attr:`django.db.models.ManyToManyField.db_constraint` options.
+
+* The jQuery library embedded in the admin has been upgraded to version 1.9.1.
+
+* Syndication feeds (:mod:`django.contrib.syndication`) can now pass extra
+ context through to feed templates using a new `Feed.get_context_data()`
+ callback.
+
+* The admin list columns have a ``column-<field_name>`` class in the HTML
+ so the columns header can be styled with CSS, e.g. to set a column width.
+
+* The isolation level can be customized under PostgreSQL.
+
+* The :ttag:`blocktrans` template tag now respects
+ :setting:`TEMPLATE_STRING_IF_INVALID` for variables not present in the
+ context, just like other template constructs.
+
+* SimpleLazyObjects will now present more helpful representations in shell
+ debugging situations.
+
+* Generic :class:`~django.contrib.gis.db.models.GeometryField` is now editable
+ with the OpenLayers widget in the admin.
+
+* The :meth:`Model.save() <django.db.models.Model.save()>` will do
+ ``UPDATE`` - if not updated - ``INSERT`` instead of ``SELECT`` - if not
+ found ``INSERT`` else ``UPDATE`` in case the model's primary key is set.
+
+* The documentation contains a :doc:`deployment checklist
+ </howto/deployment/checklist>`.
+
+* The :djadmin:`diffsettings` comand gained a ``--all`` option.
+
+* ``django.forms.fields.Field.__init__`` now calls ``super()``, allowing
+ field mixins to implement ``__init__()`` methods that will reliably be
+ called.
+
+* The ``validate_max`` parameter was added to ``BaseFormSet`` and
+ :func:`~django.forms.formsets.formset_factory`, and ``ModelForm`` and inline
+ versions of the same. The behavior of validation for formsets with
+ ``max_num`` was clarified. The previously undocumented behavior that
+ hardened formsets against memory exhaustion attacks was documented,
+ and the undocumented limit of the higher of 1000 or ``max_num`` forms
+ was changed so it is always 1000 more than ``max_num``.
+
+* Added ``BCryptSHA256PasswordHasher`` to resolve the password truncation issue
+ with bcrypt.
+
Backwards incompatible changes in 1.6
=====================================
@@ -39,5 +213,335 @@ Backwards incompatible changes in 1.6
deprecation timeline for a given feature, its removal may appear as a
backwards incompatible change.
+New transaction management model
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Behavior changes
+^^^^^^^^^^^^^^^^
+
+Database-level autocommit is enabled by default in Django 1.6. While this
+doesn't change the general spirit of Django's transaction management, there
+are a few known backwards-incompatibities, described in the :ref:`transaction
+management docs <transactions-upgrading-from-1.5>`. You should review your
+code to determine if you're affected.
+
+Savepoints and ``assertNumQueries``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The changes in transaction management may result in additional statements to
+create, release or rollback savepoints. This is more likely to happen with
+SQLite, since it didn't support savepoints until this release.
+
+If tests using :meth:`~django.test.TestCase.assertNumQueries` fail because of
+a higher number of queries than expected, check that the extra queries are
+related to savepoints, and adjust the expected number of queries accordingly.
+
+Autocommit option for PostgreSQL
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+In previous versions, database-level autocommit was only an option for
+PostgreSQL, and it was disabled by default. This option is now :ref:`ignored
+<postgresql-autocommit-mode>` and can be removed.
+
+Addition of ``QuerySet.datetimes()``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When the :doc:`time zone support </topics/i18n/timezones>` added in Django 1.4
+was active, :meth:`QuerySet.dates() <django.db.models.query.QuerySet.dates>`
+lookups returned unexpected results, because the aggregation was performed in
+UTC. To fix this, Django 1.6 introduces a new API, :meth:`QuerySet.datetimes()
+<django.db.models.query.QuerySet.datetimes>`. This requires a few changes in
+your code.
+
+``QuerySet.dates()`` returns ``date`` objects
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+:meth:`QuerySet.dates() <django.db.models.query.QuerySet.dates>` now returns a
+list of :class:`~datetime.date`. It used to return a list of
+:class:`~datetime.datetime`.
+
+:meth:`QuerySet.datetimes() <django.db.models.query.QuerySet.datetimes>`
+returns a list of :class:`~datetime.datetime`.
+
+``QuerySet.dates()`` no longer usable on ``DateTimeField``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+:meth:`QuerySet.dates() <django.db.models.query.QuerySet.dates>` raises an
+error if it's used on :class:`~django.db.models.DateTimeField` when time
+zone support is active. Use :meth:`QuerySet.datetimes()
+<django.db.models.query.QuerySet.datetimes>` instead.
+
+``date_hierarchy`` requires time zone definitions
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The :attr:`~django.contrib.admin.ModelAdmin.date_hierarchy` feature of the
+admin now relies on :meth:`QuerySet.datetimes()
+<django.db.models.query.QuerySet.datetimes>` when it's used on a
+:class:`~django.db.models.DateTimeField`.
+
+This requires time zone definitions in the database when :setting:`USE_TZ` is
+``True``. :ref:`Learn more <database-time-zone-definitions>`.
+
+``date_list`` in generic views requires time zone definitions
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+For the same reason, accessing ``date_list`` in the context of a date-based
+generic view requires time zone definitions in the database when the view is
+based on a :class:`~django.db.models.DateTimeField` and :setting:`USE_TZ` is
+``True``. :ref:`Learn more <database-time-zone-definitions>`.
+
+New lookups may clash with model fields
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Django 1.6 introduces ``hour``, ``minute``, and ``second`` lookups on
+:class:`~django.db.models.DateTimeField`. If you had model fields called
+``hour``, ``minute``, or ``second``, the new lookups will clash with you field
+names. Append an explicit :lookup:`exact` lookup if this is an issue.
+
+Persistent database connections
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Connection setup not repeated for each request
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+When Django establishes a connection to the database, it sets up appropriate
+parameters, depending on the backend being used. Since `persistent database
+connections <persistent-database-connections>`_ are enabled by default in
+Django 1.6, this setup isn't repeated at every request any more. If you
+modifiy parameters such as the connection's isolation level or time zone, you
+should either restore Django's defaults at the end of each request, force an
+appropriate value at the beginning of each request, or disable persistent
+connections.
+
+``BooleanField`` no longer defaults to ``False``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When a :class:`~django.db.models.BooleanField` doesn't have an explicit
+:attr:`~django.db.models.Field.default`, the implicit default value is
+``None``. In previous version of Django, it was ``False``, but that didn't
+represent accurately the lack of a value.
+
+Code that relies on the default value being ``False`` may raise an exception
+when saving new model instances to the database, because ``None`` isn't an
+acceptable value for a :class:`~django.db.models.BooleanField`. You should
+either specify ``default=False`` in the field definition, or ensure the field
+is set to ``True`` or ``False`` before saving the object.
+
+Translations and comments in templates
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Extraction of translations after comments
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Extraction of translatable literals from templates with the
+:djadmin:`makemessages` command now correctly detects i18n constructs when
+they are located after a ``{#`` / ``#}``-type comment on the same line. E.g.:
+
+.. code-block:: html+django
+
+ {# A comment #}{% trans "This literal was incorrectly ignored. Not anymore" %}
+
+Location of translator comments
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Validation of the placement of :ref:`translator-comments-in-templates`
+specified using ``{#`` / ``#}`` is now stricter. All translator comments not
+located at the end of their respective lines in a template are ignored and a
+warning is generated by :djadmin:`makemessages` when it finds them. E.g.:
+
+.. code-block:: html+django
+
+ {# Translators: This is ignored #}{% trans "Translate me" %}
+ {{ title }}{# Translators: Extracted and associated with 'Welcome' below #}
+ <h1>{% trans "Welcome" %}</h1>
+
+Quoting in :func:`~django.core.urlresolvers.reverse`
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When reversing URLs, Django didn't apply :func:`~django.utils.http.urlquote`
+to arguments before interpolating them in URL patterns. This bug is fixed in
+Django 1.6. If you worked around this bug by applying URL quoting before
+passing arguments to :func:`~django.core.urlresolvers.reverse`, this may
+result in double-quoting. If this happens, simply remove the URL quoting from
+your code.
+
+Storage of IP addresses in the comments app
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The :doc:`comments </ref/contrib/comments/index>` app now uses a
+``GenericIPAddressField`` for storing commenters' IP addresses, to support
+comments submitted from IPv6 addresses. Until now, it stored them in an
+``IPAddressField``, which is only meant to support IPv4. When saving a comment
+made from an IPv6 address, the address would be silently truncated on MySQL
+databases, and raise an exception on Oracle. You will need to change the
+column type in your database to benefit from this change.
+
+For MySQL, execute this query on your project's database:
+
+.. code-block:: sql
+
+ ALTER TABLE django_comments MODIFY ip_address VARCHAR(39);
+
+For Oracle, execute this query:
+
+.. code-block:: sql
+
+ ALTER TABLE DJANGO_COMMENTS MODIFY (ip_address VARCHAR2(39));
+
+If you do not apply this change, the behaviour is unchanged: on MySQL, IPv6
+addresses are silently truncated; on Oracle, an exception is generated. No
+database change is needed for SQLite or PostgreSQL databases.
+
+Percent literals in ``cursor.execute`` queries
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When you are running raw SQL queries through the
+:ref:`cursor.execute <executing-custom-sql>` method, the rule about doubling
+percent literals (``%``) inside the query has been unified. Past behavior
+depended on the database backend. Now, across all backends, you only need to
+double literal percent characters if you are also providing replacement
+parameters. For example::
+
+ # No parameters, no percent doubling
+ cursor.execute("SELECT foo FROM bar WHERE baz = '30%'")
+
+ # Parameters passed, non-placeholders have to be doubled
+ cursor.execute("SELECT foo FROM bar WHERE baz = '30%%' and id = %s", [self.id])
+
+``SQLite`` users need to check and update such queries.
+
+Miscellaneous
+~~~~~~~~~~~~~
+
+* The ``django.db.models.query.EmptyQuerySet`` can't be instantiated any more -
+ it is only usable as a marker class for checking if
+ :meth:`~django.db.models.query.QuerySet.none` has been called:
+ ``isinstance(qs.none(), EmptyQuerySet)``
+
+* If your CSS/Javascript code used to access HTML input widgets by type, you
+ should review it as ``type='text'`` widgets might be now output as
+ ``type='email'``, ``type='url'`` or ``type='number'`` depending on their
+ corresponding field type.
+
Features deprecated in 1.6
==========================
+
+Transaction management APIs
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Transaction management was completely overhauled in Django 1.6, and the
+current APIs are deprecated:
+
+- ``django.middleware.transaction.TransactionMiddleware``
+- ``django.db.transaction.autocommit``
+- ``django.db.transaction.commit_on_success``
+- ``django.db.transaction.commit_manually``
+- the ``TRANSACTIONS_MANAGED`` setting
+
+The reasons for this change and the upgrade path are described in the
+:ref:`transactions documentation <transactions-upgrading-from-1.5>`.
+
+``django.contrib.comments``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Django's comment framework has been deprecated and is no longer supported. It
+will be available in Django 1.6 and 1.7, and removed in Django 1.8. Most users
+will be better served with a custom solution, or a hosted product like Disqus__.
+
+The code formerly known as ``django.contrib.comments`` is `still available
+in an external repository`__.
+
+__ https://disqus.com/
+__ https://github.com/django/django-contrib-comments
+
+Support for PostgreSQL versions older than 8.4
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The end of upstream support periods was reached in December 2011 for
+PostgreSQL 8.2 and in February 2013 for 8.3. As a consequence, Django 1.6 sets
+8.4 as the minimum PostgreSQL version it officially supports.
+
+You're strongly encouraged to use the most recent version of PostgreSQL
+available, because of performance improvements and to take advantage of the
+native streaming replication available in PostgreSQL 9.x.
+
+Changes to :ttag:`cycle` and :ttag:`firstof`
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The template system generally escapes all variables to avoid XSS attacks.
+However, due to an accident of history, the :ttag:`cycle` and :ttag:`firstof`
+tags render their arguments as-is.
+
+Django 1.6 starts a process to correct this inconsistency. The ``future``
+template library provides alternate implementations of :ttag:`cycle` and
+:ttag:`firstof` that autoescape their inputs. If you're using these tags,
+you're encourage to include the following line at the top of your templates to
+enable the new behavior::
+
+ {% load cycle from future %}
+
+or::
+
+ {% load firstof from future %}
+
+The tags implementing the old behavior have been deprecated, and in Django
+1.8, the old behavior will be replaced with the new behavior. To ensure
+compatibility with future versions of Django, existing templates should be
+modified to use the ``future`` versions.
+
+If necessary, you can temporarily disable auto-escaping with
+:func:`~django.utils.safestring.mark_safe` or :ttag:`{% autoescape off %}
+<autoescape>`.
+
+``SEND_BROKEN_LINK_EMAILS`` setting
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:class:`~django.middleware.common.CommonMiddleware` used to provide basic
+reporting of broken links by email when ``SEND_BROKEN_LINK_EMAILS`` is set to
+``True``.
+
+Because of intractable ordering problems between
+:class:`~django.middleware.common.CommonMiddleware` and
+:class:`~django.middleware.locale.LocaleMiddleware`, this feature was split
+out into a new middleware:
+:class:`~django.middleware.common.BrokenLinkEmailsMiddleware`.
+
+If you're relying on this feature, you should add
+``'django.middleware.common.BrokenLinkEmailsMiddleware'`` to your
+:setting:`MIDDLEWARE_CLASSES` setting and remove ``SEND_BROKEN_LINK_EMAILS``
+from your settings.
+
+``_has_changed`` method on widgets
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you defined your own form widgets and defined the ``_has_changed`` method
+on a widget, you should now define this method on the form field itself.
+
+``module_name`` model meta attribute
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``Model._meta.module_name`` was renamed to ``model_name``. Despite being a
+private API, it will go through a regular deprecation path.
+
+``get_query_set`` and similar methods renamed to ``get_queryset``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Methods that return a ``QuerySet`` such as ``Manager.get_query_set`` or
+``ModelAdmin.queryset`` have been renamed to ``get_queryset``.
+
+``shortcut`` view and URLconf
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``shortcut`` view was moved from ``django.views.defaults`` to
+``django.contrib.contenttypes.views`` shortly after the 1.0 release, but the
+old location was never deprecated. This oversight was corrected in Django 1.6
+and you should now use the new location.
+
+The URLconf ``django.conf.urls.shortcut`` was also deprecated. If you're
+including it in an URLconf, simply replace::
+
+ (r'^prefix/', include('django.conf.urls.shortcut')),
+
+with::
+
+ (r'^prefix/(?P<content_type_id>\d+)/(?P<object_id>.*)/$', 'django.contrib.contenttypes.views.shortcut'),
diff --git a/docs/releases/index.txt b/docs/releases/index.txt
index b0cd87a168..c5afd8c719 100644
--- a/docs/releases/index.txt
+++ b/docs/releases/index.txt
@@ -28,6 +28,7 @@ Final releases
.. toctree::
:maxdepth: 1
+ 1.5.1
1.5
1.4 release
diff --git a/docs/topics/auth.txt b/docs/topics/auth.txt
deleted file mode 100644
index 3e2b6bbdbf..0000000000
--- a/docs/topics/auth.txt
+++ /dev/null
@@ -1,2696 +0,0 @@
-=============================
-User authentication in Django
-=============================
-
-.. module:: django.contrib.auth
- :synopsis: Django's authentication framework.
-
-Django comes with a user authentication system. It handles user accounts,
-groups, permissions and cookie-based user sessions. This document explains how
-things work.
-
-Overview
-========
-
-The auth system consists of:
-
-* Users
-* Permissions: Binary (yes/no) flags designating whether a user may perform
- a certain task.
-* Groups: A generic way of applying labels and permissions to more than one
- user.
-
-Installation
-============
-
-Authentication support is bundled as a Django application in
-``django.contrib.auth``. To install it, do the following:
-
-1. Put ``'django.contrib.auth'`` and ``'django.contrib.contenttypes'`` in
- your :setting:`INSTALLED_APPS` setting.
- (The :class:`~django.contrib.auth.models.Permission` model in
- :mod:`django.contrib.auth` depends on :mod:`django.contrib.contenttypes`.)
-2. Run the command ``manage.py syncdb``.
-
-Note that the default :file:`settings.py` file created by
-:djadmin:`django-admin.py startproject <startproject>` includes
-``'django.contrib.auth'`` and ``'django.contrib.contenttypes'`` in
-:setting:`INSTALLED_APPS` for convenience. If your :setting:`INSTALLED_APPS`
-already contains these apps, feel free to run :djadmin:`manage.py syncdb
-<syncdb>` again; you can run that command as many times as you'd like, and each
-time it'll only install what's needed.
-
-The :djadmin:`syncdb` command creates the necessary database tables, creates
-permission objects for all installed apps that need 'em, and prompts you to
-create a superuser account the first time you run it.
-
-Once you've taken those steps, that's it.
-
-Users
-=====
-
-.. class:: models.User
-
-API reference
--------------
-
-Fields
-~~~~~~
-
-.. class:: models.User
-
- :class:`~django.contrib.auth.models.User` objects have the following
- fields:
-
- .. attribute:: models.User.username
-
- Required. 30 characters or fewer. Usernames may contain alphanumeric,
- ``_``, ``@``, ``+``, ``.`` and ``-`` characters.
-
- .. attribute:: models.User.first_name
-
- Optional. 30 characters or fewer.
-
- .. attribute:: models.User.last_name
-
- Optional. 30 characters or fewer.
-
- .. attribute:: models.User.email
-
- Optional. Email address.
-
- .. attribute:: models.User.password
-
- Required. A hash of, and metadata about, the password. (Django doesn't
- store the raw password.) Raw passwords can be arbitrarily long and can
- contain any character. See the "Passwords" section below.
-
- .. attribute:: models.User.is_staff
-
- Boolean. Designates whether this user can access the admin site.
-
- .. attribute:: models.User.is_active
-
- Boolean. Designates whether this user account should be considered
- active. We recommend that you set this flag to ``False`` instead of
- deleting accounts; that way, if your applications have any foreign keys
- to users, the foreign keys won't break.
-
- 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, 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.
-
- .. attribute:: models.User.is_superuser
-
- Boolean. Designates that this user has all permissions without
- explicitly assigning them.
-
- .. attribute:: models.User.last_login
-
- A datetime of the user's last login. Is set to the current date/time by
- default.
-
- .. attribute:: models.User.date_joined
-
- A datetime designating when the account was created. Is set to the
- current date/time by default when the account is created.
-
-Methods
-~~~~~~~
-
-.. class:: models.User
-
- :class:`~django.contrib.auth.models.User` objects have two many-to-many
- fields: ``groups`` and ``user_permissions``.
- :class:`~django.contrib.auth.models.User` objects can access their related
- objects in the same way as any other :doc:`Django model
- </topics/db/models>`:
-
- .. code-block:: python
-
- myuser.groups = [group_list]
- myuser.groups.add(group, group, ...)
- myuser.groups.remove(group, group, ...)
- myuser.groups.clear()
- myuser.user_permissions = [permission_list]
- myuser.user_permissions.add(permission, permission, ...)
- myuser.user_permissions.remove(permission, permission, ...)
- myuser.user_permissions.clear()
-
- In addition to those automatic API methods,
- :class:`~django.contrib.auth.models.User` objects have the following custom
- methods:
-
- .. method:: models.User.get_username()
-
- Returns the username for the user. Since the User model can be swapped
- out, you should use this method instead of referencing the username
- attribute directly.
-
- .. method:: models.User.is_anonymous()
-
- Always returns ``False``. This is a way of differentiating
- :class:`~django.contrib.auth.models.User` and
- :class:`~django.contrib.auth.models.AnonymousUser` objects.
- Generally, you should prefer using
- :meth:`~django.contrib.auth.models.User.is_authenticated()` to this
- method.
-
- .. method:: models.User.is_authenticated()
-
- Always returns ``True``. This is a way to tell if the user has been
- authenticated. This does not imply any permissions, and doesn't check
- if the user is active - it only indicates that the user has provided a
- valid username and password.
-
- .. method:: models.User.get_full_name()
-
- Returns the :attr:`~django.contrib.auth.models.User.first_name` plus
- the :attr:`~django.contrib.auth.models.User.last_name`, with a space in
- between.
-
- .. method:: models.User.set_password(raw_password)
-
- Sets the user's password to the given raw string, taking care of the
- password hashing. Doesn't save the
- :class:`~django.contrib.auth.models.User` object.
-
- .. method:: models.User.check_password(raw_password)
-
- Returns ``True`` if the given raw string is the correct password for
- the user. (This takes care of the password hashing in making the
- comparison.)
-
- .. method:: models.User.set_unusable_password()
-
- Marks the user as having no password set. This isn't the same as
- having a blank string for a password.
- :meth:`~django.contrib.auth.models.User.check_password()` for this user
- will never return ``True``. Doesn't save the
- :class:`~django.contrib.auth.models.User` object.
-
- You may need this if authentication for your application takes place
- against an existing external source such as an LDAP directory.
-
- .. method:: models.User.has_usable_password()
-
- Returns ``False`` if
- :meth:`~django.contrib.auth.models.User.set_unusable_password()` has
- been called for this user.
-
- .. method:: models.User.get_group_permissions(obj=None)
-
- Returns a set of permission strings that the user has, through his/her
- groups.
-
- If ``obj`` is passed in, only returns the group permissions for
- this specific object.
-
- .. method:: models.User.get_all_permissions(obj=None)
-
- Returns a set of permission strings that the user has, both through
- group and user permissions.
-
- If ``obj`` is passed in, only returns the permissions for this
- specific object.
-
- .. method:: models.User.has_perm(perm, obj=None)
-
- Returns ``True`` if the user has the specified permission, where perm is
- in the format ``"<app label>.<permission codename>"``. (see
- `permissions`_ section below). If the user is inactive, this method will
- always return ``False``.
-
- If ``obj`` is passed in, this method won't check for a permission for
- the model, but for this specific object.
-
- .. method:: models.User.has_perms(perm_list, obj=None)
-
- Returns ``True`` if the user has each of the specified permissions,
- where each perm is in the format
- ``"<app label>.<permission codename>"``. If the user is inactive,
- this method will always return ``False``.
-
- If ``obj`` is passed in, this method won't check for permissions for
- the model, but for the specific object.
-
- .. method:: models.User.has_module_perms(package_name)
-
- Returns ``True`` if the user has any permissions in the given package
- (the Django app label). If the user is inactive, this method will
- always return ``False``.
-
- .. method:: models.User.email_user(subject, message, from_email=None)
-
- Sends an email to the user. If
- :attr:`~django.contrib.auth.models.User.from_email` is ``None``, Django
- uses the :setting:`DEFAULT_FROM_EMAIL`.
-
- .. method:: models.User.get_profile()
-
- .. deprecated:: 1.5
- With the introduction of :ref:`custom User models <auth-custom-user>`,
- the use of :setting:`AUTH_PROFILE_MODULE` to define a single profile
- model is no longer supported. See the
- :doc:`Django 1.5 release notes</releases/1.5>` for more information.
-
- Returns a site-specific profile for this user. Raises
- :exc:`django.contrib.auth.models.SiteProfileNotAvailable` if the
- current site doesn't allow profiles, or
- :exc:`django.core.exceptions.ObjectDoesNotExist` if the user does not
- have a profile. For information on how to define a site-specific user
- profile, see the section on `storing additional user information`_ below.
-
-.. _storing additional user information: #storing-additional-information-about-users
-
-Manager functions
-~~~~~~~~~~~~~~~~~
-
-.. class:: models.UserManager
-
- The :class:`~django.contrib.auth.models.User` model has a custom manager
- that has the following helper functions:
-
- .. method:: models.UserManager.create_user(username, email=None, password=None)
-
- .. versionchanged:: 1.4
- The ``email`` parameter was made optional. The username
- parameter is now checked for emptiness and raises a
- :exc:`ValueError` in case of a negative result.
-
- Creates, saves and returns a :class:`~django.contrib.auth.models.User`.
-
- The :attr:`~django.contrib.auth.models.User.username` and
- :attr:`~django.contrib.auth.models.User.password` are set as given. The
- domain portion of :attr:`~django.contrib.auth.models.User.email` is
- automatically converted to lowercase, and the returned
- :class:`~django.contrib.auth.models.User` object will have
- :attr:`~models.User.is_active` set to ``True``.
-
- If no password is provided,
- :meth:`~django.contrib.auth.models.User.set_unusable_password()` will
- be called.
-
- See `Creating users`_ for example usage.
-
- .. method:: models.UserManager.make_random_password(length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789')
-
- Returns a random password with the given length and given string of
- allowed characters. (Note that the default value of ``allowed_chars``
- doesn't contain letters that can cause user confusion, including:
-
- * ``i``, ``l``, ``I``, and ``1`` (lowercase letter i, lowercase
- letter L, uppercase letter i, and the number one)
- * ``o``, ``O``, and ``0`` (uppercase letter o, lowercase letter o,
- and zero)
-
-Basic usage
------------
-
-.. _topics-auth-creating-users:
-
-Creating users
-~~~~~~~~~~~~~~
-
-The most basic way to create users is to use the
-:meth:`~django.contrib.auth.models.UserManager.create_user` helper function
-that comes with Django::
-
- >>> from django.contrib.auth.models import User
- >>> user = User.objects.create_user('john', 'lennon@thebeatles.com', 'johnpassword')
-
- # At this point, user is a User object that has already been saved
- # to the database. You can continue to change its attributes
- # if you want to change other fields.
- >>> user.is_staff = True
- >>> user.save()
-
-You can also create users using the Django admin site. Assuming you've enabled
-the admin site and hooked it to the URL ``/admin/``, the "Add user" page is at
-``/admin/auth/user/add/``. You should also see a link to "Users" in the "Auth"
-section of the main admin index page. The "Add user" admin page is different
-than standard admin pages in that it requires you to choose a username and
-password before allowing you to edit the rest of the user's fields.
-
-Also note: if you want your own user account to be able to create users using
-the Django admin site, you'll need to give yourself permission to add users
-*and* change users (i.e., the "Add user" and "Change user" permissions). If
-your account has permission to add users but not to change them, you won't be
-able to add users. Why? Because if you have permission to add users, you have
-the power to create superusers, which can then, in turn, change other users. So
-Django requires add *and* change permissions as a slight security measure.
-
-Changing passwords
-~~~~~~~~~~~~~~~~~~
-
-: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
-they both match, the new password will be changed immediately. If you
-do not supply a user, the command will attempt to change the password
-whose username matches the current user.
-
-You can also change a password programmatically, using
-:meth:`~django.contrib.auth.models.User.set_password()`:
-
-.. code-block:: python
-
- >>> from django.contrib.auth.models import User
- >>> u = User.objects.get(username__exact='john')
- >>> u.set_password('new password')
- >>> u.save()
-
-Don't set the :attr:`~django.contrib.auth.models.User.password` attribute
-directly unless you know what you're doing. This is explained in the next
-section.
-
-.. _auth_password_storage:
-
-How Django stores passwords
----------------------------
-
-.. versionadded:: 1.4
- Django 1.4 introduces a new flexible password storage system and uses
- PBKDF2 by default. Previous versions of Django used SHA1, and other
- algorithms couldn't be chosen.
-
-The :attr:`~django.contrib.auth.models.User.password` attribute of a
-:class:`~django.contrib.auth.models.User` object is a string in this format::
-
- algorithm$hash
-
-That's a storage algorithm, and hash, separated by the dollar-sign
-character. The algorithm is one of a number of one way hashing or password
-storage algorithms Django can use; see below. The hash is the result of the one-
-way function.
-
-By default, Django uses the PBKDF2_ algorithm with a SHA256 hash, a
-password stretching mechanism recommended by NIST_. This should be
-sufficient for most users: it's quite secure, requiring massive
-amounts of computing time to break.
-
-However, depending on your requirements, you may choose a different
-algorithm, or even use a custom algorithm to match your specific
-security situation. Again, most users shouldn't need to do this -- if
-you're not sure, you probably don't. If you do, please read on:
-
-Django chooses the an algorithm by consulting the :setting:`PASSWORD_HASHERS`
-setting. This is a list of hashing algorithm classes that this Django
-installation supports. The first entry in this list (that is,
-``settings.PASSWORD_HASHERS[0]``) will be used to store passwords, and all the
-other entries are valid hashers that can be used to check existing passwords.
-This means that if you want to use a different algorithm, you'll need to modify
-:setting:`PASSWORD_HASHERS` to list your preferred algorithm first in the list.
-
-The default for :setting:`PASSWORD_HASHERS` is::
-
- PASSWORD_HASHERS = (
- 'django.contrib.auth.hashers.PBKDF2PasswordHasher',
- 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
- 'django.contrib.auth.hashers.BCryptPasswordHasher',
- 'django.contrib.auth.hashers.SHA1PasswordHasher',
- 'django.contrib.auth.hashers.MD5PasswordHasher',
- 'django.contrib.auth.hashers.CryptPasswordHasher',
- )
-
-This means that Django will use PBKDF2_ to store all passwords, but will support
-checking passwords stored with PBKDF2SHA1, bcrypt_, SHA1_, etc. The next few
-sections describe a couple of common ways advanced users may want to modify this
-setting.
-
-.. _bcrypt_usage:
-
-Using bcrypt with Django
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-Bcrypt_ is a popular password storage algorithm that's specifically designed
-for long-term password storage. It's not the default used by Django since it
-requires the use of third-party libraries, but since many people may want to
-use it Django supports bcrypt with minimal effort.
-
-To use Bcrypt as your default storage algorithm, do the following:
-
-1. Install the `py-bcrypt`_ library (probably by running ``sudo pip install
- py-bcrypt``, or downloading the library and installing it with ``python
- setup.py install``).
-
-2. Modify :setting:`PASSWORD_HASHERS` to list ``BCryptPasswordHasher``
- first. That is, in your settings file, you'd put::
-
- PASSWORD_HASHERS = (
- 'django.contrib.auth.hashers.BCryptPasswordHasher',
- 'django.contrib.auth.hashers.PBKDF2PasswordHasher',
- 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
- 'django.contrib.auth.hashers.SHA1PasswordHasher',
- 'django.contrib.auth.hashers.MD5PasswordHasher',
- 'django.contrib.auth.hashers.CryptPasswordHasher',
- )
-
- (You need to keep the other entries in this list, or else Django won't
- be able to upgrade passwords; see below).
-
-That's it -- now your Django install will use Bcrypt as the default storage
-algorithm.
-
-.. admonition:: Other bcrypt implementations
-
- There are several other implementations that allow bcrypt to be
- used with Django. Django's bcrypt support is NOT directly
- compatible with these. To upgrade, you will need to modify the
- hashes in your database to be in the form `bcrypt$(raw bcrypt
- output)`. For example:
- `bcrypt$$2a$12$NT0I31Sa7ihGEWpka9ASYrEFkhuTNeBQ2xfZskIiiJeyFXhRgS.Sy`.
-
-Increasing the work factor
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The PBKDF2 and bcrypt algorithms use a number of iterations or rounds of
-hashing. This deliberately slows down attackers, making attacks against hashed
-passwords harder. However, as computing power increases, the number of
-iterations needs to be increased. We've chosen a reasonable default (and will
-increase it with each release of Django), but you may wish to tune it up or
-down, depending on your security needs and available processing power. To do so,
-you'll subclass the appropriate algorithm and override the ``iterations``
-parameters. For example, to increase the number of iterations used by the
-default PBKDF2 algorithm:
-
-1. Create a subclass of ``django.contrib.auth.hashers.PBKDF2PasswordHasher``::
-
- from django.contrib.auth.hashers import PBKDF2PasswordHasher
-
- class MyPBKDF2PasswordHasher(PBKDF2PasswordHasher):
- """
- A subclass of PBKDF2PasswordHasher that uses 100 times more iterations.
- """
- iterations = PBKDF2PasswordHasher.iterations * 100
-
- Save this somewhere in your project. For example, you might put this in
- a file like ``myproject/hashers.py``.
-
-2. Add your new hasher as the first entry in :setting:`PASSWORD_HASHERS`::
-
- PASSWORD_HASHERS = (
- 'myproject.hashers.MyPBKDF2PasswordHasher',
- 'django.contrib.auth.hashers.PBKDF2PasswordHasher',
- 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
- 'django.contrib.auth.hashers.BCryptPasswordHasher',
- 'django.contrib.auth.hashers.SHA1PasswordHasher',
- 'django.contrib.auth.hashers.MD5PasswordHasher',
- 'django.contrib.auth.hashers.CryptPasswordHasher',
- )
-
-
-That's it -- now your Django install will use more iterations when it
-stores passwords using PBKDF2.
-
-Password upgrading
-~~~~~~~~~~~~~~~~~~
-
-When users log in, if their passwords are stored with anything other than
-the preferred algorithm, Django will automatically upgrade the algorithm
-to the preferred one. This means that old installs of Django will get
-automatically more secure as users log in, and it also means that you
-can switch to new (and better) storage algorithms as they get invented.
-
-However, Django can only upgrade passwords that use algorithms mentioned in
-:setting:`PASSWORD_HASHERS`, so as you upgrade to new systems you should make
-sure never to *remove* entries from this list. If you do, users using un-
-mentioned algorithms won't be able to upgrade.
-
-.. _sha1: http://en.wikipedia.org/wiki/SHA1
-.. _pbkdf2: http://en.wikipedia.org/wiki/PBKDF2
-.. _nist: http://csrc.nist.gov/publications/nistpubs/800-132/nist-sp800-132.pdf
-.. _bcrypt: http://en.wikipedia.org/wiki/Bcrypt
-.. _py-bcrypt: http://pypi.python.org/pypi/py-bcrypt/
-
-Anonymous users
----------------
-
-.. class:: models.AnonymousUser
-
- :class:`django.contrib.auth.models.AnonymousUser` is a class that
- implements the :class:`django.contrib.auth.models.User` interface, with
- these differences:
-
- * :attr:`~django.contrib.auth.models.User.id` is always ``None``.
- * :attr:`~django.contrib.auth.models.User.is_staff` and
- :attr:`~django.contrib.auth.models.User.is_superuser` are always
- ``False``.
- * :attr:`~django.contrib.auth.models.User.is_active` is always ``False``.
- * :attr:`~django.contrib.auth.models.User.groups` and
- :attr:`~django.contrib.auth.models.User.user_permissions` are always
- empty.
- * :meth:`~django.contrib.auth.models.User.is_anonymous()` returns ``True``
- instead of ``False``.
- * :meth:`~django.contrib.auth.models.User.is_authenticated()` returns
- ``False`` instead of ``True``.
- * :meth:`~django.contrib.auth.models.User.set_password()`,
- :meth:`~django.contrib.auth.models.User.check_password()`,
- :meth:`~django.contrib.auth.models.User.save()`,
- :meth:`~django.contrib.auth.models.User.delete()`,
- :meth:`~django.contrib.auth.models.User.set_groups()` and
- :meth:`~django.contrib.auth.models.User.set_permissions()` raise
- :exc:`NotImplementedError`.
-
-In practice, you probably won't need to use
-:class:`~django.contrib.auth.models.AnonymousUser` objects on your own, but
-they're used by Web requests, as explained in the next section.
-
-.. _topics-auth-creating-superusers:
-
-Creating superusers
--------------------
-
-:djadmin:`manage.py syncdb <syncdb>` prompts you to create a superuser the
-first time you run it after adding ``'django.contrib.auth'`` to your
-:setting:`INSTALLED_APPS`. If you need to create a superuser at a later date,
-you can use a command line utility::
-
- manage.py createsuperuser --username=joe --email=joe@example.com
-
-You will be prompted for a password. After you enter one, the user will be
-created immediately. If you leave off the :djadminopt:`--username` or the
-:djadminopt:`--email` options, it will prompt you for those values.
-
-If you're using an older release of Django, the old way of creating a superuser
-on the command line still works::
-
- python /path/to/django/contrib/auth/create_superuser.py
-
-...where :file:`/path/to` is the path to the Django codebase on your
-filesystem. The ``manage.py`` command is preferred because it figures out the
-correct path and environment for you.
-
-.. _auth-profiles:
-
-Storing additional information about users
-------------------------------------------
-
-.. deprecated:: 1.5
- With the introduction of :ref:`custom User models <auth-custom-user>`,
- the use of :setting:`AUTH_PROFILE_MODULE` to define a single profile
- model is no longer supported. See the
- :doc:`Django 1.5 release notes</releases/1.5>` for more information.
-
-If you'd like to store additional information related to your users, Django
-provides a method to specify a site-specific related model -- termed a "user
-profile" -- for this purpose.
-
-To make use of this feature, define a model with fields for the
-additional information you'd like to store, or additional methods
-you'd like to have available, and also add a
-:class:`~django.db.models.Field.OneToOneField` named ``user`` from your model
-to the :class:`~django.contrib.auth.models.User` model. This will ensure only
-one instance of your model can be created for each
-:class:`~django.contrib.auth.models.User`. For example::
-
- from django.contrib.auth.models import User
-
- class UserProfile(models.Model):
- # This field is required.
- user = models.OneToOneField(User)
-
- # Other fields here
- accepted_eula = models.BooleanField()
- favorite_animal = models.CharField(max_length=20, default="Dragons.")
-
-
-To indicate that this model is the user profile model for a given site, fill in
-the setting :setting:`AUTH_PROFILE_MODULE` with a string consisting of the
-following items, separated by a dot:
-
-1. The name of the application (case sensitive) in which the user
- profile model is defined (in other words, the
- name which was passed to :djadmin:`manage.py startapp <startapp>` to create
- the application).
-
-2. The name of the model (not case sensitive) class.
-
-For example, if the profile model was a class named ``UserProfile`` and was
-defined inside an application named ``accounts``, the appropriate setting would
-be::
-
- AUTH_PROFILE_MODULE = 'accounts.UserProfile'
-
-When a user profile model has been defined and specified in this manner, each
-:class:`~django.contrib.auth.models.User` object will have a method --
-:class:`~django.contrib.auth.models.User.get_profile()` -- which returns the
-instance of the user profile model associated with that
-:class:`~django.contrib.auth.models.User`.
-
-The method :class:`~django.contrib.auth.models.User.get_profile()`
-does not create a profile if one does not exist. You need to register a handler
-for the User model's :attr:`django.db.models.signals.post_save` signal and, in
-the handler, if ``created`` is ``True``, create the associated user profile::
-
- # in models.py
-
- from django.contrib.auth.models import User
- from django.db.models.signals import post_save
-
- # definition of UserProfile from above
- # ...
-
- def create_user_profile(sender, instance, created, **kwargs):
- if created:
- UserProfile.objects.create(user=instance)
-
- post_save.connect(create_user_profile, sender=User)
-
-.. seealso:: :doc:`/topics/signals` for more information on Django's signal
- dispatcher.
-
-Adding UserProfile fields to the admin
---------------------------------------
-
-To add the UserProfile fields to the user page in the admin, define an
-:class:`~django.contrib.admin.InlineModelAdmin` (for this example, we'll use a
-:class:`~django.contrib.admin.StackedInline`) in your app's ``admin.py`` and
-add it to a ``UserAdmin`` class which is registered with the
-:class:`~django.contrib.auth.models.User` class::
-
- from django.contrib import admin
- from django.contrib.auth.admin import UserAdmin
- from django.contrib.auth.models import User
-
- from my_user_profile_app.models import UserProfile
-
- # Define an inline admin descriptor for UserProfile model
- # which acts a bit like a singleton
- class UserProfileInline(admin.StackedInline):
- model = UserProfile
- can_delete = False
- verbose_name_plural = 'profile'
-
- # Define a new User admin
- class UserAdmin(UserAdmin):
- inlines = (UserProfileInline, )
-
- # Re-register UserAdmin
- admin.site.unregister(User)
- admin.site.register(User, UserAdmin)
-
-Authentication in Web requests
-==============================
-
-Until now, this document has dealt with the low-level APIs for manipulating
-authentication-related objects. On a higher level, Django can hook this
-authentication framework into its system of
-:class:`request objects <django.http.HttpRequest>`.
-
-First, install the
-:class:`~django.contrib.sessions.middleware.SessionMiddleware` and
-:class:`~django.contrib.auth.middleware.AuthenticationMiddleware`
-middlewares by adding them to your :setting:`MIDDLEWARE_CLASSES` setting. See
-the :doc:`session documentation </topics/http/sessions>` for more information.
-
-Once you have those middlewares installed, you'll be able to access
-:attr:`request.user <django.http.HttpRequest.user>` in views.
-:attr:`request.user <django.http.HttpRequest.user>` will give you a
-:class:`~django.contrib.auth.models.User` object representing the currently
-logged-in user. If a user isn't currently logged in,
-:attr:`request.user <django.http.HttpRequest.user>` will be set to an instance
-of :class:`~django.contrib.auth.models.AnonymousUser` (see the previous
-section). You can tell them apart with
-:meth:`~django.contrib.auth.models.User.is_authenticated()`, like so::
-
- if request.user.is_authenticated():
- # Do something for authenticated users.
- else:
- # Do something for anonymous users.
-
-.. _how-to-log-a-user-in:
-
-How to log a user in
---------------------
-
-Django provides two functions in :mod:`django.contrib.auth`:
-:func:`~django.contrib.auth.authenticate()` and
-:func:`~django.contrib.auth.login()`.
-
-.. function:: authenticate()
-
- To authenticate a given username and password, use
- :func:`~django.contrib.auth.authenticate()`. It takes two keyword
- arguments, ``username`` and ``password``, and it returns a
- :class:`~django.contrib.auth.models.User` object if the password is valid
- for the given username. If the password is invalid,
- :func:`~django.contrib.auth.authenticate()` returns ``None``. Example::
-
- from django.contrib.auth import authenticate
- user = authenticate(username='john', password='secret')
- if user is not None:
- if user.is_active:
- print("You provided a correct username and password!")
- else:
- print("Your account has been disabled!")
- else:
- print("Your username and password were incorrect.")
-
-.. function:: login()
-
- To log a user in, in a view, use :func:`~django.contrib.auth.login()`. It
- takes an :class:`~django.http.HttpRequest` object and a
- :class:`~django.contrib.auth.models.User` object.
- :func:`~django.contrib.auth.login()` saves the user's ID in the session,
- using Django's session framework, so, as mentioned above, you'll need to
- make sure to have the session middleware installed.
-
- Note that data set during the anonymous session is retained when the user
- logs in.
-
- This example shows how you might use both
- :func:`~django.contrib.auth.authenticate()` and
- :func:`~django.contrib.auth.login()`::
-
- from django.contrib.auth import authenticate, login
-
- def my_view(request):
- username = request.POST['username']
- password = request.POST['password']
- user = authenticate(username=username, password=password)
- if user is not None:
- if user.is_active:
- login(request, user)
- # Redirect to a success page.
- else:
- # Return a 'disabled account' error message
- else:
- # Return an 'invalid login' error message.
-
-.. admonition:: Calling ``authenticate()`` first
-
- When you're manually logging a user in, you *must* call
- :func:`~django.contrib.auth.authenticate()` before you call
- :func:`~django.contrib.auth.login()`.
- :func:`~django.contrib.auth.authenticate()`
- sets an attribute on the :class:`~django.contrib.auth.models.User` noting
- which authentication backend successfully authenticated that user (see the
- `backends documentation`_ for details), and this information is needed
- later during the login process.
-
-.. _backends documentation: #other-authentication-sources
-
-Manually managing a user's password
------------------------------------
-
-.. currentmodule:: django.contrib.auth.hashers
-
-.. versionadded:: 1.4
- The :mod:`django.contrib.auth.hashers` module provides a set of functions
- to create and validate hashed password. You can use them independently
- from the ``User`` model.
-
-.. function:: check_password(password, encoded)
-
- .. versionadded:: 1.4
-
- If you'd like to manually authenticate a user by comparing a plain-text
- password to the hashed password in the database, use the convenience
- function :func:`django.contrib.auth.hashers.check_password`. It takes two
- arguments: the plain-text password to check, and the full value of a
- user's ``password`` field in the database to check against, and returns
- ``True`` if they match, ``False`` otherwise.
-
-.. function:: make_password(password[, salt, hashers])
-
- .. versionadded:: 1.4
-
- Creates a hashed password in the format used by this application. It takes
- one mandatory argument: the password in plain-text. Optionally, you can
- provide a salt and a hashing algorithm to use, if you don't want to use the
- defaults (first entry of ``PASSWORD_HASHERS`` setting).
- Currently supported algorithms are: ``'pbkdf2_sha256'``, ``'pbkdf2_sha1'``,
- ``'bcrypt'`` (see :ref:`bcrypt_usage`), ``'sha1'``, ``'md5'``,
- ``'unsalted_md5'`` (only for backward compatibility) and ``'crypt'``
- if you have the ``crypt`` library installed. If the password argument is
- ``None``, an unusable password is returned (a one that will be never
- accepted by :func:`django.contrib.auth.hashers.check_password`).
-
-.. function:: is_password_usable(encoded_password)
-
- .. versionadded:: 1.4
-
- Checks if the given string is a hashed password that has a chance
- of being verified against :func:`django.contrib.auth.hashers.check_password`.
-
-
-How to log a user out
----------------------
-
-.. currentmodule:: django.contrib.auth
-
-.. function:: logout()
-
- To log out a user who has been logged in via
- :func:`django.contrib.auth.login()`, use
- :func:`django.contrib.auth.logout()` within your view. It takes an
- :class:`~django.http.HttpRequest` object and has no return value.
- Example::
-
- from django.contrib.auth import logout
-
- def logout_view(request):
- logout(request)
- # Redirect to a success page.
-
- Note that :func:`~django.contrib.auth.logout()` doesn't throw any errors if
- the user wasn't logged in.
-
- When you call :func:`~django.contrib.auth.logout()`, the session data for
- the current request is completely cleaned out. All existing data is
- removed. This is to prevent another person from using the same Web browser
- to log in and have access to the previous user's session data. If you want
- to put anything into the session that will be available to the user
- immediately after logging out, do that *after* calling
- :func:`django.contrib.auth.logout()`.
-
-.. _topics-auth-signals:
-
-Login and logout signals
-------------------------
-
-The auth framework uses two :doc:`signals </topics/signals>` that can be used
-for notification when a user logs in or out.
-
-.. data:: django.contrib.auth.signals.user_logged_in
- :module:
-
-Sent when a user logs in successfully.
-
-Arguments sent with this signal:
-
-``sender``
- The class of the user that just logged in.
-
-``request``
- The current :class:`~django.http.HttpRequest` instance.
-
-``user``
- The user instance that just logged in.
-
-.. data:: django.contrib.auth.signals.user_logged_out
- :module:
-
-Sent when the logout method is called.
-
-``sender``
- As above: the class of the user that just logged out or ``None``
- if the user was not authenticated.
-
-``request``
- The current :class:`~django.http.HttpRequest` instance.
-
-``user``
- The user instance that just logged out or ``None`` if the
- user was not authenticated.
-
-.. data:: django.contrib.auth.signals.user_login_failed
- :module:
-.. versionadded:: 1.5
-
-Sent when the user failed to login successfully
-
-``sender``
- The name of the module used for authentication.
-
-``credentials``
- A dictonary of keyword arguments containing the user credentials that were
- passed to :func:`~django.contrib.auth.authenticate()` or your own custom
- authentication backend. Credentials matching a set of 'sensitive' patterns,
- (including password) will not be sent in the clear as part of the signal.
-
-Limiting access to logged-in users
-----------------------------------
-
-The raw way
-~~~~~~~~~~~
-
-The simple, raw way to limit access to pages is to check
-:meth:`request.user.is_authenticated()
-<django.contrib.auth.models.User.is_authenticated()>` and either redirect to a
-login page::
-
- from django.http import HttpResponseRedirect
-
- def my_view(request):
- if not request.user.is_authenticated():
- return HttpResponseRedirect('/login/?next=%s' % request.path)
- # ...
-
-...or display an error message::
-
- def my_view(request):
- if not request.user.is_authenticated():
- return render_to_response('myapp/login_error.html')
- # ...
-
-The login_required decorator
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. function:: decorators.login_required([redirect_field_name=REDIRECT_FIELD_NAME, login_url=None])
-
- As a shortcut, you can use the convenient
- :func:`~django.contrib.auth.decorators.login_required` decorator::
-
- from django.contrib.auth.decorators import login_required
-
- @login_required
- def my_view(request):
- ...
-
- :func:`~django.contrib.auth.decorators.login_required` does the following:
-
- * If the user isn't logged in, redirect to
- :setting:`settings.LOGIN_URL <LOGIN_URL>`, passing the current absolute
- path in the query string. Example: ``/accounts/login/?next=/polls/3/``.
-
- * If the user is logged in, execute the view normally. The view code is
- free to assume the user is logged in.
-
- By default, the path that the user should be redirected to upon
- successful authentication is stored in a query string parameter called
- ``"next"``. If you would prefer to use a different name for this parameter,
- :func:`~django.contrib.auth.decorators.login_required` takes an
- optional ``redirect_field_name`` parameter::
-
- from django.contrib.auth.decorators import login_required
-
- @login_required(redirect_field_name='my_redirect_field')
- def my_view(request):
- ...
-
- Note that if you provide a value to ``redirect_field_name``, you will most
- likely need to customize your login template as well, since the template
- context variable which stores the redirect path will use the value of
- ``redirect_field_name`` as its key rather than ``"next"`` (the default).
-
- :func:`~django.contrib.auth.decorators.login_required` also takes an
- optional ``login_url`` parameter. Example::
-
- from django.contrib.auth.decorators import login_required
-
- @login_required(login_url='/accounts/login/')
- def my_view(request):
- ...
-
- Note that if you don't specify the ``login_url`` parameter, you'll need to map
- the appropriate Django view to :setting:`settings.LOGIN_URL <LOGIN_URL>`. For
- example, using the defaults, add the following line to your URLconf::
-
- (r'^accounts/login/$', 'django.contrib.auth.views.login'),
-
- .. versionchanged:: 1.5
-
- As of version 1.5 :setting:`settings.LOGIN_URL <LOGIN_URL>` now also accepts
- view function names and :ref:`named URL patterns <naming-url-patterns>`.
- This allows you to freely remap your login view within your URLconf
- without having to update the setting.
-
-.. function:: views.login(request, [template_name, redirect_field_name, authentication_form])
-
- **URL name:** ``login``
-
- See :doc:`the URL documentation </topics/http/urls>` for details on using
- named URL patterns.
-
- Here's what ``django.contrib.auth.views.login`` does:
-
- * If called via ``GET``, it displays a login form that POSTs to the
- same URL. More on this in a bit.
-
- * If called via ``POST``, it tries to log the user in. If login is
- successful, the view redirects to the URL specified in ``next``. If
- ``next`` isn't provided, it redirects to
- :setting:`settings.LOGIN_REDIRECT_URL <LOGIN_REDIRECT_URL>` (which
- defaults to ``/accounts/profile/``). If login isn't successful, it
- redisplays the login form.
-
- It's your responsibility to provide the login form in a template called
- ``registration/login.html`` by default. This template gets passed four
- template context variables:
-
- * ``form``: A :class:`~django.forms.Form` object representing the login
- form. See the :doc:`forms documentation </topics/forms/index>` for
- more on ``Form`` objects.
-
- * ``next``: The URL to redirect to after successful login. This may
- contain a query string, too.
-
- * ``site``: The current :class:`~django.contrib.sites.models.Site`,
- according to the :setting:`SITE_ID` setting. If you don't have the
- site framework installed, this will be set to an instance of
- :class:`~django.contrib.sites.models.RequestSite`, which derives the
- site name and domain from the current
- :class:`~django.http.HttpRequest`.
-
- * ``site_name``: An alias for ``site.name``. If you don't have the site
- framework installed, this will be set to the value of
- :attr:`request.META['SERVER_NAME'] <django.http.HttpRequest.META>`.
- For more on sites, see :doc:`/ref/contrib/sites`.
-
- If you'd prefer not to call the template :file:`registration/login.html`,
- you can pass the ``template_name`` parameter via the extra arguments to
- the view in your URLconf. For example, this URLconf line would use
- :file:`myapp/login.html` instead::
-
- (r'^accounts/login/$', 'django.contrib.auth.views.login', {'template_name': 'myapp/login.html'}),
-
- You can also specify the name of the ``GET`` field which contains the URL
- to redirect to after login by passing ``redirect_field_name`` to the view.
- By default, the field is called ``next``.
-
- Here's a sample :file:`registration/login.html` template you can use as a
- starting point. It assumes you have a :file:`base.html` template that
- defines a ``content`` block:
-
- .. code-block:: html+django
-
- {% extends "base.html" %}
-
- {% block content %}
-
- {% if form.errors %}
- <p>Your username and password didn't match. Please try again.</p>
- {% endif %}
-
- <form method="post" action="{% url 'django.contrib.auth.views.login' %}">
- {% csrf_token %}
- <table>
- <tr>
- <td>{{ form.username.label_tag }}</td>
- <td>{{ form.username }}</td>
- </tr>
- <tr>
- <td>{{ form.password.label_tag }}</td>
- <td>{{ form.password }}</td>
- </tr>
- </table>
-
- <input type="submit" value="login" />
- <input type="hidden" name="next" value="{{ next }}" />
- </form>
-
- {% endblock %}
-
- 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
- accept a ``request`` keyword argument in its ``__init__`` method, and
- provide a ``get_user`` method which returns the authenticated user object
- (this method is only ever called after successful form validation).
-
- .. _forms documentation: ../forms/
- .. _site framework docs: ../sites/
-
- .. versionadded:: 1.4
-
- The :func:`~views.login` view and the :ref:`other-built-in-views` now all
- return a :class:`~django.template.response.TemplateResponse` instance,
- which allows you to easily customize the response data before rendering.
- For more details, see the
- :doc:`TemplateResponse documentation </ref/template-response>`.
-
-.. _other-built-in-views:
-
-Other built-in views
---------------------
-
-.. module:: django.contrib.auth.views
-
-In addition to the :func:`~views.login` view, the authentication system
-includes a few other useful built-in views located in
-:mod:`django.contrib.auth.views`:
-
-.. function:: logout(request, [next_page, template_name, redirect_field_name])
-
- Logs a user out.
-
- **URL name:** ``logout``
-
- See :doc:`the URL documentation </topics/http/urls>` for details on using
- named URL patterns.
-
- **Optional arguments:**
-
- * ``next_page``: The URL to redirect to after logout.
-
- * ``template_name``: The full name of a template to display after
- logging the user out. Defaults to
- :file:`registration/logged_out.html` if no argument is supplied.
-
- * ``redirect_field_name``: The name of a ``GET`` field containing the
- URL to redirect to after log out. Overrides ``next_page`` if the given
- ``GET`` parameter is passed.
-
- **Template context:**
-
- * ``title``: The string "Logged out", localized.
-
- * ``site``: The current :class:`~django.contrib.sites.models.Site`,
- according to the :setting:`SITE_ID` setting. If you don't have the
- site framework installed, this will be set to an instance of
- :class:`~django.contrib.sites.models.RequestSite`, which derives the
- site name and domain from the current
- :class:`~django.http.HttpRequest`.
-
- * ``site_name``: An alias for ``site.name``. If you don't have the site
- framework installed, this will be set to the value of
- :attr:`request.META['SERVER_NAME'] <django.http.HttpRequest.META>`.
- For more on sites, see :doc:`/ref/contrib/sites`.
-
-.. function:: logout_then_login(request[, login_url])
-
- Logs a user out, then redirects to the login page.
-
- **URL name:** No default URL provided
-
- **Optional arguments:**
-
- * ``login_url``: The URL of the login page to redirect to.
- Defaults to :setting:`settings.LOGIN_URL <LOGIN_URL>` if not supplied.
-
-.. function:: password_change(request[, template_name, post_change_redirect, password_change_form])
-
- Allows a user to change their password.
-
- **URL name:** ``password_change``
-
- **Optional arguments:**
-
- * ``template_name``: The full name of a template to use for
- displaying the password change form. Defaults to
- :file:`registration/password_change_form.html` if not supplied.
-
- * ``post_change_redirect``: The URL to redirect to after a successful
- password change.
-
- * ``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
- :class:`~django.contrib.auth.forms.PasswordChangeForm`.
-
- **Template context:**
-
- * ``form``: The password change form (see ``password_change_form`` above).
-
-.. function:: password_change_done(request[, template_name])
-
- The page shown after a user has changed their password.
-
- **URL name:** ``password_change_done``
-
- **Optional arguments:**
-
- * ``template_name``: The full name of a template to use.
- Defaults to :file:`registration/password_change_done.html` if not
- supplied.
-
-.. function:: password_reset(request[, is_admin_site, template_name, email_template_name, password_reset_form, token_generator, post_reset_redirect, from_email])
-
- Allows a user to reset their password by generating a one-time use link
- that can be used to reset the password, and sending that link to the
- user's registered email address.
-
- .. versionchanged:: 1.4
- Users flagged with an unusable password (see
- :meth:`~django.contrib.auth.models.User.set_unusable_password()`
- will not be able to request a password reset to prevent misuse
- when using an external authentication source like LDAP.
-
- **URL name:** ``password_reset``
-
- **Optional arguments:**
-
- * ``template_name``: The full name of a template to use for
- displaying the password reset form. Defaults to
- :file:`registration/password_reset_form.html` if not supplied.
-
- * ``email_template_name``: The full name of a template to use for
- generating the email with the reset password link. Defaults to
- :file:`registration/password_reset_email.html` if not supplied.
-
- * ``subject_template_name``: The full name of a template to use for
- the subject of the email with the reset password link. Defaults
- to :file:`registration/password_reset_subject.txt` if not supplied.
-
- .. versionadded:: 1.4
-
- * ``password_reset_form``: Form that will be used to get the email of
- the user to reset the password for. Defaults to
- :class:`~django.contrib.auth.forms.PasswordResetForm`.
-
- * ``token_generator``: Instance of the class to check the one time link.
- This will default to ``default_token_generator``, it's an instance of
- ``django.contrib.auth.tokens.PasswordResetTokenGenerator``.
-
- * ``post_reset_redirect``: The URL to redirect to after a successful
- password reset request.
-
- * ``from_email``: A valid email address. By default Django uses
- the :setting:`DEFAULT_FROM_EMAIL`.
-
- **Template context:**
-
- * ``form``: The form (see ``password_reset_form`` above) for resetting
- the user's password.
-
- **Email template context:**
-
- * ``email``: An alias for ``user.email``
-
- * ``user``: The current :class:`~django.contrib.auth.models.User`,
- according to the ``email`` form field. Only active users are able to
- reset their passwords (``User.is_active is True``).
-
- * ``site_name``: An alias for ``site.name``. If you don't have the site
- framework installed, this will be set to the value of
- :attr:`request.META['SERVER_NAME'] <django.http.HttpRequest.META>`.
- For more on sites, see :doc:`/ref/contrib/sites`.
-
- * ``domain``: An alias for ``site.domain``. If you don't have the site
- framework installed, this will be set to the value of
- ``request.get_host()``.
-
- * ``protocol``: http or https
-
- * ``uid``: The user's id encoded in base 36.
-
- * ``token``: Token to check that the reset link is valid.
-
- Sample ``registration/password_reset_email.html`` (email body template):
-
- .. code-block:: html+django
-
- Someone asked for password reset for email {{ email }}. Follow the link below:
- {{ protocol}}://{{ domain }}{% url 'password_reset_confirm' uidb36=uid token=token %}
-
- The same template context is used for subject template. Subject must be
- single line plain text string.
-
-
-.. function:: password_reset_done(request[, template_name])
-
- The page shown after a user has been emailed a link to reset their
- password. This view is called by default if the :func:`password_reset` view
- doesn't have an explicit ``post_reset_redirect`` URL set.
-
- **URL name:** ``password_reset_done``
-
- **Optional arguments:**
-
- * ``template_name``: The full name of a template to use.
- Defaults to :file:`registration/password_reset_done.html` if not
- supplied.
-
-.. function:: password_reset_confirm(request[, uidb36, token, template_name, token_generator, set_password_form, post_reset_redirect])
-
- Presents a form for entering a new password.
-
- **URL name:** ``password_reset_confirm``
-
- **Optional arguments:**
-
- * ``uidb36``: The user's id encoded in base 36. Defaults to ``None``.
-
- * ``token``: Token to check that the password is valid. Defaults to
- ``None``.
-
- * ``template_name``: The full name of a template to display the confirm
- password view. Default value is :file:`registration/password_reset_confirm.html`.
-
- * ``token_generator``: Instance of the class to check the password. This
- will default to ``default_token_generator``, it's an instance of
- ``django.contrib.auth.tokens.PasswordResetTokenGenerator``.
-
- * ``set_password_form``: Form that will be used to set the password.
- Defaults to :class:`~django.contrib.auth.forms.SetPasswordForm`
-
- * ``post_reset_redirect``: URL to redirect after the password reset
- done. Defaults to ``None``.
-
- **Template context:**
-
- * ``form``: The form (see ``set_password_form`` above) for setting the
- new user's password.
-
- * ``validlink``: Boolean, True if the link (combination of uidb36 and
- token) is valid or unused yet.
-
-.. function:: password_reset_complete(request[,template_name])
-
- Presents a view which informs the user that the password has been
- successfully changed.
-
- **URL name:** ``password_reset_complete``
-
- **Optional arguments:**
-
- * ``template_name``: The full name of a template to display the view.
- Defaults to :file:`registration/password_reset_complete.html`.
-
-Helper functions
-----------------
-
-.. currentmodule:: django.contrib.auth.views
-
-.. function:: redirect_to_login(next[, login_url, redirect_field_name])
-
- Redirects to the login page, and then back to another URL after a
- successful login.
-
- **Required arguments:**
-
- * ``next``: The URL to redirect to after a successful login.
-
- **Optional arguments:**
-
- * ``login_url``: The URL of the login page to redirect to.
- Defaults to :setting:`settings.LOGIN_URL <LOGIN_URL>` if not supplied.
-
- * ``redirect_field_name``: The name of a ``GET`` field containing the
- URL to redirect to after log out. Overrides ``next`` if the given
- ``GET`` parameter is passed.
-
-
-.. _built-in-auth-forms:
-
-Built-in forms
---------------
-
-.. module:: django.contrib.auth.forms
-
-If you don't want to use the built-in views, but want the convenience of not
-having to write forms for this functionality, the authentication system
-provides several built-in forms located in :mod:`django.contrib.auth.forms`:
-
-.. class:: AdminPasswordChangeForm
-
- A form used in the admin interface to change a user's password.
-
-.. class:: AuthenticationForm
-
- A form for logging a user in.
-
-.. class:: PasswordChangeForm
-
- A form for allowing a user to change their password.
-
-.. class:: PasswordResetForm
-
- A form for generating and emailing a one-time use link to reset a
- user's password.
-
-.. class:: SetPasswordForm
-
- A form that lets a user change his/her password without entering the old
- password.
-
-.. class:: UserChangeForm
-
- A form used in the admin interface to change a user's information and
- permissions.
-
-.. class:: UserCreationForm
-
- A form for creating a new user.
-
-Limiting access to logged-in users that pass a test
----------------------------------------------------
-
-.. currentmodule:: django.contrib.auth.decorators
-
-To limit access based on certain permissions or some other test, you'd do
-essentially the same thing as described in the previous section.
-
-The simple way is to run your test on :attr:`request.user
-<django.http.HttpRequest.user>` in the view directly. For example, this view
-checks to make sure the user is logged in and has the permission
-``polls.can_vote``::
-
- def my_view(request):
- if not request.user.has_perm('polls.can_vote'):
- return HttpResponse("You can't vote in this poll.")
- # ...
-
-.. function:: user_passes_test(func, [login_url=None])
-
- As a shortcut, you can use the convenient ``user_passes_test`` decorator::
-
- from django.contrib.auth.decorators import user_passes_test
-
- @user_passes_test(lambda u: u.has_perm('polls.can_vote'))
- def my_view(request):
- ...
-
- We're using this particular test as a relatively simple example. However,
- if you just want to test whether a permission is available to a user, you
- can use the :func:`~django.contrib.auth.decorators.permission_required()`
- decorator, described later in this document.
-
- :func:`~django.contrib.auth.decorators.user_passes_test` takes a required
- argument: a callable that takes a
- :class:`~django.contrib.auth.models.User` object and returns ``True`` if
- the user is allowed to view the page. Note that
- :func:`~django.contrib.auth.decorators.user_passes_test` does not
- automatically check that the :class:`~django.contrib.auth.models.User` is
- not anonymous.
-
- :func:`~django.contrib.auth.decorators.user_passes_test()` takes an
- optional ``login_url`` argument, which lets you specify the URL for your
- login page (:setting:`settings.LOGIN_URL <LOGIN_URL>` by default).
-
- For example::
-
- from django.contrib.auth.decorators import user_passes_test
-
- @user_passes_test(lambda u: u.has_perm('polls.can_vote'), login_url='/login/')
- def my_view(request):
- ...
-
-The permission_required decorator
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. function:: permission_required([login_url=None, raise_exception=False])
-
- It's a relatively common task to check whether a user has a particular
- permission. For that reason, Django provides a shortcut for that case: the
- :func:`~django.contrib.auth.decorators.permission_required()` decorator.
- Using this decorator, the earlier example can be written as::
-
- from django.contrib.auth.decorators import permission_required
-
- @permission_required('polls.can_vote')
- def my_view(request):
- ...
-
- As for the :meth:`User.has_perm` method, permission names take the form
- ``"<app label>.<permission codename>"`` (i.e. ``polls.can_vote`` for a
- permission on a model in the ``polls`` application).
-
- Note that :func:`~django.contrib.auth.decorators.permission_required()`
- also takes an optional ``login_url`` parameter. Example::
-
- from django.contrib.auth.decorators import permission_required
-
- @permission_required('polls.can_vote', login_url='/loginpage/')
- def my_view(request):
- ...
-
- As in the :func:`~decorators.login_required` decorator, ``login_url``
- defaults to :setting:`settings.LOGIN_URL <LOGIN_URL>`.
-
- .. versionchanged:: 1.4
-
- Added ``raise_exception`` parameter. If given, the decorator will raise
- :exc:`~django.core.exceptions.PermissionDenied`, prompting
- :ref:`the 403 (HTTP Forbidden) view<http_forbidden_view>` instead of
- redirecting to the login page.
-
-.. currentmodule:: django.contrib.auth
-
-Applying permissions to generic views
--------------------------------------
-
-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:
-
-Permissions
-===========
-
-Django comes with a simple permissions system. It provides a way to assign
-permissions to specific users and groups of users.
-
-It's used by the Django admin site, but you're welcome to use it in your own
-code.
-
-The Django admin site uses permissions as follows:
-
-* Access to view the "add" form and add an object is limited to users with
- the "add" permission for that type of object.
-* Access to view the change list, view the "change" form and change an
- object is limited to users with the "change" permission for that type of
- object.
-* Access to delete an object is limited to users with the "delete"
- permission for that type of object.
-
-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
--------------------
-
-When ``django.contrib.auth`` is listed in your :setting:`INSTALLED_APPS`
-setting, it will ensure that three default permissions -- add, change and
-delete -- are created for each Django model defined in one of your installed
-applications.
-
-These permissions will be created when you run :djadmin:`manage.py syncdb
-<syncdb>`; the first time you run ``syncdb`` after adding
-``django.contrib.auth`` to :setting:`INSTALLED_APPS`, the default permissions
-will be created for all previously-installed models, as well as for any new
-models being installed at that time. Afterward, it will create default
-permissions for new models each time you run :djadmin:`manage.py syncdb
-<syncdb>`.
-
-Assuming you have an application with an
-:attr:`~django.db.models.Options.app_label` ``foo`` and a model named ``Bar``,
-to test for basic permissions you should use:
-
-* add: ``user.has_perm('foo.add_bar')``
-* change: ``user.has_perm('foo.change_bar')``
-* delete: ``user.has_perm('foo.delete_bar')``
-
-.. _custom-permissions:
-
-Custom permissions
-------------------
-
-To create custom permissions for a given model object, use the ``permissions``
-:ref:`model Meta attribute <meta-options>`.
-
-This example Task model creates three custom permissions, i.e., actions users
-can or cannot do with Task instances, specific to your application::
-
- class Task(models.Model):
- ...
- class Meta:
- permissions = (
- ("view_task", "Can see available tasks"),
- ("change_task_status", "Can change the status of tasks"),
- ("close_task", "Can remove a task by setting its status as closed"),
- )
-
-The only thing this does is create those extra permissions when you run
-:djadmin:`manage.py syncdb <syncdb>`. Your code is in charge of checking the
-value of these permissions when an user is trying to access the functionality
-provided by the application (viewing tasks, changing the status of tasks,
-closing tasks.) Continuing the above example, the following checks if a user may
-view tasks::
-
- user.has_perm('app.view_task')
-
-API reference
--------------
-
-.. currentmodule:: django.contrib.auth.models
-
-.. class:: models.Permission
-
-Fields
-~~~~~~
-
-:class:`~django.contrib.auth.models.Permission` objects have the following
-fields:
-
-.. attribute:: Permission.name
-
- Required. 50 characters or fewer. Example: ``'Can vote'``.
-
-.. attribute:: Permission.content_type
-
- Required. A reference to the ``django_content_type`` database table, which
- contains a record for each installed Django model.
-
-.. attribute:: Permission.codename
-
- Required. 100 characters or fewer. Example: ``'can_vote'``.
-
-Methods
-~~~~~~~
-
-:class:`~django.contrib.auth.models.Permission` objects have the standard
-data-access methods like any other :doc:`Django model </ref/models/instances>`.
-
-.. currentmodule:: django.contrib.auth
-
-Programmatically creating permissions
--------------------------------------
-
-While custom permissions can be defined within a model's ``Meta`` class, you
-can also create permissions directly. For example, you can create the
-``can_publish`` permission for a ``BlogPost`` model in ``myapp``::
-
- from django.contrib.auth.models import Group, Permission
- from django.contrib.contenttypes.models import ContentType
-
- content_type = ContentType.objects.get(app_label='myapp', model='BlogPost')
- permission = Permission.objects.create(codename='can_publish',
- name='Can Publish Posts',
- content_type=content_type)
-
-The permission can then be assigned to a
-:class:`~django.contrib.auth.models.User` via its ``user_permissions``
-attribute or to a :class:`~django.contrib.auth.models.Group` via its
-``permissions`` attribute.
-
-Authentication data in templates
-================================
-
-The currently logged-in user and his/her permissions are made available in the
-:doc:`template context </ref/templates/api>` when you use
-:class:`~django.template.context.RequestContext`.
-
-.. admonition:: Technicality
-
- Technically, these variables are only made available in the template context
- if you use :class:`~django.template.context.RequestContext` *and* your
- :setting:`TEMPLATE_CONTEXT_PROCESSORS` setting contains
- ``"django.contrib.auth.context_processors.auth"``, which is default. For
- more, see the :ref:`RequestContext docs <subclassing-context-requestcontext>`.
-
-Users
------
-
-When rendering a template :class:`~django.template.context.RequestContext`, the
-currently logged-in user, either a :class:`~django.contrib.auth.models.User`
-instance or an :class:`~django.contrib.auth.models.AnonymousUser` instance, is
-stored in the template variable ``{{ user }}``:
-
-.. code-block:: html+django
-
- {% if user.is_authenticated %}
- <p>Welcome, {{ user.username }}. Thanks for logging in.</p>
- {% else %}
- <p>Welcome, new user. Please log in.</p>
- {% endif %}
-
-This template context variable is not available if a ``RequestContext`` is not
-being used.
-
-Permissions
------------
-
-The currently logged-in user's permissions are stored in the template variable
-``{{ perms }}``. This is an instance of
-:class:`django.contrib.auth.context_processors.PermWrapper`, which is a
-template-friendly proxy of permissions.
-
-In the ``{{ perms }}`` object, single-attribute lookup is a proxy to
-:meth:`User.has_module_perms <django.contrib.auth.models.User.has_module_perms>`.
-This example would display ``True`` if the logged-in user had any permissions
-in the ``foo`` app::
-
- {{ perms.foo }}
-
-Two-level-attribute lookup is a proxy to
-:meth:`User.has_perm <django.contrib.auth.models.User.has_perm>`. This example
-would display ``True`` if the logged-in user had the permission
-``foo.can_vote``::
-
- {{ perms.foo.can_vote }}
-
-Thus, you can check permissions in template ``{% if %}`` statements:
-
-.. code-block:: html+django
-
- {% if perms.foo %}
- <p>You have permission to do something in the foo app.</p>
- {% if perms.foo.can_vote %}
- <p>You can vote!</p>
- {% endif %}
- {% if perms.foo.can_drive %}
- <p>You can drive!</p>
- {% endif %}
- {% else %}
- <p>You don't have permission to do anything in the foo app.</p>
- {% endif %}
-
-.. versionadded:: 1.5
- Permission lookup by "if in".
-
-It is possible to also look permissions up by ``{% if in %}`` statements.
-For example:
-
-.. code-block:: html+django
-
- {% if 'foo' in perms %}
- {% if 'foo.can_vote' in perms %}
- <p>In lookup works, too.</p>
- {% endif %}
- {% endif %}
-
-Groups
-======
-
-Groups are a generic way of categorizing users so you can apply permissions, or
-some other label, to those users. A user can belong to any number of groups.
-
-A user in a group automatically has the permissions granted to that group. For
-example, if the group ``Site editors`` has the permission
-``can_edit_home_page``, any user in that group will have that permission.
-
-Beyond permissions, groups are a convenient way to categorize users to give
-them some label, or extended functionality. For example, you could create a
-group ``'Special users'``, and you could write code that could, say, give them
-access to a members-only portion of your site, or send them members-only email
-messages.
-
-API reference
--------------
-
-.. class:: models.Group
-
-Fields
-~~~~~~
-
-:class:`~django.contrib.auth.models.Group` objects have the following fields:
-
-.. attribute:: Group.name
-
- Required. 80 characters or fewer. Any characters are permitted. Example:
- ``'Awesome Users'``.
-
-.. attribute:: Group.permissions
-
- Many-to-many field to :class:`~django.contrib.auth.models.Permissions`::
-
- group.permissions = [permission_list]
- group.permissions.add(permission, permission, ...)
- group.permissions.remove(permission, permission, ...)
- group.permissions.clear()
-
-.. _auth-custom-user:
-
-Customizing the User model
-==========================
-
-.. versionadded:: 1.5
-
-Some kinds of projects may have authentication requirements for which Django's
-built-in :class:`~django.contrib.auth.models.User` model is not always
-appropriate. For instance, on some sites it makes more sense to use an email
-address as your identification token instead of a username.
-
-Django allows you to override the default User model by providing a value for
-the :setting:`AUTH_USER_MODEL` setting that references a custom model::
-
- AUTH_USER_MODEL = 'myapp.MyUser'
-
-This dotted pair describes the name of the Django app, and the name of the Django
-model that you wish to use as your User model.
-
-.. admonition:: Warning
-
- Changing :setting:`AUTH_USER_MODEL` has a big effect on your database
- structure. It changes the tables that are available, and it will affect the
- construction of foreign keys and many-to-many relationships. If you intend
- to set :setting:`AUTH_USER_MODEL`, you should set it before running
- ``manage.py syncdb`` for the first time.
-
- If you have an existing project and you want to migrate to using a custom
- User model, you may need to look into using a migration tool like South_
- to ease the transition.
-
-.. _South: http://south.aeracode.org
-
-Referencing the User model
---------------------------
-
-If you reference :class:`~django.contrib.auth.models.User` directly (for
-example, by referring to it in a foreign key), your code will not work in
-projects where the :setting:`AUTH_USER_MODEL` setting has been changed to a
-different User model.
-
-Instead of referring to :class:`~django.contrib.auth.models.User` directly,
-you should reference the user model using
-:func:`django.contrib.auth.get_user_model()`. This method will return the
-currently active User model -- the custom User model if one is specified, or
-:class:`~django.contrib.auth.User` otherwise.
-
-When you define a foreign key or many-to-many relations to the User model,
-you should specify the custom model using the :setting:`AUTH_USER_MODEL`
-setting. For example::
-
- from django.conf import settings
- from django.db import models
-
- class Article(models.Model)
- author = models.ForeignKey(settings.AUTH_USER_MODEL)
-
-Specifying a custom User model
-------------------------------
-
-.. admonition:: Model design considerations
-
- Think carefully before handling information not directly related to
- authentication in your custom User Model.
-
- It may be better to store app-specific user information in a model
- that has a relation with the User model. That allows each app to specify
- its own user data requirements without risking conflicts with other
- apps. On the other hand, queries to retrieve this related information
- will involve a database join, which may have an effect on performance.
-
-Django expects your custom User model to meet some minimum requirements.
-
-1. Your model must have a single unique field that can be used for
- identification purposes. This can be a username, an email address,
- or any other unique attribute.
-
-2. Your model must provide a way to address the user in a "short" and
- "long" form. The most common interpretation of this would be to use
- the user's given name as the "short" identifier, and the user's full
- name as the "long" identifier. However, there are no constraints on
- what these two methods return - if you want, they can return exactly
- the same value.
-
-The easiest way to construct a compliant custom User model is to inherit from
-:class:`~django.contrib.auth.models.AbstractBaseUser`.
-:class:`~django.contrib.auth.models.AbstractBaseUser` provides the core
-implementation of a `User` model, including hashed passwords and tokenized
-password resets. You must then provide some key implementation details:
-
-.. class:: models.CustomUser
-
- .. attribute:: User.USERNAME_FIELD
-
- A string describing the name of the field on the User model that is
- used as the unique identifier. This will usually be a username of
- some kind, but it can also be an email address, or any other unique
- identifier. In the following example, the field `identifier` is used
- as the identifying field::
-
- class MyUser(AbstractBaseUser):
- identifier = models.CharField(max_length=40, unique=True, db_index=True)
- ...
- USERNAME_FIELD = 'identifier'
-
- .. attribute:: User.REQUIRED_FIELDS
-
- A list of the field names that *must* be provided when creating
- a user. For example, here is the partial definition for a User model
- that defines two required fields - a date of birth and height::
-
- class MyUser(AbstractBaseUser):
- ...
- date_of_birth = models.DateField()
- height = models.FloatField()
- ...
- REQUIRED_FIELDS = ['date_of_birth', 'height']
-
- .. note::
-
- ``REQUIRED_FIELDS`` must contain all required fields on your User
- model, but should *not* contain the ``USERNAME_FIELD``.
-
- .. attribute:: User.is_active
-
- A boolean attribute that indicates whether the user is considered
- "active". This attribute is provided as an attribute on
- ``AbstractBaseUser`` defaulting to ``True``. How you choose to
- implement it will depend on the details of your chosen auth backends.
- See the documentation of the :attr:`attribute on the builtin user model
- <django.contrib.auth.models.User.is_active>` for details.
-
- .. method:: User.get_full_name():
-
- A longer formal identifier for the user. A common interpretation
- would be the full name name of the user, but it can be any string that
- identifies the user.
-
- .. method:: User.get_short_name():
-
- A short, informal identifier for the user. A common interpretation
- would be the first name of the user, but it can be any string that
- identifies the user in an informal way. It may also return the same
- value as :meth:`django.contrib.auth.User.get_full_name()`.
-
-The following methods are available on any subclass of
-:class:`~django.contrib.auth.models.AbstractBaseUser`:
-
-.. class:: models.AbstractBaseUser
-
- .. method:: models.AbstractBaseUser.get_username()
-
- Returns the value of the field nominated by ``USERNAME_FIELD``.
-
- .. method:: models.AbstractBaseUser.is_anonymous()
-
- Always returns ``False``. This is a way of differentiating
- from :class:`~django.contrib.auth.models.AnonymousUser` objects.
- Generally, you should prefer using
- :meth:`~django.contrib.auth.models.AbstractBaseUser.is_authenticated()` to this
- method.
-
- .. method:: models.AbstractBaseUser.is_authenticated()
-
- Always returns ``True``. This is a way to tell if the user has been
- authenticated. This does not imply any permissions, and doesn't check
- if the user is active - it only indicates that the user has provided a
- valid username and password.
-
- .. method:: models.AbstractBaseUser.set_password(raw_password)
-
- Sets the user's password to the given raw string, taking care of the
- password hashing. Doesn't save the
- :class:`~django.contrib.auth.models.AbstractBaseUser` object.
-
- .. method:: models.AbstractBaseUser.check_password(raw_password)
-
- Returns ``True`` if the given raw string is the correct password for
- the user. (This takes care of the password hashing in making the
- comparison.)
-
- .. method:: models.AbstractBaseUser.set_unusable_password()
-
- Marks the user as having no password set. This isn't the same as
- having a blank string for a password.
- :meth:`~django.contrib.auth.models.AbstractBaseUser.check_password()` for this user
- will never return ``True``. Doesn't save the
- :class:`~django.contrib.auth.models.AbstractBaseUser` object.
-
- You may need this if authentication for your application takes place
- against an existing external source such as an LDAP directory.
-
- .. method:: models.AbstractBaseUser.has_usable_password()
-
- Returns ``False`` if
- :meth:`~django.contrib.auth.models.AbstractBaseUser.set_unusable_password()` has
- been called for this user.
-
-
-You should also define a custom manager for your User model. If your User
-model defines `username` and `email` fields the same as Django's default User,
-you can just install Django's
-:class:`~django.contrib.auth.models.UserManager`; however, if your User model
-defines different fields, you will need to define a custom manager that
-extends :class:`~django.contrib.auth.models.BaseUserManager` providing two
-additional methods:
-
-.. class:: models.CustomUserManager
-
- .. method:: models.CustomUserManager.create_user(*username_field*, password=None, **other_fields)
-
- The prototype of `create_user()` should accept the username field,
- plus all required fields as arguments. For example, if your user model
- uses `email` as the username field, and has `date_of_birth` as a required
- fields, then create_user should be defined as::
-
- def create_user(self, email, date_of_birth, password=None):
- # create user here
-
- .. method:: models.CustomUserManager.create_superuser(*username_field*, password, **other_fields)
-
- The prototype of `create_superuser()` should accept the username field,
- plus all required fields as arguments. For example, if your user model
- uses `email` as the username field, and has `date_of_birth` as a required
- fields, then create_superuser should be defined as::
-
- def create_superuser(self, email, date_of_birth, password):
- # create superuser here
-
- Unlike `create_user()`, `create_superuser()` *must* require the caller
- to provider a password.
-
-:class:`~django.contrib.auth.models.BaseUserManager` provides the following
-utility methods:
-
-.. class:: models.BaseUserManager
-
- .. method:: models.BaseUserManager.normalize_email(email)
-
- A classmethod that normalizes email addresses by lowercasing
- the domain portion of the email address.
-
- .. method:: models.BaseUserManager.get_by_natural_key(username)
-
- Retrieves a user instance using the contents of the field
- nominated by ``USERNAME_FIELD``.
-
- .. method:: models.BaseUserManager.make_random_password(length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789')
-
- Returns a random password with the given length and given string of
- allowed characters. (Note that the default value of ``allowed_chars``
- doesn't contain letters that can cause user confusion, including:
-
- * ``i``, ``l``, ``I``, and ``1`` (lowercase letter i, lowercase
- letter L, uppercase letter i, and the number one)
- * ``o``, ``O``, and ``0`` (uppercase letter o, lowercase letter o,
- and zero)
-
-Extending Django's default User
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-If you're entirely happy with Django's :class:`~django.contrib.auth.models.User`
-model and you just want to add some additional profile information, you can
-simply subclass :class:`~django.contrib.auth.models.AbstractUser` and add your
-custom profile fields.
-
-Custom users and the built-in auth forms
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-As you may expect, built-in Django's :ref:`forms <built-in-auth-forms>`
-and :ref:`views <other-built-in-views>` make certain assumptions about
-the user model that they are working with.
-
-If your user model doesn't follow the same assumptions, it may be necessary to define
-a replacement form, and pass that form in as part of the configuration of the
-auth views.
-
-* :class:`~django.contrib.auth.forms.UserCreationForm`
-
- Depends on the :class:`~django.contrib.auth.models.User` model.
- Must be re-written for any custom user model.
-
-* :class:`~django.contrib.auth.forms.UserChangeForm`
-
- Depends on the :class:`~django.contrib.auth.models.User` model.
- Must be re-written for any custom user model.
-
-* :class:`~django.contrib.auth.forms.AuthenticationForm`
-
- Works with any subclass of :class:`~django.contrib.auth.models.AbstractBaseUser`,
- and will adapt to use the field defined in `USERNAME_FIELD`.
-
-* :class:`~django.contrib.auth.forms.PasswordResetForm`
-
- Assumes that the user model has an integer primary key, has a field named
- `email` that can be used to identify the user, and a boolean field
- named `is_active` to prevent password resets for inactive users.
-
-* :class:`~django.contrib.auth.forms.SetPasswordForm`
-
- Works with any subclass of :class:`~django.contrib.auth.models.AbstractBaseUser`
-
-* :class:`~django.contrib.auth.forms.PasswordChangeForm`
-
- Works with any subclass of :class:`~django.contrib.auth.models.AbstractBaseUser`
-
-* :class:`~django.contrib.auth.forms.AdminPasswordChangeForm`
-
- Works with any subclass of :class:`~django.contrib.auth.models.AbstractBaseUser`
-
-
-Custom users and django.contrib.admin
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-If you want your custom User model to also work with Admin, your User model must
-define some additional attributes and methods. These methods allow the admin to
-control access of the User to admin content:
-
-.. attribute:: User.is_staff
-
- Returns True if the user is allowed to have access to the admin site.
-
-.. attribute:: User.is_active
-
- Returns True if the user account is currently active.
-
-.. method:: User.has_perm(perm, obj=None):
-
- Returns True if the user has the named permission. If `obj` is
- provided, the permission needs to be checked against a specific object
- instance.
-
-.. method:: User.has_module_perms(app_label):
-
- Returns True if the user has permission to access models in
- the given app.
-
-You will also need to register your custom User model with the admin. If
-your custom User model extends :class:`~django.contrib.auth.models.AbstractUser`,
-you can use Django's existing :class:`~django.contrib.auth.admin.UserAdmin`
-class. However, if your User model extends
-:class:`~django.contrib.auth.models.AbstractBaseUser`, you'll need to define
-a custom ModelAdmin class. It may be possible to subclass the default
-:class:`~django.contrib.auth.admin.UserAdmin`; however, you'll need to
-override any of the definitions that refer to fields on
-:class:`~django.contrib.auth.models.AbstractUser` that aren't on your
-custom User class.
-
-Custom users and permissions
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-To make it easy to include Django's permission framework into your own User
-class, Django provides :class:`~django.contrib.auth.model.PermissionsMixin`.
-This is an abstract model you can include in the class heirarchy for your User
-model, giving you all the methods and database fields necessary to support
-Django's permission model.
-
-:class:`~django.contrib.auth.model.PermissionsMixin` provides the following
-methods and attributes:
-
-.. class:: models.PermissionsMixin
-
- .. attribute:: models.PermissionsMixin.is_superuser
-
- Boolean. Designates that this user has all permissions without
- explicitly assigning them.
-
- .. method:: models.PermissionsMixin.get_group_permissions(obj=None)
-
- Returns a set of permission strings that the user has, through his/her
- groups.
-
- If ``obj`` is passed in, only returns the group permissions for
- this specific object.
-
- .. method:: models.PermissionsMixin.get_all_permissions(obj=None)
-
- Returns a set of permission strings that the user has, both through
- group and user permissions.
-
- If ``obj`` is passed in, only returns the permissions for this
- specific object.
-
- .. method:: models.PermissionsMixin.has_perm(perm, obj=None)
-
- Returns ``True`` if the user has the specified permission, where perm is
- in the format ``"<app label>.<permission codename>"`` (see
- `permissions`_). If the user is inactive, this method will
- always return ``False``.
-
- If ``obj`` is passed in, this method won't check for a permission for
- the model, but for this specific object.
-
- .. method:: models.PermissionsMixin.has_perms(perm_list, obj=None)
-
- Returns ``True`` if the user has each of the specified permissions,
- where each perm is in the format
- ``"<app label>.<permission codename>"``. If the user is inactive,
- this method will always return ``False``.
-
- If ``obj`` is passed in, this method won't check for permissions for
- the model, but for the specific object.
-
- .. method:: models.PermissionsMixin.has_module_perms(package_name)
-
- Returns ``True`` if the user has any permissions in the given package
- (the Django app label). If the user is inactive, this method will
- always return ``False``.
-
-.. admonition:: ModelBackend
-
- If you don't include the
- :class:`~django.contrib.auth.model.PermissionsMixin`, you must ensure you
- don't invoke the permissions methods on ``ModelBackend``. ``ModelBackend``
- assumes that certain fields are available on your user model. If your User
- model doesn't provide those fields, you will receive database errors when
- you check permissions.
-
-Custom users and Proxy models
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-One limitation of custom User models is that installing a custom User model
-will break any proxy model extending :class:`~django.contrib.auth.models.User`.
-Proxy models must be based on a concrete base class; by defining a custom User
-model, you remove the ability of Django to reliably identify the base class.
-
-If your project uses proxy models, you must either modify the proxy to extend
-the User model that is currently in use in your project, or merge your proxy's
-behavior into your User subclass.
-
-Custom users and signals
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-Another limitation of custom User models is that you can't use
-:func:`django.contrib.auth.get_user_model()` as the sender or target of a signal
-handler. Instead, you must register the handler with the actual User model.
-
-Custom users and testing/fixtures
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-If you are writing an application that interacts with the User model, you must
-take some precautions to ensure that your test suite will run regardless of
-the User model that is being used by a project. Any test that instantiates an
-instance of User will fail if the User model has been swapped out. This
-includes any attempt to create an instance of User with a fixture.
-
-To ensure that your test suite will pass in any project configuration,
-``django.contrib.auth.tests.utils`` defines a ``@skipIfCustomUser`` decorator.
-This decorator will cause a test case to be skipped if any User model other
-than the default Django user is in use. This decorator can be applied to a
-single test, or to an entire test class.
-
-Depending on your application, tests may also be needed to be added to ensure
-that the application works with *any* user model, not just the default User
-model. To assist with this, Django provides two substitute user models that
-can be used in test suites:
-
-* :class:`django.contrib.auth.tests.custom_user.CustomUser`, a custom user
- model that uses an ``email`` field as the username, and has a basic
- admin-compliant permissions setup
-
-* :class:`django.contrib.auth.tests.custom_user.ExtensionUser`, a custom
- user model that extends :class:`~django.contrib.auth.models.AbstractUser`,
- adding a ``date_of_birth`` field.
-
-You can then use the ``@override_settings`` decorator to make that test run
-with the custom User model. For example, here is a skeleton for a test that
-would test three possible User models -- the default, plus the two User
-models provided by ``auth`` app::
-
- from django.contrib.auth.tests.utils import skipIfCustomUser
- from django.test import TestCase
- from django.test.utils import override_settings
-
-
- class ApplicationTestCase(TestCase):
- @skipIfCustomUser
- def test_normal_user(self):
- "Run tests for the normal user model"
- self.assertSomething()
-
- @override_settings(AUTH_USER_MODEL='auth.CustomUser')
- def test_custom_user(self):
- "Run tests for a custom user model with email-based authentication"
- self.assertSomething()
-
- @override_settings(AUTH_USER_MODEL='auth.ExtensionUser')
- def test_extension_user(self):
- "Run tests for a simple extension of the built-in User."
- self.assertSomething()
-
-
-A full example
---------------
-
-Here is an example of an admin-compliant custom user app. This user model uses
-an email address as the username, and has a required date of birth; it
-provides no permission checking, beyond a simple `admin` flag on the user
-account. This model would be compatible with all the built-in auth forms and
-views, except for the User creation forms.
-
-This code would all live in a ``models.py`` file for a custom
-authentication app::
-
- from django.db import models
- from django.contrib.auth.models import (
- BaseUserManager, AbstractBaseUser
- )
-
-
- class MyUserManager(BaseUserManager):
- def create_user(self, email, date_of_birth, password=None):
- """
- Creates and saves a User with the given email, date of
- birth and password.
- """
- if not email:
- raise ValueError('Users must have an email address')
-
- user = self.model(
- email=MyUserManager.normalize_email(email),
- date_of_birth=date_of_birth,
- )
-
- user.set_password(password)
- user.save(using=self._db)
- return user
-
- def create_superuser(self, email, date_of_birth, password):
- """
- Creates and saves a superuser with the given email, date of
- birth and password.
- """
- user = self.create_user(email,
- password=password,
- date_of_birth=date_of_birth
- )
- user.is_admin = True
- user.save(using=self._db)
- return user
-
-
- class MyUser(AbstractBaseUser):
- email = models.EmailField(
- verbose_name='email address',
- max_length=255,
- unique=True,
- db_index=True,
- )
- date_of_birth = models.DateField()
- is_active = models.BooleanField(default=True)
- is_admin = models.BooleanField(default=False)
-
- objects = MyUserManager()
-
- USERNAME_FIELD = 'email'
- REQUIRED_FIELDS = ['date_of_birth']
-
- def get_full_name(self):
- # The user is identified by their email address
- return self.email
-
- def get_short_name(self):
- # The user is identified by their email address
- return self.email
-
- def __unicode__(self):
- return self.email
-
- def has_perm(self, perm, obj=None):
- "Does the user have a specific permission?"
- # Simplest possible answer: Yes, always
- return True
-
- def has_module_perms(self, app_label):
- "Does the user have permissions to view the app `app_label`?"
- # Simplest possible answer: Yes, always
- return True
-
- @property
- def is_staff(self):
- "Is the user a member of staff?"
- # Simplest possible answer: All admins are staff
- return self.is_admin
-
-Then, to register this custom User model with Django's admin, the following
-code would be required in the app's ``admin.py`` file::
-
- from django import forms
- from django.contrib import admin
- from django.contrib.auth.models import Group
- from django.contrib.auth.admin import UserAdmin
- from django.contrib.auth.forms import ReadOnlyPasswordHashField
-
- from customauth.models import MyUser
-
-
- class UserCreationForm(forms.ModelForm):
- """A form for creating new users. Includes all the required
- fields, plus a repeated password."""
- password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
- password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
-
- class Meta:
- model = MyUser
- fields = ('email', 'date_of_birth')
-
- def clean_password2(self):
- # Check that the two password entries match
- password1 = self.cleaned_data.get("password1")
- password2 = self.cleaned_data.get("password2")
- if password1 and password2 and password1 != password2:
- raise forms.ValidationError("Passwords don't match")
- return password2
-
- def save(self, commit=True):
- # Save the provided password in hashed format
- user = super(UserCreationForm, self).save(commit=False)
- user.set_password(self.cleaned_data["password1"])
- if commit:
- user.save()
- return user
-
-
- class UserChangeForm(forms.ModelForm):
- """A form for updating users. Includes all the fields on
- the user, but replaces the password field with admin's
- password hash display field.
- """
- password = ReadOnlyPasswordHashField()
-
- class Meta:
- model = MyUser
-
- def clean_password(self):
- # Regardless of what the user provides, return the initial value.
- # This is done here, rather than on the field, because the
- # field does not have access to the initial value
- return self.initial["password"]
-
-
- class MyUserAdmin(UserAdmin):
- # The forms to add and change user instances
- form = UserChangeForm
- add_form = UserCreationForm
-
- # The fields to be used in displaying the User model.
- # These override the definitions on the base UserAdmin
- # that reference specific fields on auth.User.
- list_display = ('email', 'date_of_birth', 'is_admin')
- list_filter = ('is_admin',)
- fieldsets = (
- (None, {'fields': ('email', 'password')}),
- ('Personal info', {'fields': ('date_of_birth',)}),
- ('Permissions', {'fields': ('is_admin',)}),
- ('Important dates', {'fields': ('last_login',)}),
- )
- add_fieldsets = (
- (None, {
- 'classes': ('wide',),
- 'fields': ('email', 'date_of_birth', 'password1', 'password2')}
- ),
- )
- search_fields = ('email',)
- ordering = ('email',)
- filter_horizontal = ()
-
- # Now register the new UserAdmin...
- admin.site.register(MyUser, MyUserAdmin)
- # ... and, since we're not using Django's builtin permissions,
- # unregister the Group model from admin.
- admin.site.unregister(Group)
-
-.. _authentication-backends:
-
-Other authentication sources
-============================
-
-The authentication that comes with Django is good enough for most common cases,
-but you may have the need to hook into another authentication source -- that
-is, another source of usernames and passwords or authentication methods.
-
-For example, your company may already have an LDAP setup that stores a username
-and password for every employee. It'd be a hassle for both the network
-administrator and the users themselves if users had separate accounts in LDAP
-and the Django-based applications.
-
-So, to handle situations like this, the Django authentication system lets you
-plug in other authentication sources. You can override Django's default
-database-based scheme, or you can use the default system in tandem with other
-systems.
-
-See the :doc:`authentication backend reference </ref/authbackends>`
-for information on the authentication backends included with Django.
-
-Specifying authentication backends
-----------------------------------
-
-Behind the scenes, Django maintains a list of "authentication backends" that it
-checks for authentication. When somebody calls
-:func:`django.contrib.auth.authenticate()` -- as described in :ref:`How to log
-a user in <how-to-log-a-user-in>` above -- Django tries authenticating across
-all of its authentication backends. If the first authentication method fails,
-Django tries the second one, and so on, until all backends have been attempted.
-
-The list of authentication backends to use is specified in the
-:setting:`AUTHENTICATION_BACKENDS` setting. This should be a tuple of Python
-path names that point to Python classes that know how to authenticate. These
-classes can be anywhere on your Python path.
-
-By default, :setting:`AUTHENTICATION_BACKENDS` is set to::
-
- ('django.contrib.auth.backends.ModelBackend',)
-
-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
-processing at the first positive match.
-
-.. note::
-
- Once a user has authenticated, Django stores which backend was used to
- authenticate the user in the user's session, and re-uses the same backend
- for the duration of that session whenever access to the currently
- authenticated user is needed. This effectively means that authentication
- sources are cached on a per-session basis, so if you change
- :setting:`AUTHENTICATION_BACKENDS`, you'll need to clear out session data if
- you need to force users to re-authenticate using different methods. A simple
- way to do that is simply to execute ``Session.objects.all().delete()``.
-
-.. versionadded:: 1.6
-
-If a backend raises a :class:`~django.core.exceptions.PermissionDenied`
-exception, authentication will immediately fail. Django won't check the
-backends that follow.
-
-Writing an authentication backend
----------------------------------
-
-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.
-
-The ``authenticate`` method takes credentials as keyword arguments. Most of
-the time, it'll just look like this::
-
- class MyBackend(object):
- def authenticate(self, username=None, password=None):
- # Check the username/password and return a User.
-
-But it could also authenticate a token, like so::
-
- class MyBackend(object):
- def authenticate(self, token=None):
- # Check the token and return a User.
-
-Either way, ``authenticate`` should check the credentials it gets, and it
-should return a ``User`` object that matches those credentials, if the
-credentials are valid. If they're not valid, it should return ``None``.
-
-The Django admin system is tightly coupled to the Django ``User`` object
-described at the beginning of this document. For now, the best way to deal with
-this is to create a Django ``User`` object for each user that exists for your
-backend (e.g., in your LDAP directory, your external SQL database, etc.) You
-can either write a script to do this in advance, or your ``authenticate``
-method can do it the first time a user logs in.
-
-Here's an example backend that authenticates against a username and password
-variable defined in your ``settings.py`` file and creates a Django ``User``
-object the first time a user authenticates::
-
- from django.conf import settings
- from django.contrib.auth.models import User, check_password
-
- class SettingsBackend(object):
- """
- Authenticate against the settings ADMIN_LOGIN and ADMIN_PASSWORD.
-
- Use the login name, and a hash of the password. For example:
-
- ADMIN_LOGIN = 'admin'
- ADMIN_PASSWORD = 'sha1$4e987$afbcf42e21bd417fb71db8c66b321e9fc33051de'
- """
-
- def authenticate(self, username=None, password=None):
- login_valid = (settings.ADMIN_LOGIN == username)
- pwd_valid = check_password(password, settings.ADMIN_PASSWORD)
- if login_valid and pwd_valid:
- try:
- user = User.objects.get(username=username)
- except User.DoesNotExist:
- # Create a new user. Note that we can set password
- # to anything, because it won't be checked; the password
- # from settings.py will.
- user = User(username=username, password='get from settings.py')
- user.is_staff = True
- user.is_superuser = True
- user.save()
- return user
- return None
-
- def get_user(self, user_id):
- try:
- return User.objects.get(pk=user_id)
- except User.DoesNotExist:
- return None
-
-.. _authorization_methods:
-
-Handling authorization in custom backends
------------------------------------------
-
-Custom auth backends can provide their own permissions.
-
-The user model will delegate permission lookup functions
-(:meth:`~django.contrib.auth.models.User.get_group_permissions()`,
-:meth:`~django.contrib.auth.models.User.get_all_permissions()`,
-:meth:`~django.contrib.auth.models.User.has_perm()`, and
-:meth:`~django.contrib.auth.models.User.has_module_perms()`) to any
-authentication backend that implements these functions.
-
-The permissions given to the user will be the superset of all permissions
-returned by all backends. That is, Django grants a permission to a user that
-any one backend grants.
-
-The simple backend above could implement permissions for the magic admin
-fairly simply::
-
- class SettingsBackend(object):
-
- # ...
-
- def has_perm(self, user_obj, perm, obj=None):
- if user_obj.username == settings.ADMIN_LOGIN:
- return True
- else:
- return False
-
-This gives full permissions to the user granted access in the above example.
-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 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
-
-.. _anonymous_auth:
-
-Authorization for anonymous users
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-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
-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, 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
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-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 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
-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). 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.
diff --git a/docs/topics/auth/customizing.txt b/docs/topics/auth/customizing.txt
new file mode 100644
index 0000000000..143a729f37
--- /dev/null
+++ b/docs/topics/auth/customizing.txt
@@ -0,0 +1,1092 @@
+====================================
+Customizing authentication in Django
+====================================
+
+The authentication that comes with Django is good enough for most common cases,
+but you may have needs not met by the out-of-the-box defaults. To customize
+authentication to your projects needs involves understanding what points of the
+provided system are extendible or replaceable. This document provides details
+about how the auth system can be customized.
+
+:ref:`Authentication backends <authentication-backends>` provide an extensible
+system for when a username and password stored with the User model need
+to be authenticated against a different service than Django's default.
+
+You can give your models :ref:`custom permissions <custom-permissions>` that can be
+checked through Django's authorization system.
+
+You can :ref:`extend <extending-user>` the default User model, or :ref:`substitute
+<auth-custom-user>` a completely customized model.
+
+.. _authentication-backends:
+
+Other authentication sources
+============================
+
+There may be times you have the need to hook into another authentication source
+-- that is, another source of usernames and passwords or authentication
+methods.
+
+For example, your company may already have an LDAP setup that stores a username
+and password for every employee. It'd be a hassle for both the network
+administrator and the users themselves if users had separate accounts in LDAP
+and the Django-based applications.
+
+So, to handle situations like this, the Django authentication system lets you
+plug in other authentication sources. You can override Django's default
+database-based scheme, or you can use the default system in tandem with other
+systems.
+
+See the `authentication backend reference
+<authentication-backends-reference>` for information on the authentication
+backends included with Django.
+
+Specifying authentication backends
+----------------------------------
+
+Behind the scenes, Django maintains a list of "authentication backends" that it
+checks for authentication. When somebody calls
+:func:`django.contrib.auth.authenticate()` -- as described in :ref:`How to log
+a user in <how-to-log-a-user-in>` above -- Django tries authenticating across
+all of its authentication backends. If the first authentication method fails,
+Django tries the second one, and so on, until all backends have been attempted.
+
+The list of authentication backends to use is specified in the
+:setting:`AUTHENTICATION_BACKENDS` setting. This should be a tuple of Python
+path names that point to Python classes that know how to authenticate. These
+classes can be anywhere on your Python path.
+
+By default, :setting:`AUTHENTICATION_BACKENDS` is set to::
+
+ ('django.contrib.auth.backends.ModelBackend',)
+
+That's the basic authentication backend that checks the Django users database
+and queries the built-in 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
+processing at the first positive match.
+
+.. note::
+
+ Once a user has authenticated, Django stores which backend was used to
+ authenticate the user in the user's session, and re-uses the same backend
+ for the duration of that session whenever access to the currently
+ authenticated user is needed. This effectively means that authentication
+ sources are cached on a per-session basis, so if you change
+ :setting:`AUTHENTICATION_BACKENDS`, you'll need to clear out session data if
+ you need to force users to re-authenticate using different methods. A simple
+ way to do that is simply to execute ``Session.objects.all().delete()``.
+
+.. versionadded:: 1.6
+
+If a backend raises a :class:`~django.core.exceptions.PermissionDenied`
+exception, authentication will immediately fail. Django won't check the
+backends that follow.
+
+Writing an authentication backend
+---------------------------------
+
+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.
+
+The ``authenticate`` method takes credentials as keyword arguments. Most of
+the time, it'll just look like this::
+
+ class MyBackend(object):
+ def authenticate(self, username=None, password=None):
+ # Check the username/password and return a User.
+ ...
+
+But it could also authenticate a token, like so::
+
+ class MyBackend(object):
+ def authenticate(self, token=None):
+ # Check the token and return a User.
+ ...
+
+Either way, ``authenticate`` should check the credentials it gets, and it
+should return a ``User`` object that matches those credentials, if the
+credentials are valid. If they're not valid, it should return ``None``.
+
+The Django admin system is tightly coupled to the Django ``User`` object
+described at the beginning of this document. For now, the best way to deal with
+this is to create a Django ``User`` object for each user that exists for your
+backend (e.g., in your LDAP directory, your external SQL database, etc.) You
+can either write a script to do this in advance, or your ``authenticate``
+method can do it the first time a user logs in.
+
+Here's an example backend that authenticates against a username and password
+variable defined in your ``settings.py`` file and creates a Django ``User``
+object the first time a user authenticates::
+
+ from django.conf import settings
+ from django.contrib.auth.models import User, check_password
+
+ class SettingsBackend(object):
+ """
+ Authenticate against the settings ADMIN_LOGIN and ADMIN_PASSWORD.
+
+ Use the login name, and a hash of the password. For example:
+
+ ADMIN_LOGIN = 'admin'
+ ADMIN_PASSWORD = 'sha1$4e987$afbcf42e21bd417fb71db8c66b321e9fc33051de'
+ """
+
+ def authenticate(self, username=None, password=None):
+ login_valid = (settings.ADMIN_LOGIN == username)
+ pwd_valid = check_password(password, settings.ADMIN_PASSWORD)
+ if login_valid and pwd_valid:
+ try:
+ user = User.objects.get(username=username)
+ except User.DoesNotExist:
+ # Create a new user. Note that we can set password
+ # to anything, because it won't be checked; the password
+ # from settings.py will.
+ user = User(username=username, password='get from settings.py')
+ user.is_staff = True
+ user.is_superuser = True
+ user.save()
+ return user
+ return None
+
+ def get_user(self, user_id):
+ try:
+ return User.objects.get(pk=user_id)
+ except User.DoesNotExist:
+ return None
+
+.. _authorization_methods:
+
+Handling authorization in custom backends
+-----------------------------------------
+
+Custom auth backends can provide their own permissions.
+
+The user model will delegate permission lookup functions
+(:meth:`~django.contrib.auth.models.User.get_group_permissions()`,
+:meth:`~django.contrib.auth.models.User.get_all_permissions()`,
+:meth:`~django.contrib.auth.models.User.has_perm()`, and
+:meth:`~django.contrib.auth.models.User.has_module_perms()`) to any
+authentication backend that implements these functions.
+
+The permissions given to the user will be the superset of all permissions
+returned by all backends. That is, Django grants a permission to a user that
+any one backend grants.
+
+The simple backend above could implement permissions for the magic admin
+fairly simply::
+
+ class SettingsBackend(object):
+ ...
+ def has_perm(self, user_obj, perm, obj=None):
+ if user_obj.username == settings.ADMIN_LOGIN:
+ return True
+ else:
+ return False
+
+This gives full permissions to the user granted access in the above example.
+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 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 inheritance 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
+
+.. _anonymous_auth:
+
+Authorization for anonymous users
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+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
+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, 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
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+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 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
+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). 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.
+
+.. _custom-permissions:
+
+Custom permissions
+==================
+
+To create custom permissions for a given model object, use the ``permissions``
+:ref:`model Meta attribute <meta-options>`.
+
+This example Task model creates three custom permissions, i.e., actions users
+can or cannot do with Task instances, specific to your application::
+
+ class Task(models.Model):
+ ...
+ class Meta:
+ permissions = (
+ ("view_task", "Can see available tasks"),
+ ("change_task_status", "Can change the status of tasks"),
+ ("close_task", "Can remove a task by setting its status as closed"),
+ )
+
+The only thing this does is create those extra permissions when you run
+:djadmin:`manage.py syncdb <syncdb>`. Your code is in charge of checking the
+value of these permissions when an user is trying to access the functionality
+provided by the application (viewing tasks, changing the status of tasks,
+closing tasks.) Continuing the above example, the following checks if a user may
+view tasks::
+
+ user.has_perm('app.view_task')
+
+.. _extending-user:
+
+Extending the existing User model
+=================================
+
+There are two ways to extend the default
+:class:`~django.contrib.auth.models.User` model without substituting your own
+model. If the changes you need are purely behavioral, and don't require any
+change to what is stored in the database, you can create a :ref:`proxy model
+<proxy-models>` based on :class:`~django.contrib.auth.models.User`. This
+allows for any of the features offered by proxy models including default
+ordering, custom managers, or custom model methods.
+
+If you wish to store information related to ``User``, you can use a :ref:`one-to-one
+relationship <ref-onetoone>` to a model containing the fields for
+additional information. This one-to-one model is often called a profile model,
+as it might store non-auth related information about a site user. For example
+you might create an Employee model::
+
+ from django.contrib.auth.models import User
+
+ class Employee(models.Model):
+ user = models.OneToOneField(User)
+ department = models.CharField(max_length=100)
+
+Assuming an existing Employee Fred Smith who has both a User and Employee
+model, you can access the related information using Django's standard related
+model conventions::
+
+ >>> u = User.objects.get(username='fsmith')
+ >>> freds_department = u.employee.department
+
+To add a profile model's fields to the user page in the admin, define an
+:class:`~django.contrib.admin.InlineModelAdmin` (for this example, we'll use a
+:class:`~django.contrib.admin.StackedInline`) in your app's ``admin.py`` and
+add it to a ``UserAdmin`` class which is registered with the
+:class:`~django.contrib.auth.models.User` class::
+
+ from django.contrib import admin
+ from django.contrib.auth.admin import UserAdmin
+ from django.contrib.auth.models import User
+
+ from my_user_profile_app.models import Employee
+
+ # Define an inline admin descriptor for Employee model
+ # which acts a bit like a singleton
+ class EmployeeInline(admin.StackedInline):
+ model = Employee
+ can_delete = False
+ verbose_name_plural = 'employee'
+
+ # Define a new User admin
+ class UserAdmin(UserAdmin):
+ inlines = (EmployeeInline, )
+
+ # Re-register UserAdmin
+ admin.site.unregister(User)
+ admin.site.register(User, UserAdmin)
+
+These profile models are not special in any way - they are just Django models that
+happen to have a one-to-one link with a User model. As such, they do not get
+auto created when a user is created, but
+a :attr:`django.db.models.signals.post_save` could be used to create or update
+related models as appropriate.
+
+Note that using related models results in additional queries or joins to
+retrieve the related data, and depending on your needs substituting the User
+model and adding the related fields may be your better option. However
+existing links to the default User model within your project's apps may justify
+the extra database load.
+
+.. _auth-profiles:
+
+.. deprecated:: 1.5
+ With the introduction of :ref:`custom User models <auth-custom-user>`,
+ the use of :setting:`AUTH_PROFILE_MODULE` to define a single profile
+ model is no longer supported. See the
+ :doc:`Django 1.5 release notes</releases/1.5>` for more information.
+
+Prior to 1.5, a single profile model could be specified site-wide with the
+setting :setting:`AUTH_PROFILE_MODULE` with a string consisting of the
+following items, separated by a dot:
+
+1. The name of the application (case sensitive) in which the user
+ profile model is defined (in other words, the
+ name which was passed to :djadmin:`manage.py startapp <startapp>` to create
+ the application).
+
+2. The name of the model (not case sensitive) class.
+
+For example, if the profile model was a class named ``UserProfile`` and was
+defined inside an application named ``accounts``, the appropriate setting would
+be::
+
+ AUTH_PROFILE_MODULE = 'accounts.UserProfile'
+
+When a user profile model has been defined and specified in this manner, each
+:class:`~django.contrib.auth.models.User` object will have a method --
+:class:`~django.contrib.auth.models.User.get_profile()` -- which returns the
+instance of the user profile model associated with that
+:class:`~django.contrib.auth.models.User`.
+
+The method :class:`~django.contrib.auth.models.User.get_profile()`
+does not create a profile if one does not exist.
+
+.. _auth-custom-user:
+
+Substituting a custom User model
+================================
+
+.. versionadded:: 1.5
+
+Some kinds of projects may have authentication requirements for which Django's
+built-in :class:`~django.contrib.auth.models.User` model is not always
+appropriate. For instance, on some sites it makes more sense to use an email
+address as your identification token instead of a username.
+
+Django allows you to override the default User model by providing a value for
+the :setting:`AUTH_USER_MODEL` setting that references a custom model::
+
+ AUTH_USER_MODEL = 'myapp.MyUser'
+
+This dotted pair describes the name of the Django app (which must be in your
+:setting:`INSTALLED_APPS`), and the name of the Django model that you wish to
+use as your User model.
+
+.. warning::
+
+ Changing :setting:`AUTH_USER_MODEL` has a big effect on your database
+ structure. It changes the tables that are available, and it will affect the
+ construction of foreign keys and many-to-many relationships. If you intend
+ to set :setting:`AUTH_USER_MODEL`, you should set it before running
+ ``manage.py syncdb`` for the first time.
+
+ If you have an existing project and you want to migrate to using a custom
+ User model, you may need to look into using a migration tool like South_
+ to ease the transition.
+
+.. _South: http://south.aeracode.org
+
+Referencing the User model
+--------------------------
+
+.. currentmodule:: django.contrib.auth
+
+If you reference :class:`~django.contrib.auth.models.User` directly (for
+example, by referring to it in a foreign key), your code will not work in
+projects where the :setting:`AUTH_USER_MODEL` setting has been changed to a
+different User model.
+
+.. function:: get_user_model()
+
+ Instead of referring to :class:`~django.contrib.auth.models.User` directly,
+ you should reference the user model using
+ ``django.contrib.auth.get_user_model()``. This method will return the
+ currently active User model -- the custom User model if one is specified, or
+ :class:`~django.contrib.auth.models.User` otherwise.
+
+ When you define a foreign key or many-to-many relations to the User model,
+ you should specify the custom model using the :setting:`AUTH_USER_MODEL`
+ setting. For example::
+
+ from django.conf import settings
+ from django.db import models
+
+ class Article(models.Model):
+ author = models.ForeignKey(settings.AUTH_USER_MODEL)
+
+Specifying a custom User model
+------------------------------
+
+.. admonition:: Model design considerations
+
+ Think carefully before handling information not directly related to
+ authentication in your custom User Model.
+
+ It may be better to store app-specific user information in a model
+ that has a relation with the User model. That allows each app to specify
+ its own user data requirements without risking conflicts with other
+ apps. On the other hand, queries to retrieve this related information
+ will involve a database join, which may have an effect on performance.
+
+Django expects your custom User model to meet some minimum requirements.
+
+1. Your model must have an integer primary key.
+
+2. Your model must have a single unique field that can be used for
+ identification purposes. This can be a username, an email address,
+ or any other unique attribute.
+
+3. Your model must provide a way to address the user in a "short" and
+ "long" form. The most common interpretation of this would be to use
+ the user's given name as the "short" identifier, and the user's full
+ name as the "long" identifier. However, there are no constraints on
+ what these two methods return - if you want, they can return exactly
+ the same value.
+
+The easiest way to construct a compliant custom User model is to inherit from
+:class:`~django.contrib.auth.models.AbstractBaseUser`.
+:class:`~django.contrib.auth.models.AbstractBaseUser` provides the core
+implementation of a ``User`` model, including hashed passwords and tokenized
+password resets. You must then provide some key implementation details:
+
+.. currentmodule:: django.contrib.auth
+
+.. class:: models.CustomUser
+
+ .. attribute:: USERNAME_FIELD
+
+ A string describing the name of the field on the User model that is
+ used as the unique identifier. This will usually be a username of
+ some kind, but it can also be an email address, or any other unique
+ identifier. The field *must* be unique (i.e., have ``unique=True``
+ set in its definition).
+
+ In the following example, the field ``identifier`` is used
+ as the identifying field::
+
+ class MyUser(AbstractBaseUser):
+ identifier = models.CharField(max_length=40, unique=True, db_index=True)
+ ...
+ USERNAME_FIELD = 'identifier'
+
+ .. attribute:: REQUIRED_FIELDS
+
+ A list of the field names that *must* be provided when creating a user
+ via the :djadmin:`createsuperuser` management command. The user will be
+ prompted to supply a value for each of these fields. It should include
+ any field for which :attr:`~django.db.models.Field.blank` is ``False``
+ or undefined, but may include additional fields you want prompted for
+ when a user is created interactively. However, it will not work for
+ :class:`~django.db.models.ForeignKey` fields.
+
+ For example, here is the partial definition for a ``User`` model that
+ defines two required fields - a date of birth and height::
+
+ class MyUser(AbstractBaseUser):
+ ...
+ date_of_birth = models.DateField()
+ height = models.FloatField()
+ ...
+ REQUIRED_FIELDS = ['date_of_birth', 'height']
+
+ .. note::
+
+ ``REQUIRED_FIELDS`` must contain all required fields on your User
+ model, but should *not* contain the ``USERNAME_FIELD``.
+
+ .. attribute:: is_active
+
+ A boolean attribute that indicates whether the user is considered
+ "active". This attribute is provided as an attribute on
+ ``AbstractBaseUser`` defaulting to ``True``. How you choose to
+ implement it will depend on the details of your chosen auth backends.
+ See the documentation of the :attr:`attribute on the builtin user model
+ <django.contrib.auth.models.User.is_active>` for details.
+
+ .. method:: get_full_name()
+
+ A longer formal identifier for the user. A common interpretation
+ would be the full name name of the user, but it can be any string that
+ identifies the user.
+
+ .. method:: get_short_name()
+
+ A short, informal identifier for the user. A common interpretation
+ would be the first name of the user, but it can be any string that
+ identifies the user in an informal way. It may also return the same
+ value as :meth:`django.contrib.auth.models.User.get_full_name()`.
+
+The following methods are available on any subclass of
+:class:`~django.contrib.auth.models.AbstractBaseUser`:
+
+.. class:: models.AbstractBaseUser
+
+ .. method:: get_username()
+
+ Returns the value of the field nominated by ``USERNAME_FIELD``.
+
+ .. method:: models.AbstractBaseUser.is_anonymous()
+
+ Always returns ``False``. This is a way of differentiating
+ from :class:`~django.contrib.auth.models.AnonymousUser` objects.
+ Generally, you should prefer using
+ :meth:`~django.contrib.auth.models.AbstractBaseUser.is_authenticated()` to this
+ method.
+
+ .. method:: models.AbstractBaseUser.is_authenticated()
+
+ Always returns ``True``. This is a way to tell if the user has been
+ authenticated. This does not imply any permissions, and doesn't check
+ if the user is active - it only indicates that the user has provided a
+ valid username and password.
+
+ .. method:: models.AbstractBaseUser.set_password(raw_password)
+
+ Sets the user's password to the given raw string, taking care of the
+ password hashing. Doesn't save the
+ :class:`~django.contrib.auth.models.AbstractBaseUser` object.
+
+ .. method:: models.AbstractBaseUser.check_password(raw_password)
+
+ Returns ``True`` if the given raw string is the correct password for
+ the user. (This takes care of the password hashing in making the
+ comparison.)
+
+ .. method:: models.AbstractBaseUser.set_unusable_password()
+
+ Marks the user as having no password set. This isn't the same as
+ having a blank string for a password.
+ :meth:`~django.contrib.auth.models.AbstractBaseUser.check_password()` for this user
+ will never return ``True``. Doesn't save the
+ :class:`~django.contrib.auth.models.AbstractBaseUser` object.
+
+ You may need this if authentication for your application takes place
+ against an existing external source such as an LDAP directory.
+
+ .. method:: models.AbstractBaseUser.has_usable_password()
+
+ Returns ``False`` if
+ :meth:`~django.contrib.auth.models.AbstractBaseUser.set_unusable_password()` has
+ been called for this user.
+
+You should also define a custom manager for your ``User`` model. If your
+``User`` model defines ``username`` and ``email`` fields the same as Django's
+default ``User``, you can just install Django's
+:class:`~django.contrib.auth.models.UserManager`; however, if your ``User``
+model defines different fields, you will need to define a custom manager that
+extends :class:`~django.contrib.auth.models.BaseUserManager` providing two
+additional methods:
+
+.. class:: models.CustomUserManager
+
+ .. method:: models.CustomUserManager.create_user(*username_field*, password=None, \**other_fields)
+
+ The prototype of ``create_user()`` should accept the username field,
+ plus all required fields as arguments. For example, if your user model
+ uses ``email`` as the username field, and has ``date_of_birth`` as a
+ required fields, then ``create_user`` should be defined as::
+
+ def create_user(self, email, date_of_birth, password=None):
+ # create user here
+ ...
+
+ .. method:: models.CustomUserManager.create_superuser(*username_field*, password, \**other_fields)
+
+ The prototype of ``create_superuser()`` should accept the username
+ field, plus all required fields as arguments. For example, if your user
+ model uses ``email`` as the username field, and has ``date_of_birth``
+ as a required fields, then ``create_superuser`` should be defined as::
+
+ def create_superuser(self, email, date_of_birth, password):
+ # create superuser here
+ ...
+
+ Unlike ``create_user()``, ``create_superuser()`` *must* require the
+ caller to provider a password.
+
+:class:`~django.contrib.auth.models.BaseUserManager` provides the following
+utility methods:
+
+.. class:: models.BaseUserManager
+
+ .. method:: models.BaseUserManager.normalize_email(email)
+
+ A classmethod that normalizes email addresses by lowercasing
+ the domain portion of the email address.
+
+ .. method:: models.BaseUserManager.get_by_natural_key(username)
+
+ Retrieves a user instance using the contents of the field
+ nominated by ``USERNAME_FIELD``.
+
+ .. method:: models.BaseUserManager.make_random_password(length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789')
+
+ Returns a random password with the given length and given string of
+ allowed characters. (Note that the default value of ``allowed_chars``
+ doesn't contain letters that can cause user confusion, including:
+
+ * ``i``, ``l``, ``I``, and ``1`` (lowercase letter i, lowercase
+ letter L, uppercase letter i, and the number one)
+ * ``o``, ``O``, and ``0`` (uppercase letter o, lowercase letter o,
+ and zero)
+
+Extending Django's default User
+-------------------------------
+
+If you're entirely happy with Django's :class:`~django.contrib.auth.models.User`
+model and you just want to add some additional profile information, you can
+simply subclass ``django.contrib.auth.models.AbstractUser`` and add your
+custom profile fields. This class provides the full implementation of the
+default :class:`~django.contrib.auth.models.User` as an :ref:`abstract model
+<abstract-base-classes>`.
+
+.. _custom-users-and-the-built-in-auth-forms:
+
+Custom users and the built-in auth forms
+----------------------------------------
+
+As you may expect, built-in Django's :ref:`forms <built-in-auth-forms>` and
+:ref:`views <built-in-auth-views>` make certain assumptions about the user
+model that they are working with.
+
+If your user model doesn't follow the same assumptions, it may be necessary to define
+a replacement form, and pass that form in as part of the configuration of the
+auth views.
+
+* :class:`~django.contrib.auth.forms.UserCreationForm`
+
+ Depends on the :class:`~django.contrib.auth.models.User` model.
+ Must be re-written for any custom user model.
+
+* :class:`~django.contrib.auth.forms.UserChangeForm`
+
+ Depends on the :class:`~django.contrib.auth.models.User` model.
+ Must be re-written for any custom user model.
+
+* :class:`~django.contrib.auth.forms.AuthenticationForm`
+
+ Works with any subclass of :class:`~django.contrib.auth.models.AbstractBaseUser`,
+ and will adapt to use the field defined in `USERNAME_FIELD`.
+
+* :class:`~django.contrib.auth.forms.PasswordResetForm`
+
+ Assumes that the user model has an integer primary key, has a field named
+ ``email`` that can be used to identify the user, and a boolean field
+ named `is_active` to prevent password resets for inactive users.
+
+* :class:`~django.contrib.auth.forms.SetPasswordForm`
+
+ Works with any subclass of :class:`~django.contrib.auth.models.AbstractBaseUser`
+
+* :class:`~django.contrib.auth.forms.PasswordChangeForm`
+
+ Works with any subclass of :class:`~django.contrib.auth.models.AbstractBaseUser`
+
+* :class:`~django.contrib.auth.forms.AdminPasswordChangeForm`
+
+ Works with any subclass of :class:`~django.contrib.auth.models.AbstractBaseUser`
+
+
+Custom users and :mod:`django.contrib.admin`
+--------------------------------------------
+
+If you want your custom User model to also work with Admin, your User model must
+define some additional attributes and methods. These methods allow the admin to
+control access of the User to admin content:
+
+.. class:: models.CustomUser
+
+.. attribute:: is_staff
+
+ Returns ``True`` if the user is allowed to have access to the admin site.
+
+.. attribute:: is_active
+
+ Returns ``True`` if the user account is currently active.
+
+.. method:: has_perm(perm, obj=None):
+
+ Returns ``True`` if the user has the named permission. If ``obj`` is
+ provided, the permission needs to be checked against a specific object
+ instance.
+
+.. method:: has_module_perms(app_label):
+
+ Returns ``True`` if the user has permission to access models in
+ the given app.
+
+You will also need to register your custom User model with the admin. If
+your custom User model extends ``django.contrib.auth.models.AbstractUser``,
+you can use Django's existing ``django.contrib.auth.admin.UserAdmin``
+class. However, if your User model extends
+:class:`~django.contrib.auth.models.AbstractBaseUser`, you'll need to define
+a custom ModelAdmin class. It may be possible to subclass the default
+``django.contrib.auth.admin.UserAdmin``; however, you'll need to
+override any of the definitions that refer to fields on
+``django.contrib.auth.models.AbstractUser`` that aren't on your
+custom User class.
+
+Custom users and permissions
+----------------------------
+
+To make it easy to include Django's permission framework into your own User
+class, Django provides :class:`~django.contrib.auth.models.PermissionsMixin`.
+This is an abstract model you can include in the class hierarchy for your User
+model, giving you all the methods and database fields necessary to support
+Django's permission model.
+
+:class:`~django.contrib.auth.models.PermissionsMixin` provides the following
+methods and attributes:
+
+.. class:: models.PermissionsMixin
+
+ .. attribute:: models.PermissionsMixin.is_superuser
+
+ Boolean. Designates that this user has all permissions without
+ explicitly assigning them.
+
+ .. method:: models.PermissionsMixin.get_group_permissions(obj=None)
+
+ Returns a set of permission strings that the user has, through his/her
+ groups.
+
+ If ``obj`` is passed in, only returns the group permissions for
+ this specific object.
+
+ .. method:: models.PermissionsMixin.get_all_permissions(obj=None)
+
+ Returns a set of permission strings that the user has, both through
+ group and user permissions.
+
+ If ``obj`` is passed in, only returns the permissions for this
+ specific object.
+
+ .. method:: models.PermissionsMixin.has_perm(perm, obj=None)
+
+ Returns ``True`` if the user has the specified permission, where perm is
+ in the format ``"<app label>.<permission codename>"`` (see
+ :ref:`permissions <topic-authorization>`). If the user is inactive, this method will
+ always return ``False``.
+
+ If ``obj`` is passed in, this method won't check for a permission for
+ the model, but for this specific object.
+
+ .. method:: models.PermissionsMixin.has_perms(perm_list, obj=None)
+
+ Returns ``True`` if the user has each of the specified permissions,
+ where each perm is in the format
+ ``"<app label>.<permission codename>"``. If the user is inactive,
+ this method will always return ``False``.
+
+ If ``obj`` is passed in, this method won't check for permissions for
+ the model, but for the specific object.
+
+ .. method:: models.PermissionsMixin.has_module_perms(package_name)
+
+ Returns ``True`` if the user has any permissions in the given package
+ (the Django app label). If the user is inactive, this method will
+ always return ``False``.
+
+.. admonition:: ModelBackend
+
+ If you don't include the
+ :class:`~django.contrib.auth.models.PermissionsMixin`, you must ensure you
+ don't invoke the permissions methods on ``ModelBackend``. ``ModelBackend``
+ assumes that certain fields are available on your user model. If your User
+ model doesn't provide those fields, you will receive database errors when
+ you check permissions.
+
+Custom users and Proxy models
+-----------------------------
+
+One limitation of custom User models is that installing a custom User model
+will break any proxy model extending :class:`~django.contrib.auth.models.User`.
+Proxy models must be based on a concrete base class; by defining a custom User
+model, you remove the ability of Django to reliably identify the base class.
+
+If your project uses proxy models, you must either modify the proxy to extend
+the User model that is currently in use in your project, or merge your proxy's
+behavior into your User subclass.
+
+Custom users and signals
+------------------------
+
+Another limitation of custom User models is that you can't use
+:func:`django.contrib.auth.get_user_model()` as the sender or target of a signal
+handler. Instead, you must register the handler with the resulting User model.
+See :doc:`/topics/signals` for more information on registering an sending
+signals.
+
+Custom users and testing/fixtures
+---------------------------------
+
+If you are writing an application that interacts with the User model, you must
+take some precautions to ensure that your test suite will run regardless of
+the User model that is being used by a project. Any test that instantiates an
+instance of User will fail if the User model has been swapped out. This
+includes any attempt to create an instance of User with a fixture.
+
+To ensure that your test suite will pass in any project configuration,
+``django.contrib.auth.tests.utils`` defines a ``@skipIfCustomUser`` decorator.
+This decorator will cause a test case to be skipped if any User model other
+than the default Django user is in use. This decorator can be applied to a
+single test, or to an entire test class.
+
+Depending on your application, tests may also be needed to be added to ensure
+that the application works with *any* user model, not just the default User
+model. To assist with this, Django provides two substitute user models that
+can be used in test suites:
+
+* ``django.contrib.auth.tests.custom_user.CustomUser``, a custom user
+ model that uses an ``email`` field as the username, and has a basic
+ admin-compliant permissions setup
+
+* ``django.contrib.auth.tests.custom_user.ExtensionUser``, a custom
+ user model that extends ``django.contrib.auth.models.AbstractUser``,
+ adding a ``date_of_birth`` field.
+
+You can then use the ``@override_settings`` decorator to make that test run
+with the custom User model. For example, here is a skeleton for a test that
+would test three possible User models -- the default, plus the two User
+models provided by ``auth`` app::
+
+ from django.contrib.auth.tests.utils import skipIfCustomUser
+ from django.test import TestCase
+ from django.test.utils import override_settings
+
+
+ class ApplicationTestCase(TestCase):
+ @skipIfCustomUser
+ def test_normal_user(self):
+ "Run tests for the normal user model"
+ self.assertSomething()
+
+ @override_settings(AUTH_USER_MODEL='auth.CustomUser')
+ def test_custom_user(self):
+ "Run tests for a custom user model with email-based authentication"
+ self.assertSomething()
+
+ @override_settings(AUTH_USER_MODEL='auth.ExtensionUser')
+ def test_extension_user(self):
+ "Run tests for a simple extension of the built-in User."
+ self.assertSomething()
+
+
+A full example
+--------------
+
+Here is an example of an admin-compliant custom user app. This user model uses
+an email address as the username, and has a required date of birth; it
+provides no permission checking, beyond a simple ``admin`` flag on the user
+account. This model would be compatible with all the built-in auth forms and
+views, except for the User creation forms. This example illustrates how most of
+the components work together, but is not intended to be copied directly into
+projects for production use.
+
+This code would all live in a ``models.py`` file for a custom
+authentication app::
+
+ from django.db import models
+ from django.contrib.auth.models import (
+ BaseUserManager, AbstractBaseUser
+ )
+
+
+ class MyUserManager(BaseUserManager):
+ def create_user(self, email, date_of_birth, password=None):
+ """
+ Creates and saves a User with the given email, date of
+ birth and password.
+ """
+ if not email:
+ raise ValueError('Users must have an email address')
+
+ user = self.model(
+ email=MyUserManager.normalize_email(email),
+ date_of_birth=date_of_birth,
+ )
+
+ user.set_password(password)
+ user.save(using=self._db)
+ return user
+
+ def create_superuser(self, email, date_of_birth, password):
+ """
+ Creates and saves a superuser with the given email, date of
+ birth and password.
+ """
+ user = self.create_user(email,
+ password=password,
+ date_of_birth=date_of_birth
+ )
+ user.is_admin = True
+ user.save(using=self._db)
+ return user
+
+
+ class MyUser(AbstractBaseUser):
+ email = models.EmailField(
+ verbose_name='email address',
+ max_length=255,
+ unique=True,
+ db_index=True,
+ )
+ date_of_birth = models.DateField()
+ is_active = models.BooleanField(default=True)
+ is_admin = models.BooleanField(default=False)
+
+ objects = MyUserManager()
+
+ USERNAME_FIELD = 'email'
+ REQUIRED_FIELDS = ['date_of_birth']
+
+ def get_full_name(self):
+ # The user is identified by their email address
+ return self.email
+
+ def get_short_name(self):
+ # The user is identified by their email address
+ return self.email
+
+ def __unicode__(self):
+ return self.email
+
+ def has_perm(self, perm, obj=None):
+ "Does the user have a specific permission?"
+ # Simplest possible answer: Yes, always
+ return True
+
+ def has_module_perms(self, app_label):
+ "Does the user have permissions to view the app `app_label`?"
+ # Simplest possible answer: Yes, always
+ return True
+
+ @property
+ def is_staff(self):
+ "Is the user a member of staff?"
+ # Simplest possible answer: All admins are staff
+ return self.is_admin
+
+Then, to register this custom User model with Django's admin, the following
+code would be required in the app's ``admin.py`` file::
+
+ from django import forms
+ from django.contrib import admin
+ from django.contrib.auth.models import Group
+ from django.contrib.auth.admin import UserAdmin
+ from django.contrib.auth.forms import ReadOnlyPasswordHashField
+
+ from customauth.models import MyUser
+
+
+ class UserCreationForm(forms.ModelForm):
+ """A form for creating new users. Includes all the required
+ fields, plus a repeated password."""
+ password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
+ password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
+
+ class Meta:
+ model = MyUser
+ fields = ('email', 'date_of_birth')
+
+ def clean_password2(self):
+ # Check that the two password entries match
+ password1 = self.cleaned_data.get("password1")
+ password2 = self.cleaned_data.get("password2")
+ if password1 and password2 and password1 != password2:
+ raise forms.ValidationError("Passwords don't match")
+ return password2
+
+ def save(self, commit=True):
+ # Save the provided password in hashed format
+ user = super(UserCreationForm, self).save(commit=False)
+ user.set_password(self.cleaned_data["password1"])
+ if commit:
+ user.save()
+ return user
+
+
+ class UserChangeForm(forms.ModelForm):
+ """A form for updating users. Includes all the fields on
+ the user, but replaces the password field with admin's
+ password hash display field.
+ """
+ password = ReadOnlyPasswordHashField()
+
+ class Meta:
+ model = MyUser
+
+ def clean_password(self):
+ # Regardless of what the user provides, return the initial value.
+ # This is done here, rather than on the field, because the
+ # field does not have access to the initial value
+ return self.initial["password"]
+
+
+ class MyUserAdmin(UserAdmin):
+ # The forms to add and change user instances
+ form = UserChangeForm
+ add_form = UserCreationForm
+
+ # The fields to be used in displaying the User model.
+ # These override the definitions on the base UserAdmin
+ # that reference specific fields on auth.User.
+ list_display = ('email', 'date_of_birth', 'is_admin')
+ list_filter = ('is_admin',)
+ fieldsets = (
+ (None, {'fields': ('email', 'password')}),
+ ('Personal info', {'fields': ('date_of_birth',)}),
+ ('Permissions', {'fields': ('is_admin',)}),
+ ('Important dates', {'fields': ('last_login',)}),
+ )
+ add_fieldsets = (
+ (None, {
+ 'classes': ('wide',),
+ 'fields': ('email', 'date_of_birth', 'password1', 'password2')}
+ ),
+ )
+ search_fields = ('email',)
+ ordering = ('email',)
+ filter_horizontal = ()
+
+ # Now register the new UserAdmin...
+ admin.site.register(MyUser, MyUserAdmin)
+ # ... and, since we're not using Django's builtin permissions,
+ # unregister the Group model from admin.
+ admin.site.unregister(Group)
diff --git a/docs/topics/auth/default.txt b/docs/topics/auth/default.txt
new file mode 100644
index 0000000000..a38ee84841
--- /dev/null
+++ b/docs/topics/auth/default.txt
@@ -0,0 +1,1092 @@
+======================================
+Using the Django authentication system
+======================================
+
+.. currentmodule:: django.contrib.auth
+
+This document explains the usage of Django's authentication system in its
+default configuration. This configuration has evolved to serve the most common
+project needs, handling a reasonably wide range of tasks, and has a careful
+implementation of passwords and permissions, and can handle many projects as
+is. For projects where authentication needs differ from the default, Django
+supports extensive :doc:`extension and customization
+</topics/auth/customizing>` of authentication.
+
+Django authentication provides both authentication and authorization, together
+and is generally referred to as the authentication system, as these features
+somewhat coupled.
+
+.. _user-objects:
+
+User objects
+============
+
+:class:`~django.contrib.auth.models.User` objects are the core of the
+authentication system. They typically represent the people interacting with
+your site and are used to enable things like restricting access, registering
+user profiles, associating content with creators etc. Only one class of user
+exists in Django's authentication framework, i.e., 'superusers' or admin
+'staff' users are just user objects with special attributes set, not different
+classes of user objects.
+
+The primary attributes of the default user are:
+
+* username
+* password
+* email
+* first name
+* last name
+
+See the :class:`full API documentation <django.contrib.auth.models.User>` for
+full reference, the documentation that follows is more task oriented.
+
+.. _topics-auth-creating-users:
+
+Creating users
+--------------
+
+The most direct way to create users is to use the included
+:meth:`~django.contrib.auth.models.UserManager.create_user` helper function::
+
+ >>> from django.contrib.auth.models import User
+ >>> user = User.objects.create_user('john', 'lennon@thebeatles.com', 'johnpassword')
+
+ # At this point, user is a User object that has already been saved
+ # to the database. You can continue to change its attributes
+ # if you want to change other fields.
+ >>> user.last_name = 'Lennon'
+ >>> user.save()
+
+If you have the Django admin installed, you can also :ref:`create users
+interactively <auth-admin>`.
+
+.. _topics-auth-creating-superusers:
+
+Creating superusers
+-------------------
+
+:djadmin:`manage.py syncdb <syncdb>` prompts you to create a superuser the
+first time you run it with ``'django.contrib.auth'`` in your
+:setting:`INSTALLED_APPS`. If you need to create a superuser at a later date,
+you can use a command line utility::
+
+ manage.py createsuperuser --username=joe --email=joe@example.com
+
+You will be prompted for a password. After you enter one, the user will be
+created immediately. If you leave off the :djadminopt:`--username` or the
+:djadminopt:`--email` options, it will prompt you for those values.
+
+Changing passwords
+------------------
+
+Django does not store raw (clear text) passwords on the user model, but only
+a hash (see :doc:`documentation of how passwords are managed
+</topics/auth/passwords>` for full details). Because of this, do not attempt to
+manipulate the password attribute of the user directly. This is why a helper
+function is used when creating a user.
+
+To change a user's password, you have several options:
+
+: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
+they both match, the new password will be changed immediately. If you
+do not supply a user, the command will attempt to change the password
+whose username matches the current system user.
+
+You can also change a password programmatically, using
+:meth:`~django.contrib.auth.models.User.set_password()`:
+
+.. code-block:: python
+
+ >>> from django.contrib.auth.models import User
+ >>> u = User.objects.get(username__exact='john')
+ >>> u.set_password('new password')
+ >>> u.save()
+
+If you have the Django admin installed, you can also change user's passwords
+on the :ref:`authentication system's admin pages <auth-admin>`.
+
+Django also provides :ref:`views <built-in-auth-views>` and :ref:`forms
+<built-in-auth-forms>` that may be used to allow users to change their own
+passwords.
+
+Authenticating Users
+--------------------
+
+.. function:: authenticate(\**credentials)
+
+ To authenticate a given username and password, use
+ :func:`~django.contrib.auth.authenticate()`. It takes credentials in the
+ form of keyword arguments, for the default configuration this is
+ ``username`` and ``password``, and it returns
+ a :class:`~django.contrib.auth.models.User` object if the password is valid
+ for the given username. If the password is invalid,
+ :func:`~django.contrib.auth.authenticate()` returns ``None``. Example::
+
+ from django.contrib.auth import authenticate
+ user = authenticate(username='john', password='secret')
+ if user is not None:
+ # the password verified for the user
+ if user.is_active:
+ print("User is valid, active and authenticated")
+ else:
+ print("The password is valid, but the account has been disabled!")
+ else:
+ # the authentication system was unable to verify the username and password
+ print("The username and password were incorrect.")
+
+.. _topic-authorization:
+
+Permissions and Authorization
+=============================
+
+Django comes with a simple permissions system. It provides a way to assign
+permissions to specific users and groups of users.
+
+It's used by the Django admin site, but you're welcome to use it in your own
+code.
+
+The Django admin site uses permissions as follows:
+
+* Access to view the "add" form and add an object is limited to users with
+ the "add" permission for that type of object.
+* Access to view the change list, view the "change" form and change an
+ object is limited to users with the "change" permission for that type of
+ object.
+* Access to delete an object is limited to users with the "delete"
+ permission for that type of object.
+
+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.
+
+:class:`~django.contrib.auth.models.User` objects have two many-to-many
+fields: ``groups`` and ``user_permissions``.
+:class:`~django.contrib.auth.models.User` objects can access their related
+objects in the same way as any other :doc:`Django model
+</topics/db/models>`:
+
+.. code-block:: python
+
+ myuser.groups = [group_list]
+ myuser.groups.add(group, group, ...)
+ myuser.groups.remove(group, group, ...)
+ myuser.groups.clear()
+ myuser.user_permissions = [permission_list]
+ myuser.user_permissions.add(permission, permission, ...)
+ myuser.user_permissions.remove(permission, permission, ...)
+ myuser.user_permissions.clear()
+
+Default permissions
+-------------------
+
+When ``django.contrib.auth`` is listed in your :setting:`INSTALLED_APPS`
+setting, it will ensure that three default permissions -- add, change and
+delete -- are created for each Django model defined in one of your installed
+applications.
+
+These permissions will be created when you run :djadmin:`manage.py syncdb
+<syncdb>`; the first time you run ``syncdb`` after adding
+``django.contrib.auth`` to :setting:`INSTALLED_APPS`, the default permissions
+will be created for all previously-installed models, as well as for any new
+models being installed at that time. Afterward, it will create default
+permissions for new models each time you run :djadmin:`manage.py syncdb
+<syncdb>`.
+
+Assuming you have an application with an
+:attr:`~django.db.models.Options.app_label` ``foo`` and a model named ``Bar``,
+to test for basic permissions you should use:
+
+* add: ``user.has_perm('foo.add_bar')``
+* change: ``user.has_perm('foo.change_bar')``
+* delete: ``user.has_perm('foo.delete_bar')``
+
+The :class:`~django.contrib.auth.models.Permission` model is rarely accessed
+directly.
+
+Groups
+------
+
+:class:`django.contrib.auth.models.Group` models are a generic way of
+categorizing users so you can apply permissions, or some other label, to those
+users. A user can belong to any number of groups.
+
+A user in a group automatically has the permissions granted to that group. For
+example, if the group ``Site editors`` has the permission
+``can_edit_home_page``, any user in that group will have that permission.
+
+Beyond permissions, groups are a convenient way to categorize users to give
+them some label, or extended functionality. For example, you could create a
+group ``'Special users'``, and you could write code that could, say, give them
+access to a members-only portion of your site, or send them members-only email
+messages.
+
+Programmatically creating permissions
+-------------------------------------
+
+While :ref:`custom permissions <custom-permissions>` can be defined within
+a model's ``Meta`` class, you can also create permissions directly. For
+example, you can create the ``can_publish`` permission for a ``BlogPost`` model
+in ``myapp``::
+
+ from django.contrib.auth.models import Group, Permission
+ from django.contrib.contenttypes.models import ContentType
+
+ content_type = ContentType.objects.get(app_label='myapp', model='BlogPost')
+ permission = Permission.objects.create(codename='can_publish',
+ name='Can Publish Posts',
+ content_type=content_type)
+
+The permission can then be assigned to a
+:class:`~django.contrib.auth.models.User` via its ``user_permissions``
+attribute or to a :class:`~django.contrib.auth.models.Group` via its
+``permissions`` attribute.
+
+.. _auth-web-requests:
+
+Authentication in Web requests
+==============================
+
+Django uses :doc:`sessions </topics/http/sessions>` and middleware to hook the
+authentication system into :class:`request objects <django.http.HttpRequest>`.
+
+These provide a :attr:`request.user <django.http.HttpRequest.user>` attribute
+on every request which represents the current user. If the current user has not
+logged in, this attribute will be set to an instance
+of :class:`~django.contrib.auth.models.AnonymousUser`, otherwise it will be an
+instance of :class:`~django.contrib.auth.models.User`.
+
+You can tell them apart with
+:meth:`~django.contrib.auth.models.User.is_authenticated()`, like so::
+
+ if request.user.is_authenticated():
+ # Do something for authenticated users.
+ else:
+ # Do something for anonymous users.
+
+.. _how-to-log-a-user-in:
+
+How to log a user in
+--------------------
+
+If you have an authenticated user you want to attach to the current session
+- this is done with a :func:`~django.contrib.auth.login` function.
+
+.. function:: login()
+
+ To log a user in, from a view, use :func:`~django.contrib.auth.login()`. It
+ takes an :class:`~django.http.HttpRequest` object and a
+ :class:`~django.contrib.auth.models.User` object.
+ :func:`~django.contrib.auth.login()` saves the user's ID in the session,
+ using Django's session framework.
+
+ Note that any data set during the anonymous session is retained in the
+ session after a user logs in.
+
+ This example shows how you might use both
+ :func:`~django.contrib.auth.authenticate()` and
+ :func:`~django.contrib.auth.login()`::
+
+ from django.contrib.auth import authenticate, login
+
+ def my_view(request):
+ username = request.POST['username']
+ password = request.POST['password']
+ user = authenticate(username=username, password=password)
+ if user is not None:
+ if user.is_active:
+ login(request, user)
+ # Redirect to a success page.
+ else:
+ # Return a 'disabled account' error message
+ else:
+ # Return an 'invalid login' error message.
+
+.. admonition:: Calling ``authenticate()`` first
+
+ When you're manually logging a user in, you *must* call
+ :func:`~django.contrib.auth.authenticate()` before you call
+ :func:`~django.contrib.auth.login()`.
+ :func:`~django.contrib.auth.authenticate()`
+ sets an attribute on the :class:`~django.contrib.auth.models.User` noting
+ which authentication backend successfully authenticated that user (see the
+ :ref:`backends documentation <authentication-backends>` for details), and
+ this information is needed later during the login process. An error will be
+ raise if you try to login a user object retrieved from the database
+ directly.
+
+How to log a user out
+---------------------
+
+.. function:: logout()
+
+ To log out a user who has been logged in via
+ :func:`django.contrib.auth.login()`, use
+ :func:`django.contrib.auth.logout()` within your view. It takes an
+ :class:`~django.http.HttpRequest` object and has no return value.
+ Example::
+
+ from django.contrib.auth import logout
+
+ def logout_view(request):
+ logout(request)
+ # Redirect to a success page.
+
+ Note that :func:`~django.contrib.auth.logout()` doesn't throw any errors if
+ the user wasn't logged in.
+
+ When you call :func:`~django.contrib.auth.logout()`, the session data for
+ the current request is completely cleaned out. All existing data is
+ removed. This is to prevent another person from using the same Web browser
+ to log in and have access to the previous user's session data. If you want
+ to put anything into the session that will be available to the user
+ immediately after logging out, do that *after* calling
+ :func:`django.contrib.auth.logout()`.
+
+Limiting access to logged-in users
+----------------------------------
+
+The raw way
+~~~~~~~~~~~
+
+The simple, raw way to limit access to pages is to check
+:meth:`request.user.is_authenticated()
+<django.contrib.auth.models.User.is_authenticated()>` and either redirect to a
+login page::
+
+ from django.shortcuts import redirect
+
+ def my_view(request):
+ if not request.user.is_authenticated():
+ return redirect('/login/?next=%s' % request.path)
+ # ...
+
+...or display an error message::
+
+ from django.shortcuts import render
+
+ def my_view(request):
+ if not request.user.is_authenticated():
+ return render(request, 'myapp/login_error.html')
+ # ...
+
+.. currentmodule:: django.contrib.auth.decorators
+
+The login_required decorator
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. function:: login_required([redirect_field_name=REDIRECT_FIELD_NAME, login_url=None])
+
+ As a shortcut, you can use the convenient
+ :func:`~django.contrib.auth.decorators.login_required` decorator::
+
+ from django.contrib.auth.decorators import login_required
+
+ @login_required
+ def my_view(request):
+ ...
+
+ :func:`~django.contrib.auth.decorators.login_required` does the following:
+
+ * If the user isn't logged in, redirect to
+ :setting:`settings.LOGIN_URL <LOGIN_URL>`, passing the current absolute
+ path in the query string. Example: ``/accounts/login/?next=/polls/3/``.
+
+ * If the user is logged in, execute the view normally. The view code is
+ free to assume the user is logged in.
+
+ By default, the path that the user should be redirected to upon
+ successful authentication is stored in a query string parameter called
+ ``"next"``. If you would prefer to use a different name for this parameter,
+ :func:`~django.contrib.auth.decorators.login_required` takes an
+ optional ``redirect_field_name`` parameter::
+
+ from django.contrib.auth.decorators import login_required
+
+ @login_required(redirect_field_name='my_redirect_field')
+ def my_view(request):
+ ...
+
+ Note that if you provide a value to ``redirect_field_name``, you will most
+ likely need to customize your login template as well, since the template
+ context variable which stores the redirect path will use the value of
+ ``redirect_field_name`` as its key rather than ``"next"`` (the default).
+
+ :func:`~django.contrib.auth.decorators.login_required` also takes an
+ optional ``login_url`` parameter. Example::
+
+ from django.contrib.auth.decorators import login_required
+
+ @login_required(login_url='/accounts/login/')
+ def my_view(request):
+ ...
+
+ Note that if you don't specify the ``login_url`` parameter, you'll need to
+ ensure that the :setting:`settings.LOGIN_URL <LOGIN_URL>` and your login
+ view are properly associated. For example, using the defaults, add the
+ following line to your URLconf::
+
+ (r'^accounts/login/$', 'django.contrib.auth.views.login'),
+
+ .. versionchanged:: 1.5
+
+ The :setting:`settings.LOGIN_URL <LOGIN_URL>` also accepts
+ view function names and :ref:`named URL patterns <naming-url-patterns>`.
+ This allows you to freely remap your login view within your URLconf
+ without having to update the setting.
+
+.. note::
+
+ The login_required decorator does NOT check the is_active flag on a user.
+
+Limiting access to logged-in users that pass a test
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To limit access based on certain permissions or some other test, you'd do
+essentially the same thing as described in the previous section.
+
+The simple way is to run your test on :attr:`request.user
+<django.http.HttpRequest.user>` in the view directly. For example, this view
+checks to make sure the user has an email in the desired domain::
+
+ def my_view(request):
+ if not '@example.com' in request.user.email:
+ return HttpResponse("You can't vote in this poll.")
+ # ...
+
+.. function:: user_passes_test(func, [login_url=None])
+
+ As a shortcut, you can use the convenient ``user_passes_test`` decorator::
+
+ from django.contrib.auth.decorators import user_passes_test
+
+ def email_check(user):
+ return '@example.com' in user.email
+
+ @user_passes_test(email_check)
+ def my_view(request):
+ ...
+
+ :func:`~django.contrib.auth.decorators.user_passes_test` takes a required
+ argument: a callable that takes a
+ :class:`~django.contrib.auth.models.User` object and returns ``True`` if
+ the user is allowed to view the page. Note that
+ :func:`~django.contrib.auth.decorators.user_passes_test` does not
+ automatically check that the :class:`~django.contrib.auth.models.User` is
+ not anonymous.
+
+ :func:`~django.contrib.auth.decorators.user_passes_test()` takes an
+ optional ``login_url`` argument, which lets you specify the URL for your
+ login page (:setting:`settings.LOGIN_URL <LOGIN_URL>` by default).
+
+ For example::
+
+ @user_passes_test(email_check, login_url='/login/')
+ def my_view(request):
+ ...
+
+The permission_required decorator
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. function:: permission_required([login_url=None, raise_exception=False])
+
+ It's a relatively common task to check whether a user has a particular
+ permission. For that reason, Django provides a shortcut for that case: the
+ :func:`~django.contrib.auth.decorators.permission_required()` decorator.::
+
+ from django.contrib.auth.decorators import permission_required
+
+ @permission_required('polls.can_vote')
+ def my_view(request):
+ ...
+
+ As for the :meth:`~django.contrib.auth.models.User.has_perm` method,
+ permission names take the form ``"<app label>.<permission codename>"``
+ (i.e. ``polls.can_vote`` for a permission on a model in the ``polls``
+ application).
+
+ Note that :func:`~django.contrib.auth.decorators.permission_required()`
+ also takes an optional ``login_url`` parameter. Example::
+
+ from django.contrib.auth.decorators import permission_required
+
+ @permission_required('polls.can_vote', login_url='/loginpage/')
+ def my_view(request):
+ ...
+
+ As in the :func:`~django.contrib.auth.decorators.login_required` decorator,
+ ``login_url`` defaults to :setting:`settings.LOGIN_URL <LOGIN_URL>`.
+
+ If the ``raise_exception`` parameter is given, the decorator will raise
+ :exc:`~django.core.exceptions.PermissionDenied`, prompting :ref:`the 403
+ (HTTP Forbidden) view<http_forbidden_view>` instead of redirecting to the
+ login page.
+
+Applying permissions to generic views
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+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.
+
+
+.. _built-in-auth-views:
+
+Authentication Views
+--------------------
+
+.. module:: django.contrib.auth.views
+
+Django provides several views that you can use for handling login, logout, and
+password management. These make use of the :ref:`stock auth forms
+<built-in-auth-forms>` but you can pass in your own forms as well.
+
+Django provides no default template for the authentication views - however the
+template context is documented for each view below.
+
+The built-in views all return
+a :class:`~django.template.response.TemplateResponse` instance, which allows
+you to easily customize the response data before rendering. For more details,
+see the :doc:`TemplateResponse documentation </ref/template-response>`.
+
+Most built-in authentication views provide a URL name for easier reference. See
+:doc:`the URL documentation </topics/http/urls>` for details on using named URL
+patterns.
+
+
+.. function:: login(request, [template_name, redirect_field_name, authentication_form])
+
+ **URL name:** ``login``
+
+ See :doc:`the URL documentation </topics/http/urls>` for details on using
+ named URL patterns.
+
+ Here's what ``django.contrib.auth.views.login`` does:
+
+ * If called via ``GET``, it displays a login form that POSTs to the
+ same URL. More on this in a bit.
+
+ * If called via ``POST`` with user submitted credentials, it tries to log
+ the user in. If login is successful, the view redirects to the URL
+ specified in ``next``. If ``next`` isn't provided, it redirects to
+ :setting:`settings.LOGIN_REDIRECT_URL <LOGIN_REDIRECT_URL>` (which
+ defaults to ``/accounts/profile/``). If login isn't successful, it
+ redisplays the login form.
+
+ It's your responsibility to provide the html for the login template
+ , called ``registration/login.html`` by default. This template gets passed
+ four template context variables:
+
+ * ``form``: A :class:`~django.forms.Form` object representing the
+ :class:`~django.contrib.auth.forms.AuthenticationForm`.
+
+ * ``next``: The URL to redirect to after successful login. This may
+ contain a query string, too.
+
+ * ``site``: The current :class:`~django.contrib.sites.models.Site`,
+ according to the :setting:`SITE_ID` setting. If you don't have the
+ site framework installed, this will be set to an instance of
+ :class:`~django.contrib.sites.models.RequestSite`, which derives the
+ site name and domain from the current
+ :class:`~django.http.HttpRequest`.
+
+ * ``site_name``: An alias for ``site.name``. If you don't have the site
+ framework installed, this will be set to the value of
+ :attr:`request.META['SERVER_NAME'] <django.http.HttpRequest.META>`.
+ For more on sites, see :doc:`/ref/contrib/sites`.
+
+ If you'd prefer not to call the template :file:`registration/login.html`,
+ you can pass the ``template_name`` parameter via the extra arguments to
+ the view in your URLconf. For example, this URLconf line would use
+ :file:`myapp/login.html` instead::
+
+ (r'^accounts/login/$', 'django.contrib.auth.views.login', {'template_name': 'myapp/login.html'}),
+
+ You can also specify the name of the ``GET`` field which contains the URL
+ to redirect to after login by passing ``redirect_field_name`` to the view.
+ By default, the field is called ``next``.
+
+ Here's a sample :file:`registration/login.html` template you can use as a
+ starting point. It assumes you have a :file:`base.html` template that
+ defines a ``content`` block:
+
+ .. code-block:: html+django
+
+ {% extends "base.html" %}
+
+ {% block content %}
+
+ {% if form.errors %}
+ <p>Your username and password didn't match. Please try again.</p>
+ {% endif %}
+
+ <form method="post" action="{% url 'django.contrib.auth.views.login' %}">
+ {% csrf_token %}
+ <table>
+ <tr>
+ <td>{{ form.username.label_tag }}</td>
+ <td>{{ form.username }}</td>
+ </tr>
+ <tr>
+ <td>{{ form.password.label_tag }}</td>
+ <td>{{ form.password }}</td>
+ </tr>
+ </table>
+
+ <input type="submit" value="login" />
+ <input type="hidden" name="next" value="{{ next }}" />
+ </form>
+
+ {% endblock %}
+
+ If you have customized authentication (see
+ :doc:`Customizing Authentication </topics/auth/customizing>`) you can pass a custom authentication form
+ to the login view via the ``authentication_form`` parameter. This form must
+ accept a ``request`` keyword argument in its ``__init__`` method, and
+ provide a ``get_user`` method which returns the authenticated user object
+ (this method is only ever called after successful form validation).
+
+ .. _forms documentation: ../forms/
+ .. _site framework docs: ../sites/
+
+
+.. function:: logout(request, [next_page, template_name, redirect_field_name])
+
+ Logs a user out.
+
+ **URL name:** ``logout``
+
+ **Optional arguments:**
+
+ * ``next_page``: The URL to redirect to after logout.
+
+ * ``template_name``: The full name of a template to display after
+ logging the user out. Defaults to
+ :file:`registration/logged_out.html` if no argument is supplied.
+
+ * ``redirect_field_name``: The name of a ``GET`` field containing the
+ URL to redirect to after log out. Overrides ``next_page`` if the given
+ ``GET`` parameter is passed.
+
+ **Template context:**
+
+ * ``title``: The string "Logged out", localized.
+
+ * ``site``: The current :class:`~django.contrib.sites.models.Site`,
+ according to the :setting:`SITE_ID` setting. If you don't have the
+ site framework installed, this will be set to an instance of
+ :class:`~django.contrib.sites.models.RequestSite`, which derives the
+ site name and domain from the current
+ :class:`~django.http.HttpRequest`.
+
+ * ``site_name``: An alias for ``site.name``. If you don't have the site
+ framework installed, this will be set to the value of
+ :attr:`request.META['SERVER_NAME'] <django.http.HttpRequest.META>`.
+ For more on sites, see :doc:`/ref/contrib/sites`.
+
+.. function:: logout_then_login(request[, login_url])
+
+ Logs a user out, then redirects to the login page.
+
+ **URL name:** No default URL provided
+
+ **Optional arguments:**
+
+ * ``login_url``: The URL of the login page to redirect to.
+ Defaults to :setting:`settings.LOGIN_URL <LOGIN_URL>` if not supplied.
+
+.. function:: password_change(request[, template_name, post_change_redirect, password_change_form])
+
+ Allows a user to change their password.
+
+ **URL name:** ``password_change``
+
+ **Optional arguments:**
+
+ * ``template_name``: The full name of a template to use for
+ displaying the password change form. Defaults to
+ :file:`registration/password_change_form.html` if not supplied.
+
+ * ``post_change_redirect``: The URL to redirect to after a successful
+ password change.
+
+ * ``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
+ :class:`~django.contrib.auth.forms.PasswordChangeForm`.
+
+ **Template context:**
+
+ * ``form``: The password change form (see ``password_change_form`` above).
+
+.. function:: password_change_done(request[, template_name])
+
+ The page shown after a user has changed their password.
+
+ **URL name:** ``password_change_done``
+
+ **Optional arguments:**
+
+ * ``template_name``: The full name of a template to use.
+ Defaults to :file:`registration/password_change_done.html` if not
+ supplied.
+
+.. function:: password_reset(request[, is_admin_site, template_name, email_template_name, password_reset_form, token_generator, post_reset_redirect, from_email])
+
+ Allows a user to reset their password by generating a one-time use link
+ that can be used to reset the password, and sending that link to the
+ user's registered email address.
+
+ If the email address provided does not exist in the system, this view
+ won't send an email, but the user won't receive any error message either.
+ This prevents information leaking to potential attackers. If you want to
+ provide an error message in this case, you can subclass
+ :class:`~django.contrib.auth.forms.PasswordResetForm` and use the
+ ``password_reset_form`` argument.
+
+
+ Users flagged with an unusable password (see
+ :meth:`~django.contrib.auth.models.User.set_unusable_password()` aren't
+ allowed to request a password reset to prevent misuse when using an
+ external authentication source like LDAP. Note that they won't receive any
+ error message since this would expose their account's existence but no
+ mail will be sent either.
+
+ .. versionchanged:: 1.6
+ Previously, error messages indicated whether a given email was
+ registered.
+
+ **URL name:** ``password_reset``
+
+ **Optional arguments:**
+
+ * ``template_name``: The full name of a template to use for
+ displaying the password reset form. Defaults to
+ :file:`registration/password_reset_form.html` if not supplied.
+
+ * ``email_template_name``: The full name of a template to use for
+ generating the email with the reset password link. Defaults to
+ :file:`registration/password_reset_email.html` if not supplied.
+
+ * ``subject_template_name``: The full name of a template to use for
+ the subject of the email with the reset password link. Defaults
+ to :file:`registration/password_reset_subject.txt` if not supplied.
+
+ * ``password_reset_form``: Form that will be used to get the email of
+ the user to reset the password for. Defaults to
+ :class:`~django.contrib.auth.forms.PasswordResetForm`.
+
+ * ``token_generator``: Instance of the class to check the one time link.
+ This will default to ``default_token_generator``, it's an instance of
+ ``django.contrib.auth.tokens.PasswordResetTokenGenerator``.
+
+ * ``post_reset_redirect``: The URL to redirect to after a successful
+ password reset request.
+
+ * ``from_email``: A valid email address. By default Django uses
+ the :setting:`DEFAULT_FROM_EMAIL`.
+
+ **Template context:**
+
+ * ``form``: The form (see ``password_reset_form`` above) for resetting
+ the user's password.
+
+ **Email template context:**
+
+ * ``email``: An alias for ``user.email``
+
+ * ``user``: The current :class:`~django.contrib.auth.models.User`,
+ according to the ``email`` form field. Only active users are able to
+ reset their passwords (``User.is_active is True``).
+
+ * ``site_name``: An alias for ``site.name``. If you don't have the site
+ framework installed, this will be set to the value of
+ :attr:`request.META['SERVER_NAME'] <django.http.HttpRequest.META>`.
+ For more on sites, see :doc:`/ref/contrib/sites`.
+
+ * ``domain``: An alias for ``site.domain``. If you don't have the site
+ framework installed, this will be set to the value of
+ ``request.get_host()``.
+
+ * ``protocol``: http or https
+
+ * ``uid``: The user's id encoded in base 36.
+
+ * ``token``: Token to check that the reset link is valid.
+
+ Sample ``registration/password_reset_email.html`` (email body template):
+
+ .. code-block:: html+django
+
+ Someone asked for password reset for email {{ email }}. Follow the link below:
+ {{ protocol}}://{{ domain }}{% url 'password_reset_confirm' uidb36=uid token=token %}
+
+ The same template context is used for subject template. Subject must be
+ single line plain text string.
+
+
+.. function:: password_reset_done(request[, template_name])
+
+ The page shown after a user has been emailed a link to reset their
+ password. This view is called by default if the :func:`password_reset` view
+ doesn't have an explicit ``post_reset_redirect`` URL set.
+
+ **URL name:** ``password_reset_done``
+
+ **Optional arguments:**
+
+ * ``template_name``: The full name of a template to use.
+ Defaults to :file:`registration/password_reset_done.html` if not
+ supplied.
+
+.. function:: password_reset_confirm(request[, uidb36, token, template_name, token_generator, set_password_form, post_reset_redirect])
+
+ Presents a form for entering a new password.
+
+ **URL name:** ``password_reset_confirm``
+
+ **Optional arguments:**
+
+ * ``uidb36``: The user's id encoded in base 36. Defaults to ``None``.
+
+ * ``token``: Token to check that the password is valid. Defaults to
+ ``None``.
+
+ * ``template_name``: The full name of a template to display the confirm
+ password view. Default value is :file:`registration/password_reset_confirm.html`.
+
+ * ``token_generator``: Instance of the class to check the password. This
+ will default to ``default_token_generator``, it's an instance of
+ ``django.contrib.auth.tokens.PasswordResetTokenGenerator``.
+
+ * ``set_password_form``: Form that will be used to set the password.
+ Defaults to :class:`~django.contrib.auth.forms.SetPasswordForm`
+
+ * ``post_reset_redirect``: URL to redirect after the password reset
+ done. Defaults to ``None``.
+
+ **Template context:**
+
+ * ``form``: The form (see ``set_password_form`` above) for setting the
+ new user's password.
+
+ * ``validlink``: Boolean, True if the link (combination of uidb36 and
+ token) is valid or unused yet.
+
+.. function:: password_reset_complete(request[,template_name])
+
+ Presents a view which informs the user that the password has been
+ successfully changed.
+
+ **URL name:** ``password_reset_complete``
+
+ **Optional arguments:**
+
+ * ``template_name``: The full name of a template to display the view.
+ Defaults to :file:`registration/password_reset_complete.html`.
+
+Helper functions
+----------------
+
+.. currentmodule:: django.contrib.auth.views
+
+.. function:: redirect_to_login(next[, login_url, redirect_field_name])
+
+ Redirects to the login page, and then back to another URL after a
+ successful login.
+
+ **Required arguments:**
+
+ * ``next``: The URL to redirect to after a successful login.
+
+ **Optional arguments:**
+
+ * ``login_url``: The URL of the login page to redirect to.
+ Defaults to :setting:`settings.LOGIN_URL <LOGIN_URL>` if not supplied.
+
+ * ``redirect_field_name``: The name of a ``GET`` field containing the
+ URL to redirect to after log out. Overrides ``next`` if the given
+ ``GET`` parameter is passed.
+
+
+.. _built-in-auth-forms:
+
+Built-in forms
+--------------
+
+.. module:: django.contrib.auth.forms
+
+If you don't want to use the built-in views, but want the convenience of not
+having to write forms for this functionality, the authentication system
+provides several built-in forms located in :mod:`django.contrib.auth.forms`:
+
+.. note::
+ The built-in authentication forms make certain assumptions about the user
+ model that they are working with. If you're using a :ref:`custom User model
+ <auth-custom-user>`, it may be necessary to define your own forms for the
+ authentication system. For more information, refer to the documentation
+ about :ref:`using the built-in authentication forms with custom user models
+ <custom-users-and-the-built-in-auth-forms>`.
+
+.. class:: AdminPasswordChangeForm
+
+ A form used in the admin interface to change a user's password.
+
+.. class:: AuthenticationForm
+
+ A form for logging a user in.
+
+.. class:: PasswordChangeForm
+
+ A form for allowing a user to change their password.
+
+.. class:: PasswordResetForm
+
+ A form for generating and emailing a one-time use link to reset a
+ user's password.
+
+.. class:: SetPasswordForm
+
+ A form that lets a user change his/her password without entering the old
+ password.
+
+.. class:: UserChangeForm
+
+ A form used in the admin interface to change a user's information and
+ permissions.
+
+.. class:: UserCreationForm
+
+ A form for creating a new user.
+
+.. currentmodule:: django.contrib.auth
+
+
+Authentication data in templates
+--------------------------------
+
+The currently logged-in user and his/her permissions are made available in the
+:doc:`template context </ref/templates/api>` when you use
+:class:`~django.template.RequestContext`.
+
+.. admonition:: Technicality
+
+ Technically, these variables are only made available in the template context
+ if you use :class:`~django.template.RequestContext` *and* your
+ :setting:`TEMPLATE_CONTEXT_PROCESSORS` setting contains
+ ``"django.contrib.auth.context_processors.auth"``, which is default. For
+ more, see the :ref:`RequestContext docs <subclassing-context-requestcontext>`.
+
+Users
+~~~~~
+
+When rendering a template :class:`~django.template.RequestContext`, the
+currently logged-in user, either a :class:`~django.contrib.auth.models.User`
+instance or an :class:`~django.contrib.auth.models.AnonymousUser` instance, is
+stored in the template variable ``{{ user }}``:
+
+.. code-block:: html+django
+
+ {% if user.is_authenticated %}
+ <p>Welcome, {{ user.username }}. Thanks for logging in.</p>
+ {% else %}
+ <p>Welcome, new user. Please log in.</p>
+ {% endif %}
+
+This template context variable is not available if a ``RequestContext`` is not
+being used.
+
+Permissions
+~~~~~~~~~~~
+
+The currently logged-in user's permissions are stored in the template variable
+``{{ perms }}``. This is an instance of
+``django.contrib.auth.context_processors.PermWrapper``, which is a
+template-friendly proxy of permissions.
+
+In the ``{{ perms }}`` object, single-attribute lookup is a proxy to
+:meth:`User.has_module_perms <django.contrib.auth.models.User.has_module_perms>`.
+This example would display ``True`` if the logged-in user had any permissions
+in the ``foo`` app::
+
+ {{ perms.foo }}
+
+Two-level-attribute lookup is a proxy to
+:meth:`User.has_perm <django.contrib.auth.models.User.has_perm>`. This example
+would display ``True`` if the logged-in user had the permission
+``foo.can_vote``::
+
+ {{ perms.foo.can_vote }}
+
+Thus, you can check permissions in template ``{% if %}`` statements:
+
+.. code-block:: html+django
+
+ {% if perms.foo %}
+ <p>You have permission to do something in the foo app.</p>
+ {% if perms.foo.can_vote %}
+ <p>You can vote!</p>
+ {% endif %}
+ {% if perms.foo.can_drive %}
+ <p>You can drive!</p>
+ {% endif %}
+ {% else %}
+ <p>You don't have permission to do anything in the foo app.</p>
+ {% endif %}
+
+.. versionadded:: 1.5
+ Permission lookup by "if in".
+
+It is possible to also look permissions up by ``{% if in %}`` statements.
+For example:
+
+.. code-block:: html+django
+
+ {% if 'foo' in perms %}
+ {% if 'foo.can_vote' in perms %}
+ <p>In lookup works, too.</p>
+ {% endif %}
+ {% endif %}
+
+.. _auth-admin:
+
+Managing users in the admin
+===========================
+
+When you have both ``django.contrib.admin`` and ``django.contrib.auth``
+installed, the admin provides a convenient way to view and manage users,
+groups, and permissions. Users can be created and deleted like any Django
+model. Groups can be created, and permissions can be assigned to users or
+groups. A log of user edits to models made within the admin is also stored and
+displayed.
+
+Creating Users
+--------------
+
+You should see a link to "Users" in the "Auth"
+section of the main admin index page. The "Add user" admin page is different
+than standard admin pages in that it requires you to choose a username and
+password before allowing you to edit the rest of the user's fields.
+
+Also note: if you want a user account to be able to create users using the
+Django admin site, you'll need to give them permission to add users *and*
+change users (i.e., the "Add user" and "Change user" permissions). If an
+account has permission to add users but not to change them, that account won't
+be able to add users. Why? Because if you have permission to add users, you
+have the power to create superusers, which can then, in turn, change other
+users. So Django requires add *and* change permissions as a slight security
+measure.
+
+Changing Passwords
+------------------
+
+User passwords are not displayed in the admin (nor stored in the database), but
+the :doc:`password storage details </topics/auth/passwords>` are displayed.
+Included in the display of this information is a link to
+a password change form that allows admins to change user passwords.
diff --git a/docs/topics/auth/index.txt b/docs/topics/auth/index.txt
new file mode 100644
index 0000000000..8447d449ce
--- /dev/null
+++ b/docs/topics/auth/index.txt
@@ -0,0 +1,89 @@
+=============================
+User authentication in Django
+=============================
+
+.. toctree::
+ :hidden:
+
+ default
+ passwords
+ customizing
+
+.. module:: django.contrib.auth
+ :synopsis: Django's authentication framework.
+
+Django comes with an user authentication system. It handles user accounts,
+groups, permissions and cookie-based user sessions. This section of the
+documentation explains how the default implementation works out of the box, as
+well as how to :doc:`extend and customize </topics/auth/customizing>` it to
+suit your project's needs.
+
+Overview
+========
+
+The Django authentication system handles both authentication and authorization.
+Briefly, authentication verifies a user is who they claim to be, and
+authorization determines what an authenticated user is allowed to do. Here the
+term authentication is used to refer to both tasks.
+
+The auth system consists of:
+
+* Users
+* Permissions: Binary (yes/no) flags designating whether a user may perform
+ a certain task.
+* Groups: A generic way of applying labels and permissions to more than one
+ user.
+* A configurable password hashing system
+* Forms and view tools for logging in users, or restricting content
+* A pluggable backend system
+
+The authentication system in Django aims to be very generic and doesn't provide
+some features commonly found in web authentication systems. Solutions for some
+of these common problems have been implemented in third-party packages:
+
+* Password strength checking
+* Throttling of login attempts
+* Authentication against third-parties (OAuth, for example)
+
+Installation
+============
+
+Authentication support is bundled as a Django contrib module in
+``django.contrib.auth``. By default, the required configuration is already
+included in the :file:`settings.py` generated by :djadmin:`django-admin.py
+startproject <startproject>`, these consist of two items listed in your
+:setting:`INSTALLED_APPS` setting:
+
+1. ``'django.contrib.auth'`` contains the core of the authentication framework,
+ and its default models.
+2. ``'django.contrib.contenttypes'`` is the Django :doc:`content type system
+ </ref/contrib/contenttypes>`, which allows permissions to be associated with
+ models you create.
+
+and two items in your :setting:`MIDDLEWARE_CLASSES` setting:
+
+1. :class:`~django.contrib.sessions.middleware.SessionMiddleware` manages
+ :doc:`sessions </topics/http/sessions>` across requests.
+2. :class:`~django.contrib.auth.middleware.AuthenticationMiddleware` associates
+ users with requests using sessions.
+
+With these settings in place, running the command ``manage.py syncdb`` creates
+the necessary database tables for auth related models, creates permissions for
+any models defined in your installed apps, and prompts you to create
+a superuser account the first time you run it.
+
+Usage
+=====
+
+:doc:`Using Django's default implementation <default>`
+
+* :ref:`Working with User objects <user-objects>`
+* :ref:`Permissions and authorization <topic-authorization>`
+* :ref:`Authentication in web requests <auth-web-requests>`
+* :ref:`Managing users in the admin <auth-admin>`
+
+:doc:`API reference for the default implementation </ref/contrib/auth>`
+
+:doc:`Customizing Users and authentication <customizing>`
+
+:doc:`Password management in Django <passwords>`
diff --git a/docs/topics/auth/passwords.txt b/docs/topics/auth/passwords.txt
new file mode 100644
index 0000000000..2193e6a3c7
--- /dev/null
+++ b/docs/topics/auth/passwords.txt
@@ -0,0 +1,225 @@
+=============================
+Password management in Django
+=============================
+
+Password management is something that should generally not be reinvented
+unnecessarily, and Django endeavors to provide a secure and flexible set of
+tools for managing user passwords. This document describes how Django stores
+passwords, how the storage hashing can be configured, and some utilities to
+work with hashed passwords.
+
+.. _auth_password_storage:
+
+How Django stores passwords
+===========================
+
+Django provides a flexible password storage system and uses PBKDF2 by default.
+
+The :attr:`~django.contrib.auth.models.User.password` attribute of a
+:class:`~django.contrib.auth.models.User` object is a string in this format::
+
+ <algorithm>$<iterations>$<salt>$<hash>
+
+Those are the components used for storing a User's password, separated by the
+dollar-sign character and consist of: the hashing algorithm, the number of
+algorithm iterations (work factor), the random salt, and the resulting password
+hash. The algorithm is one of a number of one-way hashing or password storage
+algorithms Django can use; see below. Iterations describe the number of times
+the algorithm is run over the hash. Salt is the random seed used and the hash
+is the result of the one-way function.
+
+By default, Django uses the PBKDF2_ algorithm with a SHA256 hash, a
+password stretching mechanism recommended by NIST_. This should be
+sufficient for most users: it's quite secure, requiring massive
+amounts of computing time to break.
+
+However, depending on your requirements, you may choose a different
+algorithm, or even use a custom algorithm to match your specific
+security situation. Again, most users shouldn't need to do this -- if
+you're not sure, you probably don't. If you do, please read on:
+
+Django chooses the algorithm to use by consulting the
+:setting:`PASSWORD_HASHERS` setting. This is a list of hashing algorithm
+classes that this Django installation supports. The first entry in this list
+(that is, ``settings.PASSWORD_HASHERS[0]``) will be used to store passwords,
+and all the other entries are valid hashers that can be used to check existing
+passwords. This means that if you want to use a different algorithm, you'll
+need to modify :setting:`PASSWORD_HASHERS` to list your preferred algorithm
+first in the list.
+
+The default for :setting:`PASSWORD_HASHERS` is::
+
+ PASSWORD_HASHERS = (
+ 'django.contrib.auth.hashers.PBKDF2PasswordHasher',
+ 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
+ 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
+ 'django.contrib.auth.hashers.BCryptPasswordHasher',
+ 'django.contrib.auth.hashers.SHA1PasswordHasher',
+ 'django.contrib.auth.hashers.MD5PasswordHasher',
+ 'django.contrib.auth.hashers.CryptPasswordHasher',
+ )
+
+This means that Django will use PBKDF2_ to store all passwords, but will support
+checking passwords stored with PBKDF2SHA1, bcrypt_, SHA1_, etc. The next few
+sections describe a couple of common ways advanced users may want to modify this
+setting.
+
+.. _bcrypt_usage:
+
+Using bcrypt with Django
+------------------------
+
+Bcrypt_ is a popular password storage algorithm that's specifically designed
+for long-term password storage. It's not the default used by Django since it
+requires the use of third-party libraries, but since many people may want to
+use it Django supports bcrypt with minimal effort.
+
+To use Bcrypt as your default storage algorithm, do the following:
+
+1. Install the `py-bcrypt`_ library (probably by running ``sudo pip install
+ py-bcrypt``, or downloading the library and installing it with ``python
+ setup.py install``).
+
+2. Modify :setting:`PASSWORD_HASHERS` to list ``BCryptSHA256PasswordHasher``
+ first. That is, in your settings file, you'd put::
+
+ PASSWORD_HASHERS = (
+ 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
+ 'django.contrib.auth.hashers.BCryptPasswordHasher',
+ 'django.contrib.auth.hashers.PBKDF2PasswordHasher',
+ 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
+ 'django.contrib.auth.hashers.SHA1PasswordHasher',
+ 'django.contrib.auth.hashers.MD5PasswordHasher',
+ 'django.contrib.auth.hashers.CryptPasswordHasher',
+ )
+
+ (You need to keep the other entries in this list, or else Django won't
+ be able to upgrade passwords; see below).
+
+That's it -- now your Django install will use Bcrypt as the default storage
+algorithm.
+
+.. admonition:: Password truncation with BCryptPasswordHasher
+
+ The designers of bcrypt truncate all passwords at 72 characters which means
+ that ``bcrypt(password_with_100_chars) == bcrypt(password_with_100_chars[:72])``.
+ The original ``BCryptPasswordHasher`` does not have any special handling and
+ thus is also subject to this hidden password length limit.
+ ``BCryptSHA256PasswordHasher`` fixes this by first first hashing the
+ password using sha256. This prevents the password truncation and so should
+ be preferred over the ``BCryptPasswordHasher``. The practical ramification
+ of this truncation is pretty marginal as the average user does not have a
+ password greater than 72 characters in length and even being truncated at 72
+ the compute powered required to brute force bcrypt in any useful amount of
+ time is still astronomical. Nonetheless, we recommend you use
+ ``BCryptSHA256PasswordHasher`` anyway on the principle of "better safe than
+ sorry".
+
+.. admonition:: Other bcrypt implementations
+
+ There are several other implementations that allow bcrypt to be
+ used with Django. Django's bcrypt support is NOT directly
+ compatible with these. To upgrade, you will need to modify the
+ hashes in your database to be in the form ``bcrypt$(raw bcrypt
+ output)``. For example:
+ ``bcrypt$$2a$12$NT0I31Sa7ihGEWpka9ASYrEFkhuTNeBQ2xfZskIiiJeyFXhRgS.Sy``.
+
+Increasing the work factor
+--------------------------
+
+The PBKDF2 and bcrypt algorithms use a number of iterations or rounds of
+hashing. This deliberately slows down attackers, making attacks against hashed
+passwords harder. However, as computing power increases, the number of
+iterations needs to be increased. We've chosen a reasonable default (and will
+increase it with each release of Django), but you may wish to tune it up or
+down, depending on your security needs and available processing power. To do so,
+you'll subclass the appropriate algorithm and override the ``iterations``
+parameters. For example, to increase the number of iterations used by the
+default PBKDF2 algorithm:
+
+1. Create a subclass of ``django.contrib.auth.hashers.PBKDF2PasswordHasher``::
+
+ from django.contrib.auth.hashers import PBKDF2PasswordHasher
+
+ class MyPBKDF2PasswordHasher(PBKDF2PasswordHasher):
+ """
+ A subclass of PBKDF2PasswordHasher that uses 100 times more iterations.
+ """
+ iterations = PBKDF2PasswordHasher.iterations * 100
+
+ Save this somewhere in your project. For example, you might put this in
+ a file like ``myproject/hashers.py``.
+
+2. Add your new hasher as the first entry in :setting:`PASSWORD_HASHERS`::
+
+ PASSWORD_HASHERS = (
+ 'myproject.hashers.MyPBKDF2PasswordHasher',
+ 'django.contrib.auth.hashers.PBKDF2PasswordHasher',
+ 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
+ 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
+ 'django.contrib.auth.hashers.BCryptPasswordHasher',
+ 'django.contrib.auth.hashers.SHA1PasswordHasher',
+ 'django.contrib.auth.hashers.MD5PasswordHasher',
+ 'django.contrib.auth.hashers.CryptPasswordHasher',
+ )
+
+
+That's it -- now your Django install will use more iterations when it
+stores passwords using PBKDF2.
+
+Password upgrading
+------------------
+
+When users log in, if their passwords are stored with anything other than
+the preferred algorithm, Django will automatically upgrade the algorithm
+to the preferred one. This means that old installs of Django will get
+automatically more secure as users log in, and it also means that you
+can switch to new (and better) storage algorithms as they get invented.
+
+However, Django can only upgrade passwords that use algorithms mentioned in
+:setting:`PASSWORD_HASHERS`, so as you upgrade to new systems you should make
+sure never to *remove* entries from this list. If you do, users using un-
+mentioned algorithms won't be able to upgrade.
+
+.. _sha1: http://en.wikipedia.org/wiki/SHA1
+.. _pbkdf2: http://en.wikipedia.org/wiki/PBKDF2
+.. _nist: http://csrc.nist.gov/publications/nistpubs/800-132/nist-sp800-132.pdf
+.. _bcrypt: http://en.wikipedia.org/wiki/Bcrypt
+.. _py-bcrypt: http://pypi.python.org/pypi/py-bcrypt/
+
+
+Manually managing a user's password
+===================================
+
+.. module:: django.contrib.auth.hashers
+
+The :mod:`django.contrib.auth.hashers` module provides a set of functions
+to create and validate hashed password. You can use them independently
+from the ``User`` model.
+
+.. function:: check_password(password, encoded)
+
+ If you'd like to manually authenticate a user by comparing a plain-text
+ password to the hashed password in the database, use the convenience
+ function :func:`check_password`. It takes two arguments: the plain-text
+ password to check, and the full value of a user's ``password`` field in the
+ database to check against, and returns ``True`` if they match, ``False``
+ otherwise.
+
+.. function:: make_password(password[, salt, hashers])
+
+ Creates a hashed password in the format used by this application. It takes
+ one mandatory argument: the password in plain-text. Optionally, you can
+ provide a salt and a hashing algorithm to use, if you don't want to use the
+ defaults (first entry of ``PASSWORD_HASHERS`` setting).
+ Currently supported algorithms are: ``'pbkdf2_sha256'``, ``'pbkdf2_sha1'``,
+ ``'bcrypt_sha256'`` (see :ref:`bcrypt_usage`), ``'bcrypt'``, ``'sha1'``,
+ ``'md5'``, ``'unsalted_md5'`` (only for backward compatibility) and ``'crypt'``
+ if you have the ``crypt`` library installed. If the password argument is
+ ``None``, an unusable password is returned (a one that will be never
+ accepted by :func:`check_password`).
+
+.. function:: is_password_usable(encoded_password)
+
+ Checks if the given string is a hashed password that has a chance
+ of being verified against :func:`check_password`.
diff --git a/docs/topics/cache.txt b/docs/topics/cache.txt
index a15cf58370..6b6d57511a 100644
--- a/docs/topics/cache.txt
+++ b/docs/topics/cache.txt
@@ -137,7 +137,7 @@ on the IP addresses 172.19.26.240 (port 11211), 172.19.26.242 (port 11212), and
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': [
'172.19.26.240:11211',
- '172.19.26.242:11211',
+ '172.19.26.242:11212',
'172.19.26.244:11213',
]
}
@@ -482,8 +482,6 @@ include the name of the active :term:`language<language code>` -- see also
:ref:`how-django-discovers-language-preference`). This allows you to easily
cache multilingual sites without having to create the cache key yourself.
-.. versionchanged:: 1.4
-
Cache keys also include the active :term:`language <language code>` when
:setting:`USE_L10N` is set to ``True`` and the :ref:`current time zone
<default-current-time-zone>` when :setting:`USE_TZ` is set to ``True``.
@@ -641,6 +639,23 @@ equivalent:
This feature is useful in avoiding repetition in templates. You can set the
timeout in a variable, in one place, and just reuse that value.
+.. function:: django.core.cache.utils.make_template_fragment_key(fragment_name, vary_on=None)
+
+If you want to obtain the cache key used for a cached fragment, you can use
+``make_template_fragment_key``. ``fragment_name`` is the same as second argument
+to the ``cache`` template tag; ``vary_on`` is a list of all additional arguments
+passed to the tag. This function can be useful for invalidating or overwriting
+a cached item, for example:
+
+.. code-block:: python
+
+ >>> from django.core.cache import cache
+ >>> from django.core.cache.utils import make_template_fragment_key
+ # cache key for {% cache 500 sidebar username %}
+ >>> key = make_template_fragment_key('sidebar', [username])
+ >>> cache.delete(key) # invalidates cached template fragment
+
+
The low-level cache API
=======================
@@ -666,6 +681,8 @@ pickling.)
Accessing the cache
-------------------
+.. function:: django.core.cache.get_cache(backend, **kwargs)
+
The cache module, ``django.core.cache``, has a ``cache`` object that's
automatically created from the ``'default'`` entry in the :setting:`CACHES`
setting::
@@ -678,7 +695,7 @@ If you have multiple caches defined in :setting:`CACHES`, then you can use
>>> from django.core.cache import get_cache
>>> cache = get_cache('alternate')
-If the named key does not exist, :exc:`InvalidCacheBackendError` will be raised.
+If the named key does not exist, ``InvalidCacheBackendError`` will be raised.
Basic usage
@@ -846,7 +863,7 @@ key version to set or get. For example::
'hello world!'
The version of a specific key can be incremented and decremented using
-the :func:`incr_version()` and :func:`decr_version()` methods. This
+the ``incr_version()`` and ``decr_version()`` methods. This
enables specific keys to be bumped to a new version, leaving other
keys unaffected. Continuing our previous example::
@@ -881,7 +898,7 @@ parts), you can provide a custom key function.
The :setting:`KEY_FUNCTION <CACHES-KEY_FUNCTION>` cache setting
specifies a dotted-path to a function matching the prototype of
-:func:`make_key()` above. If provided, this custom key function will
+``make_key()`` above. If provided, this custom key function will
be used instead of the default key combining function.
Cache key warnings
diff --git a/docs/topics/class-based-views/generic-display.txt b/docs/topics/class-based-views/generic-display.txt
index 10279c0f63..64b998770f 100644
--- a/docs/topics/class-based-views/generic-display.txt
+++ b/docs/topics/class-based-views/generic-display.txt
@@ -110,7 +110,7 @@ Now we need to define a view::
Finally hook that view into your urls::
# urls.py
- from django.conf.urls import patterns, url, include
+ from django.conf.urls import patterns, url
from books.views import PublisherList
urlpatterns = patterns('',
@@ -172,7 +172,7 @@ 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
+If 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::
@@ -188,6 +188,8 @@ Providing a useful ``context_object_name`` is always a good idea. Your
coworkers who design templates will thank you.
+.. _adding-extra-context:
+
Adding extra context
--------------------
@@ -228,7 +230,7 @@ more::
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
+ to super), any children of that class will also need to explicitly 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.
@@ -257,9 +259,9 @@ Specifying ``model = Publisher`` is really just shorthand for saying
``queryset = Publisher.objects.all()``. However, by using ``queryset``
to define a filtered list of objects you can be more specific about the
objects that will be visible in the view (see :doc:`/topics/db/queries`
-for more information about :class:`QuerySet` objects, and see the
-:doc:`class-based views reference </ref/class-based-views/index>` for the
-complete details).
+for more information about :class:`~django.db.models.query.QuerySet` objects,
+and see the :doc:`class-based views reference </ref/class-based-views/index>`
+for the complete details).
To pick a simple example, we might want to order a list of books by
publication date, with the most recent first::
@@ -312,9 +314,9 @@ what if we wanted to write a view that displayed all the books by some arbitrary
publisher?
Handily, the ``ListView`` has a
-:meth:`~django.views.generic.detail.ListView.get_queryset` method we can
-override. Previously, it has just been returning the value of the ``queryset``
-attribute, but now we can add more logic.
+:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset` method we
+can override. Previously, it has just been returning the value of the
+``queryset`` attribute, but now we can add more logic.
The key part to making this work is that when class-based views are called,
various useful things are stored on ``self``; as well as the request
diff --git a/docs/topics/class-based-views/generic-editing.txt b/docs/topics/class-based-views/generic-editing.txt
index 7d12184705..8cd34f8ad9 100644
--- a/docs/topics/class-based-views/generic-editing.txt
+++ b/docs/topics/class-based-views/generic-editing.txt
@@ -7,10 +7,10 @@ Form processing generally has 3 paths:
* POST with invalid data (typically redisplay form with errors)
* POST with valid data (process the data and typically redirect)
-Implementing this yourself often results in a lot of repeated
-boilerplate code (see :ref:`Using a form in a
-view<using-a-form-in-a-view>`). To help avoid this, Django provides a
-collection of generic class-based views for form processing.
+Implementing this yourself often results in a lot of repeated boilerplate code
+(see :ref:`Using a form in a view<using-a-form-in-a-view>`). To help avoid
+this, Django provides a collection of generic class-based views for form
+processing.
Basic Forms
-----------
@@ -28,7 +28,7 @@ Given a simple contact form::
# send email using the self.cleaned_data dictionary
pass
-The view can be constructed using a FormView::
+The view can be constructed using a ``FormView``::
# views.py
from myapp.forms import ContactForm
@@ -50,42 +50,46 @@ Notes:
* FormView inherits
:class:`~django.views.generic.base.TemplateResponseMixin` so
:attr:`~django.views.generic.base.TemplateResponseMixin.template_name`
- can be used here
+ can be used here.
* The default implementation for
- :meth:`~django.views.generic.edit.FormView.form_valid` simply
- redirects to the :attr:`success_url`
+ :meth:`~django.views.generic.edit.FormMixin.form_valid` simply
+ redirects to the :attr:`~django.views.generic.edit.FormMixin.success_url`.
Model Forms
-----------
Generic views really shine when working with models. These generic
-views will automatically create a :class:`ModelForm`, so long as they
-can work out which model class to use:
+views will automatically create a :class:`~django.forms.ModelForm`, so long as
+they can work out which model class to use:
-* If the :attr:`model` attribute is given, that model class will be used
-* If :meth:`get_object()` returns an object, the class of that object
- will be used
-* If a :attr:`queryset` is given, the model for that queryset will be used
+* If the :attr:`~django.views.generic.edit.ModelFormMixin.model` attribute is
+ given, that model class will be used.
+* If :meth:`~django.views.generic.detail.SingleObjectMixin.get_object()`
+ returns an object, the class of that object will be used.
+* If a :attr:`~django.views.generic.detail.SingleObjectMixin.queryset` is
+ given, the model for that queryset will be used.
-Model form views provide a :meth:`form_valid()` implementation that
-saves the model automatically. You can override this if you have any
+Model form views provide a
+:meth:`~django.views.generic.edit.ModelFormMixin.form_valid()` implementation
+that saves the model automatically. You can override this if you have any
special requirements; see below for examples.
-You don't even need to provide a attr:`success_url` for
+You don't even need to provide a ``success_url`` for
:class:`~django.views.generic.edit.CreateView` or
:class:`~django.views.generic.edit.UpdateView` - they will use
-:meth:`get_absolute_url()` on the model object if available.
+:meth:`~django.db.models.Model.get_absolute_url()` on the model object if available.
-If you want to use a custom :class:`ModelForm` (for instance to add
-extra validation) simply set
+If you want to use a custom :class:`~django.forms.ModelForm` (for instance to
+add extra validation) simply set
:attr:`~django.views.generic.edit.FormMixin.form_class` on your view.
.. note::
When specifying a custom form class, you must still specify the model,
- even though the :attr:`form_class` may be a :class:`ModelForm`.
+ even though the :attr:`~django.views.generic.edit.FormMixin.form_class` may
+ be a :class:`~django.forms.ModelForm`.
-First we need to add :meth:`get_absolute_url()` to our :class:`Author`
-class:
+First we need to add :meth:`~django.db.models.Model.get_absolute_url()` to our
+``Author`` class:
.. code-block:: python
@@ -137,8 +141,10 @@ Finally, we hook these new views into the URLconf::
.. note::
- These views inherit :class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`
- which uses :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_prefix`
+ These views inherit
+ :class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`
+ which uses
+ :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_suffix`
to construct the
:attr:`~django.views.generic.base.TemplateResponseMixin.template_name`
based on the model.
@@ -149,15 +155,17 @@ Finally, we hook these new views into the URLconf::
* :class:`DeleteView` uses ``myapp/author_confirm_delete.html``
If you wish to have separate templates for :class:`CreateView` and
- :class:1UpdateView`, you can set either :attr:`template_name` or
- :attr:`template_name_suffix` on your view class.
+ :class:`UpdateView`, you can set either
+ :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` or
+ :attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_suffix`
+ on your view class.
Models and request.user
-----------------------
To track the user that created an object using a :class:`CreateView`,
-you can use a custom :class:`ModelForm` to do this. First, add the
-foreign key relation to the model::
+you can use a custom :class:`~django.forms.ModelForm` to do this. First, add
+the foreign key relation to the model::
# models.py
from django.contrib.auth import User
@@ -169,7 +177,7 @@ foreign key relation to the model::
# ...
-Create a custom :class:`ModelForm` in order to exclude the
+Create a custom :class:`~django.forms.ModelForm` in order to exclude the
``created_by`` field and prevent the user from editing it:
.. code-block:: python
@@ -183,8 +191,10 @@ Create a custom :class:`ModelForm` in order to exclude the
model = Author
exclude = ('created_by',)
-In the view, use the custom :attr:`form_class` and override
-:meth:`form_valid()` to add the user::
+In the view, use the custom
+:attr:`~django.views.generic.edit.FormMixin.form_class` and override
+:meth:`~django.views.generic.edit.ModelFormMixin.form_valid()` to add the
+user::
# views.py
from django.views.generic.edit import CreateView
@@ -202,7 +212,8 @@ In the view, use the custom :attr:`form_class` and override
Note that you'll need to :ref:`decorate this
view<decorating-class-based-views>` using
:func:`~django.contrib.auth.decorators.login_required`, or
-alternatively handle unauthorised users in the :meth:`form_valid()`.
+alternatively handle unauthorized users in the
+:meth:`~django.views.generic.edit.ModelFormMixin.form_valid()`.
AJAX example
------------
@@ -214,7 +225,6 @@ works for AJAX requests as well as 'normal' form POSTs::
from django.http import HttpResponse
from django.views.generic.edit import CreateView
- from django.views.generic.detail import SingleObjectTemplateResponseMixin
class AjaxableResponseMixin(object):
"""
diff --git a/docs/topics/class-based-views/index.txt b/docs/topics/class-based-views/index.txt
index 54d4b0f252..b2fa93e05f 100644
--- a/docs/topics/class-based-views/index.txt
+++ b/docs/topics/class-based-views/index.txt
@@ -14,6 +14,7 @@ reusable views which suits your use case. For full details, see the
.. toctree::
:maxdepth: 1
+ intro
generic-display
generic-editing
mixins
@@ -37,7 +38,7 @@ URLconf. If you're only changing a few simple attributes on a class-based view,
you can simply pass them into the
:meth:`~django.views.generic.base.View.as_view` method call itself::
- from django.conf.urls import patterns, url, include
+ from django.conf.urls import patterns
from django.views.generic import TemplateView
urlpatterns = patterns('',
@@ -73,7 +74,7 @@ point the URL to the :meth:`~django.views.generic.base.View.as_view` class
method instead, which provides a function-like entry to class-based views::
# urls.py
- from django.conf.urls import patterns, url, include
+ from django.conf.urls import patterns
from some_app.views import AboutView
urlpatterns = patterns('',
@@ -127,68 +128,3 @@ the client issues a ``HEAD`` request, the response has an empty body and
the ``Last-Modified`` header indicates when the most recent book was published.
Based on this information, the client may or may not download the full object
list.
-
-Decorating class-based views
-============================
-
-.. highlightlang:: python
-
-Since class-based views aren't functions, decorating them works differently
-depending on if you're using ``as_view`` or creating a subclass.
-
-Decorating in URLconf
----------------------
-
-The simplest way of decorating class-based views is to decorate the
-result of the :meth:`~django.views.generic.base.View.as_view` method.
-The easiest place to do this is in the URLconf where you deploy your view::
-
- from django.contrib.auth.decorators import login_required, permission_required
- from django.views.generic import TemplateView
-
- from .views import VoteView
-
- urlpatterns = patterns('',
- (r'^about/', login_required(TemplateView.as_view(template_name="secret.html"))),
- (r'^vote/', permission_required('polls.can_vote')(VoteView.as_view())),
- )
-
-This approach applies the decorator on a per-instance basis. If you
-want every instance of a view to be decorated, you need to take a
-different approach.
-
-.. _decorating-class-based-views:
-
-Decorating the class
---------------------
-
-To decorate every instance of a class-based view, you need to decorate
-the class definition itself. To do this you apply the decorator to the
-:meth:`~django.views.generic.base.View.dispatch` method of the class.
-
-A method on a class isn't quite the same as a standalone function, so
-you can't just apply a function decorator to the method -- you need to
-transform it into a method decorator first. The ``method_decorator``
-decorator transforms a function decorator into a method decorator so
-that it can be used on an instance method. For example::
-
- from django.contrib.auth.decorators import login_required
- from django.utils.decorators import method_decorator
- from django.views.generic import TemplateView
-
- class ProtectedView(TemplateView):
- template_name = 'secret.html'
-
- @method_decorator(login_required)
- def dispatch(self, *args, **kwargs):
- return super(ProtectedView, self).dispatch(*args, **kwargs)
-
-In this example, every instance of ``ProtectedView`` will have
-login protection.
-
-.. note::
-
- ``method_decorator`` passes ``*args`` and ``**kwargs``
- as parameters to the decorated method on the class. If your method
- does not accept a compatible set of parameters it will raise a
- ``TypeError`` exception.
diff --git a/docs/topics/class-based-views/intro.txt b/docs/topics/class-based-views/intro.txt
new file mode 100644
index 0000000000..dbbbea25f0
--- /dev/null
+++ b/docs/topics/class-based-views/intro.txt
@@ -0,0 +1,289 @@
+=================================
+Introduction to Class-based views
+=================================
+
+Class-based views provide an alternative way to implement views as Python
+objects instead of functions. They do not replace function-based views, but
+have certain differences and advantages when compared to function-based views:
+
+* Organization of code related to specific HTTP methods (``GET``, ``POST``,
+ etc) can be addressed by separate methods instead of conditional branching.
+
+* Object oriented techniques such as mixins (multiple inheritance) can be
+ used to factor code into reusable components.
+
+The relationship and history of generic views, class-based views, and class-based generic views
+===============================================================================================
+
+In the beginning there was only the view function contract, Django passed your
+function an :class:`~django.http.HttpRequest` and expected back an
+:class:`~django.http.HttpResponse`. This was the extent of what Django provided.
+
+Early on it was recognized that there were common idioms and patterns found in
+view development. Function-based generic views were introduced to abstract
+these patterns and ease view development for the common cases.
+
+The problem with function-based generic views is that while they covered the
+simple cases well, there was no way to extend or customize them beyond some
+simple configuration options, limiting their usefulness in many real-world
+applications.
+
+Class-based generic views were created with the same objective as
+function-based generic views, to make view development easier. However, the way
+the solution is implemented, through the use of mixins, provides a toolkit that
+results in class-based generic views being more extensible and flexible than
+their function-based counterparts.
+
+If you have tried function based generic views in the past and found them
+lacking, you should not think of class-based generic views as simply a
+class-based equivalent, but rather as a fresh approach to solving the original
+problems that generic views were meant to solve.
+
+The toolkit of base classes and mixins that Django uses to build class-based
+generic views are built for maximum flexibility, and as such have many hooks in
+the form of default method implementations and attributes that you are unlikely
+to be concerned with in the simplest use cases. For example, instead of
+limiting you to a class based attribute for ``form_class``, the implementation
+uses a ``get_form`` method, which calls a ``get_form_class`` method, which in
+its default implementation just returns the ``form_class`` attribute of the
+class. This gives you several options for specifying what form to use, from a
+simple attribute, to a fully dynamic, callable hook. These options seem to add
+hollow complexity for simple situations, but without them, more advanced
+designs would be limited.
+
+Using class-based views
+=======================
+
+At its core, a class-based view allows you to respond to different HTTP request
+methods with different class instance methods, instead of with conditionally
+branching code inside a single view function.
+
+So where the code to handle HTTP ``GET`` in a view function would look
+something like::
+
+ from django.http import HttpResponse
+
+ def my_view(request):
+ if request.method == 'GET':
+ # <view logic>
+ return HttpResponse('result')
+
+In a class-based view, this would become::
+
+ from django.http import HttpResponse
+ from django.views.generic.base import View
+
+ class MyView(View):
+ def get(self, request):
+ # <view logic>
+ return HttpResponse('result')
+
+Because Django's URL resolver expects to send the request and associated
+arguments to a callable function, not a class, class-based views have an
+:meth:`~django.views.generic.base.View.as_view` class method which serves as
+the callable entry point to your class. The ``as_view`` entry point creates an
+instance of your class and calls its
+:meth:`~django.views.generic.base.View.dispatch` method. ``dispatch`` looks at
+the request to determine whether it is a ``GET``, ``POST``, etc, and relays the
+request to a matching method if one is defined, or raises
+:class:`~django.http.HttpResponseNotAllowed` if not::
+
+ # urls.py
+ from django.conf.urls import patterns
+ from myapp.views import MyView
+
+ urlpatterns = patterns('',
+ (r'^about/', MyView.as_view()),
+ )
+
+
+It is worth noting that what your method returns is identical to what you
+return from a function-based view, namely some form of
+:class:`~django.http.HttpResponse`. This means that
+:doc:`http shortcuts </topics/http/shortcuts>` or
+:class:`~django.template.response.TemplateResponse` objects are valid to use
+inside a class-based view.
+
+While a minimal class-based view does not require any class attributes to
+perform its job, class attributes are useful in many class-based designs,
+and there are two ways to configure or set class attributes.
+
+The first is the standard Python way of subclassing and overriding attributes
+and methods in the subclass. So that if your parent class had an attribute
+``greeting`` like this::
+
+ from django.http import HttpResponse
+ from django.views.generic.base import View
+
+ class GreetingView(View):
+ greeting = "Good Day"
+
+ def get(self, request):
+ return HttpResponse(self.greeting)
+
+You can override that in a subclass::
+
+ class MorningGreetingView(GreetingView):
+ greeting = "Morning to ya"
+
+Another option is to configure class attributes as keyword arguments to the
+:meth:`~django.views.generic.base.View.as_view` call in the URLconf::
+
+ urlpatterns = patterns('',
+ (r'^about/', GreetingView.as_view(greeting="G'day")),
+ )
+
+.. note::
+
+ While your class is instantiated for each request dispatched to it, class
+ attributes set through the
+ :meth:`~django.views.generic.base.View.as_view` entry point are
+ configured only once at the time your URLs are imported.
+
+Using mixins
+============
+
+Mixins are a form of multiple inheritance where behaviors and attributes of
+multiple parent classes can be combined.
+
+For example, in the generic class-based views there is a mixin called
+:class:`~django.views.generic.base.TemplateResponseMixin` whose primary purpose
+is to define the method
+:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`.
+When combined with the behavior of the :class:`~django.views.generic.base.View`
+base class, the result is a :class:`~django.views.generic.base.TemplateView`
+class that will dispatch requests to the appropriate matching methods (a
+behavior defined in the ``View`` base class), and that has a
+:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
+method that uses a
+:attr:`~django.views.generic.base.TemplateResponseMixin.template_name`
+attribute to return a :class:`~django.template.response.TemplateResponse`
+object (a behavior defined in the ``TemplateResponseMixin``).
+
+Mixins are an excellent way of reusing code across multiple classes, but they
+come with some cost. The more your code is scattered among mixins, the harder
+it will be to read a child class and know what exactly it is doing, and the
+harder it will be to know which methods from which mixins to override if you
+are subclassing something that has a deep inheritance tree.
+
+Note also that you can only inherit from one generic view - that is, only one
+parent class may inherit from :class:`~django.views.generic.base.View` and
+the rest (if any) should be mixins. Trying to inherit from more than one class
+that inherits from ``View`` - for example, trying to use a form at the top of a
+list and combining :class:`~django.views.generic.edit.ProcessFormView` and
+:class:`~django.views.generic.list.ListView` - won't work as expected.
+
+Handling forms with class-based views
+=====================================
+
+A basic function-based view that handles forms may look something like this::
+
+ from django.http import HttpResponseRedirect
+ from django.shortcuts import render
+
+ from .forms import MyForm
+
+ def myview(request):
+ if request.method == "POST":
+ form = MyForm(request.POST)
+ if form.is_valid():
+ # <process form cleaned data>
+ return HttpResponseRedirect('/success/')
+ else:
+ form = MyForm(initial={'key': 'value'})
+
+ return render(request, 'form_template.html', {'form': form})
+
+A similar class-based view might look like::
+
+ from django.http import HttpResponseRedirect
+ from django.shortcuts import render
+
+ from .forms import MyForm
+
+ class MyFormView(View):
+ form_class = MyForm
+ initial = {'key': 'value'}
+ template_name = 'form_template.html'
+
+ def get(self, request, *args, **kwargs):
+ form = self.form_class(initial=self.initial)
+ return render(request, self.template_name, {'form': form})
+
+ def post(self, request, *args, **kwargs):
+ form = self.form_class(request.POST)
+ if form.is_valid():
+ # <process form cleaned data>
+ return HttpResponseRedirect('/success/')
+
+ return render(request, self.template_name, {'form': form})
+
+This is a very simple case, but you can see that you would then have the option
+of customizing this view by overriding any of the class attributes, e.g.
+``form_class``, via URLconf configuration, or subclassing and overriding one or
+more of the methods (or both!).
+
+Decorating class-based views
+============================
+
+The extension of class-based views isn't limited to using mixins. You
+can use also use decorators. Since class-based views aren't functions,
+decorating them works differently depending on if you're using ``as_view`` or
+creating a subclass.
+
+Decorating in URLconf
+---------------------
+
+The simplest way of decorating class-based views is to decorate the
+result of the :meth:`~django.views.generic.base.View.as_view` method.
+The easiest place to do this is in the URLconf where you deploy your view::
+
+ from django.contrib.auth.decorators import login_required, permission_required
+ from django.views.generic import TemplateView
+
+ from .views import VoteView
+
+ urlpatterns = patterns('',
+ (r'^about/', login_required(TemplateView.as_view(template_name="secret.html"))),
+ (r'^vote/', permission_required('polls.can_vote')(VoteView.as_view())),
+ )
+
+This approach applies the decorator on a per-instance basis. If you
+want every instance of a view to be decorated, you need to take a
+different approach.
+
+.. _decorating-class-based-views:
+
+Decorating the class
+--------------------
+
+To decorate every instance of a class-based view, you need to decorate
+the class definition itself. To do this you apply the decorator to the
+:meth:`~django.views.generic.base.View.dispatch` method of the class.
+
+A method on a class isn't quite the same as a standalone function, so
+you can't just apply a function decorator to the method -- you need to
+transform it into a method decorator first. The ``method_decorator``
+decorator transforms a function decorator into a method decorator so
+that it can be used on an instance method. For example::
+
+ from django.contrib.auth.decorators import login_required
+ from django.utils.decorators import method_decorator
+ from django.views.generic import TemplateView
+
+ class ProtectedView(TemplateView):
+ template_name = 'secret.html'
+
+ @method_decorator(login_required)
+ def dispatch(self, *args, **kwargs):
+ return super(ProtectedView, self).dispatch(*args, **kwargs)
+
+In this example, every instance of ``ProtectedView`` will have
+login protection.
+
+.. note::
+
+ ``method_decorator`` passes ``*args`` and ``**kwargs``
+ as parameters to the decorated method on the class. If your method
+ does not accept a compatible set of parameters it will raise a
+ ``TypeError`` exception.
diff --git a/docs/topics/class-based-views/mixins.txt b/docs/topics/class-based-views/mixins.txt
index f349c23626..9550d2fb86 100644
--- a/docs/topics/class-based-views/mixins.txt
+++ b/docs/topics/class-based-views/mixins.txt
@@ -13,7 +13,7 @@ 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
+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.
@@ -35,10 +35,10 @@ interface to working with templates in class-based views.
Every built in view which returns a
:class:`~django.template.response.TemplateResponse` will call the
:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
- method that :class:`TemplateResponseMixin` provides. Most of the time this
+ method that ``TemplateResponseMixin`` provides. Most of the time this
will be called for you (for instance, it is called by the ``get()`` method
implemented by both :class:`~django.views.generic.base.TemplateView` and
- :class:`~django.views.generic.base.DetailView`); similarly, it's unlikely
+ :class:`~django.views.generic.detail.DetailView`); similarly, it's unlikely
that you'll need to override it, although if you want your response to
return something not rendered via a Django template then you'll want to do
it. For an example of this, see the :ref:`JSONResponseMixin example
@@ -59,10 +59,10 @@ interface to working with templates in class-based views.
:class:`~django.views.generic.base.ContextMixin`
Every built in view which needs context data, such as for rendering a
- template (including :class:`TemplateResponseMixin` above), should call
+ template (including ``TemplateResponseMixin`` above), should call
:meth:`~django.views.generic.base.ContextMixin.get_context_data` passing
any data they want to ensure is in there as keyword arguments.
- ``get_context_data`` returns a dictionary; in :class:`ContextMixin` it
+ ``get_context_data`` returns a dictionary; in ``ContextMixin`` it
simply returns its keyword arguments, but it is common to override this to
add more members to the dictionary.
@@ -93,8 +93,8 @@ 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.
+:class:`~django.template.response.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`,
@@ -106,20 +106,21 @@ URLConf, and looks the object up either from the
:attr:`~django.views.generic.detail.SingleObjectMixin.model` attribute
on the view, or the
:attr:`~django.views.generic.detail.SingleObjectMixin.queryset`
-attribute if that's provided). :class:`SingleObjectMixin` also overrides
+attribute if that's provided). ``SingleObjectMixin`` also overrides
:meth:`~django.views.generic.base.ContextMixin.get_context_data`,
which is used across all Django's built in class-based views to supply
context data for template renders.
-To then make a :class:`TemplateResponse`, :class:`DetailView` uses
+To then make a :class:`~django.template.response.TemplateResponse`,
+:class:`DetailView` uses
:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`,
-which extends
-:class:`~django.views.generic.base.TemplateResponseMixin`, overriding
-:meth:`get_template_names()` as discussed above. It actually provides
-a fairly sophisticated set of options, but the main one that most
-people are going to use is
-``<app_label>/<object_name>_detail.html``. The ``_detail`` part can be
-changed by setting
+which extends :class:`~django.views.generic.base.TemplateResponseMixin`,
+overriding
+:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names()`
+as discussed above. It actually provides a fairly sophisticated set of options,
+but the main one that most people are going to use is
+``<app_label>/<object_name>_detail.html``. The ``_detail`` part can be changed
+by setting
:attr:`~django.views.generic.detail.SingleObjectTemplateResponseMixin.template_name_suffix`
on a subclass to something else. (For instance, the :doc:`generic edit
views<generic-editing>` use ``_form`` for create and update views, and
@@ -129,9 +130,10 @@ ListView: working with many Django objects
------------------------------------------
Lists of objects follow roughly the same pattern: we need a (possibly
-paginated) list of objects, typically a :class:`QuerySet`, and then we need
-to make a :class:`TemplateResponse` with a suitable template using
-that list of objects.
+paginated) list of objects, typically a
+:class:`~django.db.models.query.QuerySet`, and then we need to make a
+:class:`~django.template.response.TemplateResponse` with a suitable template
+using that list of objects.
To get the objects, :class:`~django.views.generic.list.ListView` uses
:class:`~django.views.generic.list.MultipleObjectMixin`, which
@@ -139,9 +141,9 @@ provides both
:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`
and
:meth:`~django.views.generic.list.MultipleObjectMixin.paginate_queryset`. Unlike
-with :class:`SingleObjectMixin`, there's no need to key off parts of
-the URL to figure out the queryset to work with, so the default just
-uses the
+with :class:`~django.views.generic.detail.SingleObjectMixin`, there's no need
+to key off parts of the URL to figure out the queryset to work with, so the
+default just uses the
:attr:`~django.views.generic.list.MultipleObjectMixin.queryset` or
:attr:`~django.views.generic.list.MultipleObjectMixin.model` attribute
on the view class. A common reason to override
@@ -149,19 +151,19 @@ on the view class. A common reason to override
here would be to dynamically vary the objects, such as depending on
the current user or to exclude posts in the future for a blog.
-:class:`MultipleObjectMixin` also overrides
+:class:`~django.views.generic.list.MultipleObjectMixin` also overrides
:meth:`~django.views.generic.base.ContextMixin.get_context_data` to
include appropriate context variables for pagination (providing
dummies if pagination is disabled). It relies on ``object_list`` being
passed in as a keyword argument, which :class:`ListView` arranges for
it.
-To make a :class:`TemplateResponse`, :class:`ListView` then uses
+To make a :class:`~django.template.response.TemplateResponse`,
+:class:`ListView` then uses
:class:`~django.views.generic.list.MultipleObjectTemplateResponseMixin`;
-as with :class:`SingleObjectTemplateResponseMixin` above, this
-overrides :meth:`get_template_names()` to provide :meth:`a range of
-options
-<~django.views.generic.list.MultipleObjectTempalteResponseMixin>`,
+as with :class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`
+above, this overrides ``get_template_names()`` to provide :meth:`a range of
+options <django.views.generic.list.MultipleObjectTemplateResponseMixin>`,
with the most commonly-used being
``<app_label>/<object_name>_list.html``, with the ``_list`` part again
being taken from the
@@ -198,13 +200,13 @@ the box.
If in doubt, it's often better to back off and base your work on
:class:`View` or :class:`TemplateView`, perhaps with
- :class:`SimpleObjectMixin` and
- :class:`MultipleObjectMixin`. Although you will probably end up
- writing more code, it is more likely to be clearly understandable
- to someone else coming to it later, and with fewer interactions to
- worry about you will save yourself some thinking. (Of course, you
- can always dip into Django's implementation of the generic class
- based views for inspiration on how to tackle problems.)
+ :class:`~django.views.generic.detail.SingleObjectMixin` and
+ :class:`~django.views.generic.list.MultipleObjectMixin`. Although you
+ will probably end up writing more code, it is more likely to be clearly
+ understandable to someone else coming to it later, and with fewer
+ interactions to worry about you will save yourself some thinking. (Of
+ course, you can always dip into Django's implementation of the generic
+ class based views for inspiration on how to tackle problems.)
.. _method resolution order: http://www.python.org/download/releases/2.3/mro/
@@ -248,9 +250,9 @@ We'll demonstrate this with the publisher modelling we used in the
In practice you'd probably want to record the interest in a key-value
store rather than in a relational database, so we've left that bit
out. The only bit of the view that needs to worry about using
-:class:`SingleObjectMixin` is where we want to look up the author
-we're interested in, which it just does with a simple call to
-``self.get_object()``. Everything else is taken care of for us by the
+:class:`~django.views.generic.detail.SingleObjectMixin` is where we want to
+look up the author we're interested in, which it just does with a simple call
+to ``self.get_object()``. Everything else is taken care of for us by the
mixin.
We can hook this into our URLs easily enough::
@@ -265,8 +267,9 @@ We can hook this into our URLs easily enough::
Note the ``pk`` named group, which
:meth:`~django.views.generic.detail.SingleObjectMixin.get_object` uses
-to look up the :class:`Author` instance. You could also use a slug, or
-any of the other features of :class:`SingleObjectMixin`.
+to look up the ``Author`` instance. You could also use a slug, or
+any of the other features of
+:class:`~django.views.generic.detail.SingleObjectMixin`.
Using SingleObjectMixin with ListView
-------------------------------------
@@ -278,28 +281,29 @@ example, you might want to paginate through all the books by a
particular publisher.
One way to do this is to combine :class:`ListView` with
-:class:`SingleObjectMixin`, so that the queryset for the paginated
-list of books can hang off the publisher found as the single
+:class:`~django.views.generic.detail.SingleObjectMixin`, so that the queryset
+for the paginated list of books can hang off the publisher found as the single
object. In order to do this, we need to have two different querysets:
**Publisher queryset for use in get_object**
- We'll set that up directly when we call :meth:`get_object()`.
+ We'll set that up directly when we call ``get_object()``.
**Book queryset for use by ListView**
- We'll figure that out ourselves in :meth:`get_queryset()` so we
- can take into account the Publisher we're looking at.
+ We'll figure that out ourselves in ``get_queryset()`` so we
+ can take into account the ``Publisher`` we're looking at.
.. note::
- We have to think carefully about :meth:`get_context_data()`.
- Since both :class:`SingleObjectMixin` and :class:`ListView` will
+ We have to think carefully about ``get_context_data()``.
+ Since both :class:`~django.views.generic.detail.SingleObjectMixin` and
+ :class:`ListView` will
put things in the context data under the value of
- :attr:`context_object_name` if it's set, we'll instead explictly
+ ``context_object_name`` if it's set, we'll instead explictly
ensure the Publisher is in the context data. :class:`ListView`
will add in the suitable ``page_obj`` and ``paginator`` for us
providing we remember to call ``super()``.
-Now we can write a new :class:`PublisherDetail`::
+Now we can write a new ``PublisherDetail``::
from django.views.generic import ListView
from django.views.generic.detail import SingleObjectMixin
@@ -317,13 +321,14 @@ Now we can write a new :class:`PublisherDetail`::
self.object = self.get_object(Publisher.objects.all())
return self.object.book_set.all()
-Notice how we set ``self.object`` within :meth:`get_queryset` so we
-can use it again later in :meth:`get_context_data`. If you don't set
-:attr:`template_name`, the template will default to the normal
+Notice how we set ``self.object`` within ``get_queryset()`` so we
+can use it again later in ``get_context_data()``. If you don't set
+``template_name``, the template will default to the normal
:class:`ListView` choice, which in this case would be
``"books/book_list.html"`` because it's a list of books;
-:class:`ListView` knows nothing about :class:`SingleObjectMixin`, so
-it doesn't have any clue this view is anything to do with a Publisher.
+:class:`ListView` knows nothing about
+:class:`~django.views.generic.detail.SingleObjectMixin`, so it doesn't have
+any clue this view is anything to do with a Publisher.
.. highlightlang:: html+django
@@ -366,7 +371,7 @@ Generally you can use
:class:`~django.views.generic.base.TemplateResponseMixin` and
:class:`~django.views.generic.detail.SingleObjectMixin` when you need
their functionality. As shown above, with a bit of care you can even
-combine :class:`SingleObjectMixin` with
+combine ``SingleObjectMixin`` with
:class:`~django.views.generic.list.ListView`. However things get
increasingly complex as you try to do so, and a good rule of thumb is:
@@ -377,53 +382,53 @@ increasingly complex as you try to do so, and a good rule of thumb is:
list<generic-display>`, :doc:`editing<generic-editing>` and
date. For example it's fine to combine
:class:`TemplateView` (built in view) with
- :class:`MultipleObjectMixin` (generic list), but you're likely to
- have problems combining :class:`SingleObjectMixin` (generic
- detail) with :class:`MultipleObjectMixin` (generic list).
+ :class:`~django.views.generic.list.MultipleObjectMixin` (generic list), but
+ you're likely to have problems combining ``SingleObjectMixin`` (generic
+ detail) with ``MultipleObjectMixin`` (generic list).
To show what happens when you try to get more sophisticated, we show
an example that sacrifices readability and maintainability when there
is a simpler solution. First, let's look at a naive attempt to combine
:class:`~django.views.generic.detail.DetailView` with
:class:`~django.views.generic.edit.FormMixin` to enable use to
-``POST`` a Django :class:`Form` to the same URL as we're displaying an
-object using :class:`DetailView`.
+``POST`` a Django :class:`~django.forms.Form` to the same URL as we're
+displaying an object using :class:`DetailView`.
Using FormMixin with DetailView
-------------------------------
Think back to our earlier example of using :class:`View` and
-:class:`SingleObjectMixin` together. We were recording a user's
-interest in a particular author; say now that we want to let them
-leave a message saying why they like them. Again, let's assume we're
+:class:`~django.views.generic.detail.SingleObjectMixin` together. We were
+recording a user's interest in a particular author; say now that we want to
+let them leave a message saying why they like them. Again, let's assume we're
not going to store this in a relational database but instead in
something more esoteric that we won't worry about here.
-At this point it's natural to reach for a :class:`Form` to encapsulate
-the information sent from the user's browser to Django. Say also that
-we're heavily invested in `REST`_, so we want to use the same URL for
+At this point it's natural to reach for a :class:`~django.forms.Form` to
+encapsulate the information sent from the user's browser to Django. Say also
+that we're heavily invested in `REST`_, so we want to use the same URL for
displaying the author as for capturing the message from the
-user. Let's rewrite our :class:`AuthorDetailView` to do that.
+user. Let's rewrite our ``AuthorDetailView`` to do that.
.. _REST: http://en.wikipedia.org/wiki/Representational_state_transfer
We'll keep the ``GET`` handling from :class:`DetailView`, although
-we'll have to add a :class:`Form` into the context data so we can
+we'll have to add a :class:`~django.forms.Form` into the context data so we can
render it in the template. We'll also want to pull in form processing
from :class:`~django.views.generic.edit.FormMixin`, and write a bit of
code so that on ``POST`` the form gets called appropriately.
.. note::
- We use :class:`FormMixin` and implement :meth:`post()` ourselves
- rather than try to mix :class:`DetailView` with :class:`FormView`
- (which provides a suitable :meth:`post()` already) because both of
- the views implement :meth:`get()`, and things would get much more
+ We use :class:`~django.views.generic.edit.FormMixin` and implement
+ ``post()`` ourselves rather than try to mix :class:`DetailView` with
+ :class:`FormView` (which provides a suitable ``post()`` already) because
+ both of the views implement ``get()``, and things would get much more
confusing.
.. highlightlang:: python
-Our new :class:`AuthorDetail` looks like this::
+Our new ``AuthorDetail`` looks like this::
# CAUTION: you almost certainly do not want to do this.
# It is provided as part of a discussion of problems you can
@@ -473,24 +478,24 @@ Our new :class:`AuthorDetail` looks like this::
# record the interest using the message in form.cleaned_data
return super(AuthorDetail, self).form_valid(form)
-:meth:`get_success_url()` is just providing somewhere to redirect to,
+``get_success_url()`` is just providing somewhere to redirect to,
which gets used in the default implementation of
-:meth:`form_valid()`. We have to provide our own :meth:`post()` as
-noted earlier, and override :meth:`get_context_data()` to make the
-:class:`Form` available in the context data.
+``form_valid()``. We have to provide our own ``post()`` as
+noted earlier, and override ``get_context_data()`` to make the
+:class:`~django.forms.Form` available in the context data.
A better solution
-----------------
It should be obvious that the number of subtle interactions between
-:class:`FormMixin` and :class:`DetailView` is already testing our
-ability to manage things. It's unlikely you'd want to write this kind
-of class yourself.
+:class:`~django.views.generic.edit.FormMixin` and :class:`DetailView` is
+already testing our ability to manage things. It's unlikely you'd want to
+write this kind of class yourself.
-In this case, it would be fairly easy to just write the :meth:`post()`
+In this case, it would be fairly easy to just write the ``post()``
method yourself, keeping :class:`DetailView` as the only generic
-functionality, although writing :class:`Form` handling code involves a
-lot of duplication.
+functionality, although writing :class:`~django.forms.Form` handling code
+involves a lot of duplication.
Alternatively, it would still be easier than the above approach to
have a separate view for processing the form, which could use
@@ -503,15 +508,15 @@ An alternative better solution
What we're really trying to do here is to use two different class
based views from the same URL. So why not do just that? We have a very
clear division here: ``GET`` requests should get the
-:class:`DetailView` (with the :class:`Form` added to the context
+:class:`DetailView` (with the :class:`~django.forms.Form` added to the context
data), and ``POST`` requests should get the :class:`FormView`. Let's
set up those views first.
-The :class:`AuthorDisplay` view is almost the same as :ref:`when we
+The ``AuthorDisplay`` view is almost the same as :ref:`when we
first introduced AuthorDetail<generic-views-extra-work>`; we have to
-write our own :meth:`get_context_data()` to make the
-:class:`AuthorInterestForm` available to the template. We'll skip the
-:meth:`get_object()` override from before for clarity.
+write our own ``get_context_data()`` to make the
+``AuthorInterestForm`` available to the template. We'll skip the
+``get_object()`` override from before for clarity.
.. code-block:: python
@@ -533,11 +538,11 @@ write our own :meth:`get_context_data()` to make the
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``.
+Then the ``AuthorInterest`` is a simple :class:`FormView`, but we
+have to bring in :class:`~django.views.generic.detail.SingleObjectMixin` so we
+can find the author we're talking about, and we have to remember to set
+``template_name`` to ensure that form errors will render the same
+template as ``AuthorDisplay`` is using on ``GET``.
.. code-block:: python
@@ -568,15 +573,15 @@ template as :class:`AuthorDisplay` is using on ``GET``.
# 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.
+Finally we bring this together in a new ``AuthorDetail`` view. We
+already know that calling :meth:`~django.views.generic.base.View.as_view()` on
+a class-based view gives us something that behaves exactly like a function
+based view, so we can do that at the point we choose between the two subviews.
-You can of course pass through keyword arguments to :meth:`as_view()`
-in the same way you would in your URLconf, such as if you wanted the
-:class:`AuthorInterest` behaviour to also appear at another URL but
-using a different template.
+You can of course pass through keyword arguments to
+:meth:`~django.views.generic.base.View.as_view()` in the same way you
+would in your URLconf, such as if you wanted the ``AuthorInterest`` behavior
+to also appear at another URL but using a different template.
.. code-block:: python
@@ -647,8 +652,8 @@ Now we mix this into the base TemplateView::
Equally we could use our mixin with one of the generic views. We can make our
own version of :class:`~django.views.generic.detail.DetailView` by mixing
-:class:`JSONResponseMixin` with the
-:class:`~django.views.generic.detail.BaseDetailView` -- (the
+``JSONResponseMixin`` with the
+``django.views.generic.detail.BaseDetailView`` -- (the
:class:`~django.views.generic.detail.DetailView` before template
rendering behavior has been mixed in)::
@@ -663,11 +668,12 @@ If you want to be really adventurous, you could even mix a
:class:`~django.views.generic.detail.DetailView` subclass that is able
to return *both* HTML and JSON content, depending on some property of
the HTTP request, such as a query argument or a HTTP header. Just mix
-in both the :class:`JSONResponseMixin` and a
+in both the ``JSONResponseMixin`` and a
:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`,
-and override the implementation of :func:`render_to_response()` to defer
-to the appropriate subclass depending on the type of response that the user
-requested::
+and override the implementation of
+:func:`~django.views.generic.base.TemplateResponseMixin.render_to_response()`
+to defer to the appropriate subclass depending on the type of response that the
+user requested::
class HybridDetailView(JSONResponseMixin, SingleObjectTemplateResponseMixin, BaseDetailView):
def render_to_response(self, context):
@@ -679,5 +685,5 @@ requested::
Because of the way that Python resolves method overloading, the local
``render_to_response()`` implementation will override the versions provided by
-:class:`JSONResponseMixin` and
+``JSONResponseMixin`` and
:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`.
diff --git a/docs/topics/conditional-view-processing.txt b/docs/topics/conditional-view-processing.txt
index 1979e89c31..caa7376189 100644
--- a/docs/topics/conditional-view-processing.txt
+++ b/docs/topics/conditional-view-processing.txt
@@ -27,7 +27,7 @@ instead of a full response, telling the client that nothing has changed.
When you need more fine-grained control you may use per-view conditional
processing functions.
-.. conditional-decorators:
+.. _conditional-decorators:
The ``condition`` decorator
===========================
diff --git a/docs/topics/db/aggregation.txt b/docs/topics/db/aggregation.txt
index 3a4d287864..125cd0bdee 100644
--- a/docs/topics/db/aggregation.txt
+++ b/docs/topics/db/aggregation.txt
@@ -21,14 +21,12 @@ used to track the inventory for a series of online bookstores:
class Author(models.Model):
name = models.CharField(max_length=100)
age = models.IntegerField()
- friends = models.ManyToManyField('self', blank=True)
class Publisher(models.Model):
name = models.CharField(max_length=300)
num_awards = models.IntegerField()
class Book(models.Model):
- isbn = models.CharField(max_length=9)
name = models.CharField(max_length=300)
pages = models.IntegerField()
price = models.DecimalField(max_digits=10, decimal_places=2)
@@ -40,11 +38,14 @@ used to track the inventory for a series of online bookstores:
class Store(models.Model):
name = models.CharField(max_length=300)
books = models.ManyToManyField(Book)
+ registered_users = models.PositiveIntegerField()
Cheat sheet
===========
-In a hurry? Here's how to do common aggregate queries, assuming the models above::
+In a hurry? Here's how to do common aggregate queries, assuming the models above:
+
+.. code-block:: python
# Total number of books.
>>> Book.objects.count()
@@ -64,6 +65,9 @@ In a hurry? Here's how to do common aggregate queries, assuming the models above
>>> Book.objects.all().aggregate(Max('price'))
{'price__max': Decimal('81.20')}
+ # All the following queries involve traversing the Book<->Publisher
+ # many-to-many relationship backward
+
# Each publisher, each with a count of books as a "num_books" attribute.
>>> from django.db.models import Count
>>> pubs = Publisher.objects.annotate(num_books=Count('book'))
@@ -73,7 +77,6 @@ In a hurry? Here's how to do common aggregate queries, assuming the models above
73
# The top 5 publishers, in order by number of books.
- >>> from django.db.models import Count
>>> pubs = Publisher.objects.annotate(num_books=Count('book')).order_by('-num_books')[:5]
>>> pubs[0].num_books
1323
@@ -139,8 +142,10 @@ will be annotated with the specified values.
The syntax for these annotations is identical to that used for the
``aggregate()`` clause. Each argument to ``annotate()`` describes an
-aggregate that is to be calculated. For example, to annotate Books with
-the number of authors::
+aggregate that is to be calculated. For example, to annotate books with
+the number of authors:
+
+.. code-block:: python
# Build an annotated queryset
>>> q = Book.objects.annotate(Count('authors'))
@@ -169,7 +174,7 @@ specify the annotation::
Unlike ``aggregate()``, ``annotate()`` is *not* a terminal clause. The output
of the ``annotate()`` clause is a ``QuerySet``; this ``QuerySet`` can be
modified using any other ``QuerySet`` operation, including ``filter()``,
-``order_by``, or even additional calls to ``annotate()``.
+``order_by()``, or even additional calls to ``annotate()``.
Joins and aggregates
====================
@@ -189,8 +194,8 @@ you could use the annotation::
>>> Store.objects.annotate(min_price=Min('books__price'), max_price=Max('books__price'))
-This tells Django to retrieve the Store model, join (through the
-many-to-many relationship) with the Book model, and aggregate on the
+This tells Django to retrieve the ``Store`` model, join (through the
+many-to-many relationship) with the ``Book`` model, and aggregate on the
price field of the book model to produce a minimum and maximum value.
The same rules apply to the ``aggregate()`` clause. If you wanted to
@@ -205,6 +210,50 @@ issue the query::
>>> Store.objects.aggregate(youngest_age=Min('books__authors__age'))
+Following relationships backwards
+---------------------------------
+
+In a way similar to :ref:`lookups-that-span-relationships`, aggregations and
+annotations on fields of models or models that are related to the one you are
+querying can include traversing "reverse" relationships. The lowercase name
+of related models and double-underscores are used here too.
+
+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
+ >>> Publisher.objects.annotate(Count('book'))
+
+(Every ``Publisher`` in the resulting ``QuerySet`` will have an extra attribute
+called ``book__count``.)
+
+We can also ask for the oldest book of any of those managed by every publisher::
+
+ >>> Publisher.objects.aggregate(oldest_pubdate=Min('book__pubdate'))
+
+(The resulting dictionary will have a key called ``'oldest_pubdate'``. If no
+such alias were specified, it would be the rather long ``'book__pubdate__min'``.)
+
+This doesn't apply just to foreign keys. It also works with many-to-many
+relations. For example, we can ask for every author, annotated with the total
+number of pages considering all the books he/she has (co-)authored (note how we
+use ``'book'`` to specify the ``Author`` -> ``Book`` reverse many-to-many hop)::
+
+ >>> Author.objects.annotate(total_pages=Sum('book__pages'))
+
+(Every ``Author`` in the resulting ``QuerySet`` will have an extra attribute
+called ``total_pages``. If no such alias were specified, it would be the rather
+long ``book__pages__sum``.)
+
+Or ask for the average rating of all the books written by author(s) we have on
+file::
+
+ >>> Author.objects.aggregate(average_rating=Avg('book__rating'))
+
+(The resulting dictionary will have a key called ``'average__rating'``. If no
+such alias were specified, it would be the rather long ``'book__rating__avg'``.)
+
Aggregations and other QuerySet clauses
=======================================
@@ -263,7 +312,7 @@ and the query::
>>> Publisher.objects.filter(book__rating__gt=3.0).annotate(num_books=Count('book'))
-Both queries will return a list of Publishers that have at least one good
+Both queries will return a list of publishers that have at least one good
book (i.e., a book with a rating exceeding 3.0). However, the annotation in
the first query will provide the total number of all books published by the
publisher; the second query will only include good books in the annotated
diff --git a/docs/topics/db/examples/many_to_many.txt b/docs/topics/db/examples/many_to_many.txt
index 5a24027894..2076427768 100644
--- a/docs/topics/db/examples/many_to_many.txt
+++ b/docs/topics/db/examples/many_to_many.txt
@@ -35,7 +35,7 @@ objects, and a ``Publication`` has multiple ``Article`` objects:
What follows are examples of operations that can be performed using the Python
API facilities.
-Create a couple of Publications::
+Create a couple of ``Publications``::
>>> p1 = Publication(title='The Python Journal')
>>> p1.save()
@@ -44,11 +44,11 @@ Create a couple of Publications::
>>> p3 = Publication(title='Science Weekly')
>>> p3.save()
-Create an Article::
+Create an ``Article``::
>>> a1 = Article(headline='Django lets you build Web apps easily')
-You can't associate it with a Publication until it's been saved::
+You can't associate it with a ``Publication`` until it's been saved::
>>> a1.publications.add(p1)
Traceback (most recent call last):
@@ -60,11 +60,11 @@ Save it!
>>> a1.save()
-Associate the Article with a Publication::
+Associate the ``Article`` with a ``Publication``::
>>> a1.publications.add(p1)
-Create another Article, and set it to appear in both Publications::
+Create another ``Article``, and set it to appear in both ``Publications``::
>>> a2 = Article(headline='NASA uses Python')
>>> a2.save()
@@ -75,25 +75,26 @@ Adding a second time is OK::
>>> a2.publications.add(p3)
-Adding an object of the wrong type raises TypeError::
+Adding an object of the wrong type raises :exc:`~exceptions.TypeError`::
>>> a2.publications.add(a1)
Traceback (most recent call last):
...
TypeError: 'Publication' instance expected
-Add a Publication directly via publications.add by using keyword arguments::
+Create and add a ``Publication`` to an ``Article`` in one step using
+:meth:`~django.db.models.fields.related.RelatedManager.create`::
>>> new_publication = a2.publications.create(title='Highlights for Children')
-Article objects have access to their related Publication objects::
+``Article`` objects have access to their related ``Publication`` objects::
>>> a1.publications.all()
[<Publication: The Python Journal>]
>>> a2.publications.all()
[<Publication: Highlights for Children>, <Publication: Science News>, <Publication: Science Weekly>, <Publication: The Python Journal>]
-Publication objects have access to their related Article objects::
+``Publication`` objects have access to their related ``Article`` objects::
>>> p2.article_set.all()
[<Article: NASA uses Python>]
@@ -102,7 +103,8 @@ Publication objects have access to their related Article objects::
>>> Publication.objects.get(id=4).article_set.all()
[<Article: NASA uses Python>]
-Many-to-many relationships can be queried using :ref:`lookups across relationships <lookups-that-span-relationships>`::
+Many-to-many relationships can be queried using :ref:`lookups across
+relationships <lookups-that-span-relationships>`::
>>> Article.objects.filter(publications__id__exact=1)
[<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>]
@@ -119,7 +121,8 @@ Many-to-many relationships can be queried using :ref:`lookups across relationshi
>>> Article.objects.filter(publications__title__startswith="Science").distinct()
[<Article: NASA uses Python>]
-The count() function respects distinct() as well::
+The :meth:`~django.db.models.query.QuerySet.count` function respects
+:meth:`~django.db.models.query.QuerySet.distinct` as well::
>>> Article.objects.filter(publications__title__startswith="Science").count()
2
@@ -133,7 +136,7 @@ The count() function respects distinct() as well::
[<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>]
Reverse m2m queries are supported (i.e., starting at the table that doesn't have
-a ManyToManyField)::
+a :class:`~django.db.models.ManyToManyField`)::
>>> Publication.objects.filter(id__exact=1)
[<Publication: The Python Journal>]
@@ -163,7 +166,7 @@ involved is a little complex)::
>>> Article.objects.exclude(publications=p2)
[<Article: Django lets you build Web apps easily>]
-If we delete a Publication, its Articles won't be able to access it::
+If we delete a ``Publication``, its ``Articles`` won't be able to access it::
>>> p1.delete()
>>> Publication.objects.all()
@@ -172,7 +175,7 @@ If we delete a Publication, its Articles won't be able to access it::
>>> a1.publications.all()
[]
-If we delete an Article, its Publications won't be able to access it::
+If we delete an ``Article``, its ``Publications`` won't be able to access it::
>>> a2.delete()
>>> Article.objects.all()
@@ -199,7 +202,7 @@ Adding via the other end using keywords::
>>> a5.publications.all()
[<Publication: Science News>]
-Removing publication from an article::
+Removing ``Publication`` from an ``Article``::
>>> a4.publications.remove(p2)
>>> p2.article_set.all()
@@ -242,7 +245,7 @@ And you can clear from the other end::
>>> p2.article_set.all()
[<Article: Oxygen-free diet works wonders>]
-Recreate the article and Publication we have deleted::
+Recreate the ``Article`` and ``Publication`` we have deleted::
>>> p1 = Publication(title='The Python Journal')
>>> p1.save()
@@ -250,7 +253,8 @@ Recreate the article and Publication we have deleted::
>>> a2.save()
>>> a2.publications.add(p1, p2, p3)
-Bulk delete some Publications - references to deleted publications should go::
+Bulk delete some ``Publications`` - references to deleted publications should
+go::
>>> Publication.objects.filter(title__startswith='Science').delete()
>>> Publication.objects.all()
@@ -267,15 +271,18 @@ Bulk delete some articles - references to deleted objects should go::
[<Article: Django lets you build Web apps easily>]
>>> q.delete()
-After the delete, the QuerySet cache needs to be cleared, and the referenced
-objects should be gone::
+After the :meth:`~django.db.models.query.QuerySet.delete`, the
+:class:`~django.db.models.query.QuerySet` cache needs to be cleared, and the
+referenced objects should be gone::
>>> print(q)
[]
>>> p1.article_set.all()
[<Article: NASA uses Python>]
-An alternate to calling clear() is to assign the empty set::
+An alternate to calling
+:meth:`~django.db.models.fields.related.RelatedManager.clear` is to assign the
+empty set::
>>> p1.article_set = []
>>> p1.article_set.all()
diff --git a/docs/topics/db/examples/one_to_one.txt b/docs/topics/db/examples/one_to_one.txt
index 4c8e0ecfcc..09634c84c7 100644
--- a/docs/topics/db/examples/one_to_one.txt
+++ b/docs/topics/db/examples/one_to_one.txt
@@ -10,7 +10,7 @@ In this example, a ``Place`` optionally can be a ``Restaurant``:
.. code-block:: python
- from django.db import models, transaction, IntegrityError
+ from django.db import models
class Place(models.Model):
name = models.CharField(max_length=50)
diff --git a/docs/topics/db/managers.txt b/docs/topics/db/managers.txt
index a14616a17c..56bdd16e84 100644
--- a/docs/topics/db/managers.txt
+++ b/docs/topics/db/managers.txt
@@ -70,8 +70,8 @@ returns a list of all ``OpinionPoll`` objects, each with an extra
SELECT p.id, p.question, p.poll_date, COUNT(*)
FROM polls_opinionpoll p, polls_response r
WHERE p.id = r.poll_id
- GROUP BY 1, 2, 3
- ORDER BY 3 DESC""")
+ GROUP BY p.id, p.question, p.poll_date
+ ORDER BY p.poll_date DESC""")
result_list = []
for row in cursor.fetchall():
p = self.model(id=row[0], question=row[1], poll_date=row[2])
@@ -85,7 +85,7 @@ returns a list of all ``OpinionPoll`` objects, each with an extra
objects = PollManager()
class Response(models.Model):
- poll = models.ForeignKey(Poll)
+ poll = models.ForeignKey(OpinionPoll)
person_name = models.CharField(max_length=50)
response = models.TextField()
@@ -108,7 +108,7 @@ example, using this model::
...the statement ``Book.objects.all()`` will return all books in the database.
You can override a ``Manager``\'s base ``QuerySet`` by overriding the
-``Manager.get_query_set()`` method. ``get_query_set()`` should return a
+``Manager.get_queryset()`` method. ``get_queryset()`` should return a
``QuerySet`` with the properties you require.
For example, the following model has *two* ``Manager``\s -- one that returns
@@ -116,8 +116,8 @@ all objects, and one that returns only the books by Roald Dahl::
# First, define the Manager subclass.
class DahlBookManager(models.Manager):
- def get_query_set(self):
- return super(DahlBookManager, self).get_query_set().filter(author='Roald Dahl')
+ def get_queryset(self):
+ return super(DahlBookManager, self).get_queryset().filter(author='Roald Dahl')
# Then hook it into the Book model explicitly.
class Book(models.Model):
@@ -131,7 +131,7 @@ With this sample model, ``Book.objects.all()`` will return all books in the
database, but ``Book.dahl_objects.all()`` will only return the ones written by
Roald Dahl.
-Of course, because ``get_query_set()`` returns a ``QuerySet`` object, you can
+Of course, because ``get_queryset()`` returns a ``QuerySet`` object, you can
use ``filter()``, ``exclude()`` and all the other ``QuerySet`` methods on it.
So these statements are all legal::
@@ -147,12 +147,12 @@ models.
For example::
class MaleManager(models.Manager):
- def get_query_set(self):
- return super(MaleManager, self).get_query_set().filter(sex='M')
+ def get_queryset(self):
+ return super(MaleManager, self).get_queryset().filter(sex='M')
class FemaleManager(models.Manager):
- def get_query_set(self):
- return super(FemaleManager, self).get_query_set().filter(sex='F')
+ def get_queryset(self):
+ return super(FemaleManager, self).get_queryset().filter(sex='F')
class Person(models.Model):
first_name = models.CharField(max_length=50)
@@ -172,9 +172,12 @@ the "default" ``Manager``, and several parts of Django
(including :djadmin:`dumpdata`) will use that ``Manager``
exclusively for that model. As a result, it's a good idea to be careful in
your choice of default manager in order to avoid a situation where overriding
-``get_query_set()`` results in an inability to retrieve objects you'd like to
+``get_queryset()`` results in an inability to retrieve objects you'd like to
work with.
+.. versionchanged:: 1.6
+ The ``get_queryset`` method was previously named ``get_query_set``.
+
.. _managers-for-related-objects:
Using managers for related object access
@@ -188,7 +191,7 @@ by the default manager.
If the normal plain manager class (:class:`django.db.models.Manager`) is not
appropriate for your circumstances, you can force Django to use the same class
-as the default manager for your model by setting the `use_for_related_fields`
+as the default manager for your model by setting the ``use_for_related_fields``
attribute on the manager class. This is documented fully below_.
.. _below: manager-types_
@@ -366,7 +369,7 @@ it will use :class:`django.db.models.Manager`.
Writing correct Managers for use in automatic Manager instances
---------------------------------------------------------------
-As already suggested by the `django.contrib.gis` example, above, the
+As already suggested by the :mod:`django.contrib.gis` example, above, the
``use_for_related_fields`` feature is primarily for managers that need to
return a custom ``QuerySet`` subclass. In providing this functionality in your
manager, there are a couple of things to remember.
@@ -379,9 +382,9 @@ to from some other model. In those situations, Django has to be able to see
all the objects for the model it is fetching, so that *anything* which is
referred to can be retrieved.
-If you override the ``get_query_set()`` method and filter out any rows, Django
+If you override the ``get_queryset()`` method and filter out any rows, Django
will return incorrect results. Don't do that. A manager that filters results
-in ``get_query_set()`` is not appropriate for use as an automatic manager.
+in ``get_queryset()`` is not appropriate for use as an automatic manager.
Set ``use_for_related_fields`` when you define the class
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -410,4 +413,3 @@ used in a model, since the attribute's value is processed when the model class
is created and not subsequently reread. Set the attribute on the manager class
when it is first defined, as in the initial example of this section and
everything will work smoothly.
-
diff --git a/docs/topics/db/models.txt b/docs/topics/db/models.txt
index 2f1676ac1a..dd7714052d 100644
--- a/docs/topics/db/models.txt
+++ b/docs/topics/db/models.txt
@@ -66,13 +66,13 @@ those models. Do this by editing your settings file and changing the
your ``models.py``.
For example, if the models for your application live in the module
-``mysite.myapp.models`` (the package structure that is created for an
+``myapp.models`` (the package structure that is created for an
application by the :djadmin:`manage.py startapp <startapp>` script),
:setting:`INSTALLED_APPS` should read, in part::
INSTALLED_APPS = (
#...
- 'mysite.myapp',
+ 'myapp',
#...
)
@@ -660,15 +660,13 @@ model.
For example, this model has a few custom methods::
- from django.contrib.localflavor.us.models import USStateField
-
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
birth_date = models.DateField()
address = models.CharField(max_length=100)
city = models.CharField(max_length=50)
- state = USStateField() # Yes, this is America-centric...
+ state = models.CharField(max_length=2) # yes, this is America-centric
def baby_boomer_status(self):
"Returns the person's baby-boomer status."
@@ -1063,52 +1061,46 @@ 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 standard
-:class:`~django.contrib.auth.models.User` model that will be used in your
-templates. You can do it like this::
-
- from django.contrib.auth.models import User
+For example, suppose you want to add a method to the ``Person`` model described
+above. You can do it like this::
- class MyUser(User):
+ class MyPerson(Person):
class Meta:
proxy = True
def do_something(self):
...
-The ``MyUser`` class operates on the same database table as its parent
-:class:`~django.contrib.auth.models.User` class. In particular, any new
-instances of :class:`~django.contrib.auth.models.User` will also be accessible
-through ``MyUser``, and vice-versa::
+The ``MyPerson`` class operates on the same database table as its parent
+``Person`` class. In particular, any new instances of ``Person`` will also be
+accessible through ``MyPerson``, and vice-versa::
- >>> u = User.objects.create(username="foobar")
- >>> MyUser.objects.get(username="foobar")
- <MyUser: foobar>
+ >>> p = Person.objects.create(first_name="foobar")
+ >>> MyPerson.objects.get(first_name="foobar")
+ <MyPerson: foobar>
-You could also use a proxy model to define a different default ordering on a
-model. The standard :class:`~django.contrib.auth.models.User` model has no
-ordering defined on it (intentionally; sorting is expensive and we don't want
-to do it all the time when we fetch users). You might want to regularly order
-by the ``username`` attribute when you use the proxy. This is easy::
+You could also use a proxy model to define a different default ordering on
+a model. You might not always want to order the ``Person`` model, but regularly
+order by the ``last_name`` attribute when you use the proxy. This is easy::
- class OrderedUser(User):
+ class OrderedPerson(Person):
class Meta:
- ordering = ["username"]
+ ordering = ["last_name"]
proxy = True
-Now normal :class:`~django.contrib.auth.models.User` queries will be unordered
-and ``OrderedUser`` queries will be ordered by ``username``.
+Now normal ``Person`` queries will be unordered
+and ``OrderedPerson`` queries will be ordered by ``last_name``.
QuerySets still return the model that was requested
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-There is no way to have Django return, say, a ``MyUser`` object whenever you
-query for :class:`~django.contrib.auth.models.User` objects. A queryset for
-``User`` objects will return those types of objects. The whole point of proxy
-objects is that code relying on the original ``User`` will use those and your
-own code can use the extensions you included (that no other code is relying on
-anyway). It is not a way to replace the ``User`` (or any other) model
-everywhere with something of your own creation.
+There is no way to have Django return, say, a ``MyPerson`` object whenever you
+query for ``Person`` objects. A queryset for ``Person`` objects will return
+those types of objects. The whole point of proxy objects is that code relying
+on the original ``Person`` will use those and your own code can use the
+extensions you included (that no other code is relying on anyway). It is not
+a way to replace the ``Person`` (or any other) model everywhere with something
+of your own creation.
Base class restrictions
~~~~~~~~~~~~~~~~~~~~~~~
@@ -1131,12 +1123,12 @@ it will become the default, although any managers defined on the parent
classes will still be available.
Continuing our example from above, you could change the default manager used
-when you query the ``User`` model like this::
+when you query the ``Person`` model like this::
class NewManager(models.Manager):
...
- class MyUser(User):
+ class MyPerson(Person):
objects = NewManager()
class Meta:
@@ -1154,7 +1146,7 @@ containing the new managers and inherit that after the primary base class::
class Meta:
abstract = True
- class MyUser(User, ExtraManagers):
+ class MyPerson(Person, ExtraManagers):
class Meta:
proxy = True
diff --git a/docs/topics/db/multi-db.txt b/docs/topics/db/multi-db.txt
index 8a02305376..182099cc3a 100644
--- a/docs/topics/db/multi-db.txt
+++ b/docs/topics/db/multi-db.txt
@@ -20,9 +20,7 @@ documentation.
Databases can have any alias you choose. However, the alias
``default`` has special significance. Django uses the database with
-the alias of ``default`` when no other database has been selected. If
-you don't have a ``default`` database, you need to be careful to
-always specify the database that you want to use.
+the alias of ``default`` when no other database has been selected.
The following is an example ``settings.py`` snippet defining two
databases -- a default PostgreSQL database and a MySQL database called
@@ -45,6 +43,29 @@ databases -- a default PostgreSQL database and a MySQL database called
}
}
+If the concept of a ``default`` database doesn't make sense in the context
+of your project, you need to be careful to always specify the database
+that you want to use. Django requires that a ``default`` database entry
+be defined, but the parameters dictionary can be left blank if it will not be
+used. The following is an example ``settings.py`` snippet defining two
+non-default databases, with the ``default`` entry intentionally left empty::
+
+ DATABASES = {
+ 'default': {},
+ 'users': {
+ 'NAME': 'user_data',
+ 'ENGINE': 'django.db.backends.mysql',
+ 'USER': 'mysql_user',
+ 'PASSWORD': 'superS3cret'
+ },
+ 'customers': {
+ 'NAME': 'customer_data',
+ 'ENGINE': 'django.db.backends.mysql',
+ 'USER': 'mysql_cust',
+ 'PASSWORD': 'veryPriv@ate'
+ }
+ }
+
If you attempt to access a database that you haven't defined in your
:setting:`DATABASES` setting, Django will raise a
``django.db.utils.ConnectionDoesNotExist`` exception.
@@ -303,7 +324,7 @@ from::
in the master/slave pool.
"""
db_list = ('master', 'slave1', 'slave2')
- if obj1.state.db in db_list and obj2.state.db in db_list:
+ if obj1._state.db in db_list and obj2._state.db in db_list:
return True
return None
@@ -485,19 +506,19 @@ solution is to use ``db_manager()``, like this::
``db_manager()`` returns a copy of the manager bound to the database you specify.
-Using ``get_query_set()`` with multiple databases
+Using ``get_queryset()`` with multiple databases
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-If you're overriding ``get_query_set()`` on your manager, be sure to
+If you're overriding ``get_queryset()`` on your manager, be sure to
either call the method on the parent (using ``super()``) or do the
appropriate handling of the ``_db`` attribute on the manager (a string
containing the name of the database to use).
For example, if you want to return a custom ``QuerySet`` class from
-the ``get_query_set`` method, you could do this::
+the ``get_queryset`` method, you could do this::
class MyManager(models.Manager):
- def get_query_set(self):
+ def get_queryset(self):
qs = CustomQuerySet(self.model)
if self._db is not None:
qs = qs.using(self._db)
@@ -527,9 +548,9 @@ multiple-database support::
# Tell Django to delete objects from the 'other' database
obj.delete(using=self.using)
- def queryset(self, request):
+ def get_queryset(self, request):
# Tell Django to look for objects on the 'other' database.
- return super(MultiDBModelAdmin, self).queryset(request).using(self.using)
+ return super(MultiDBModelAdmin, self).get_queryset(request).using(self.using)
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
# Tell Django to populate ForeignKey widgets using a query
@@ -552,9 +573,9 @@ Inlines can be handled in a similar fashion. They require three customized metho
class MultiDBTabularInline(admin.TabularInline):
using = 'other'
- def queryset(self, request):
+ def get_queryset(self, request):
# Tell Django to look for inline objects on the 'other' database.
- return super(MultiDBTabularInline, self).queryset(request).using(self.using)
+ return super(MultiDBTabularInline, self).get_queryset(request).using(self.using)
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
# Tell Django to populate ForeignKey widgets using a query
diff --git a/docs/topics/db/optimization.txt b/docs/topics/db/optimization.txt
index b5cca52e23..c1459ae247 100644
--- a/docs/topics/db/optimization.txt
+++ b/docs/topics/db/optimization.txt
@@ -157,7 +157,7 @@ Doing the following is potentially quite slow:
>>> entry = Entry.objects.get(headline__startswith="News")
-First of all, `headline` is not indexed, which will make the underlying
+First of all, ``headline`` is not indexed, which will make the underlying
database fetch slower.
Second, the lookup doesn't guarantee that only one object will be returned.
diff --git a/docs/topics/db/queries.txt b/docs/topics/db/queries.txt
index 90c06ac66a..f19302974d 100644
--- a/docs/topics/db/queries.txt
+++ b/docs/topics/db/queries.txt
@@ -163,10 +163,9 @@ default. Access it directly via the model class, like so::
"record-level" operations.
The :class:`~django.db.models.Manager` is the main source of ``QuerySets`` for
-a model. It acts as a "root" :class:`~django.db.models.query.QuerySet` that
-describes all objects in the model's database table. For example,
-``Blog.objects`` is the initial :class:`~django.db.models.query.QuerySet` that
-contains all ``Blog`` objects in the database.
+a model. For example, ``Blog.objects.all()`` returns a
+:class:`~django.db.models.query.QuerySet` that contains all ``Blog`` objects in
+the database.
Retrieving all objects
----------------------
@@ -180,20 +179,13 @@ this, use the :meth:`~django.db.models.query.QuerySet.all` method on a
The :meth:`~django.db.models.query.QuerySet.all` method returns a
:class:`~django.db.models.query.QuerySet` of all the objects in the database.
-(If ``Entry.objects`` is a :class:`~django.db.models.query.QuerySet`, why can't
-we just do ``Entry.objects``? That's because ``Entry.objects``, the root
-:class:`~django.db.models.query.QuerySet`, is a special case that cannot be
-evaluated. The :meth:`~django.db.models.query.QuerySet.all` method returns a
-:class:`~django.db.models.query.QuerySet` that *can* be evaluated.)
-
-
Retrieving specific objects with filters
----------------------------------------
-The root :class:`~django.db.models.query.QuerySet` provided by the
-:class:`~django.db.models.Manager` describes all objects in the database
-table. Usually, though, you'll need to select only a subset of the complete set
-of objects.
+The :class:`~django.db.models.query.QuerySet` returned by
+:meth:`~django.db.models.query.QuerySet.all` describes all objects in the
+database table. Usually, though, you'll need to select only a subset of the
+complete set of objects.
To create such a subset, you refine the initial
:class:`~django.db.models.query.QuerySet`, adding filter conditions. The two
@@ -216,10 +208,9 @@ so::
Entry.objects.filter(pub_date__year=2006)
-We don't have to add an :meth:`~django.db.models.query.QuerySet.all` --
-``Entry.objects.all().filter(...)``. That would still work, but you only need
-:meth:`~django.db.models.query.QuerySet.all` when you want all objects from the
-root :class:`~django.db.models.query.QuerySet`.
+With the default manager class, it is the same as::
+
+ Entry.objects.all().filter(pub_date__year=2006)
.. _chaining-filters:
@@ -306,8 +297,8 @@ the query - in this case, it will be a
:class:`~django.db.models.query.QuerySet` containing a single element.
If you know there is only one object that matches your query, you can use the
-:meth:`~django.db.models.query.QuerySet.get` method on a `Manager` which
-returns the object directly::
+:meth:`~django.db.models.query.QuerySet.get` method on a
+:class:`~django.db.models.Manager` which returns the object directly::
>>> one_entry = Entry.objects.get(pk=1)
@@ -327,8 +318,8 @@ a primary key of 1, Django will raise ``Entry.DoesNotExist``.
Similarly, Django will complain if more than one item matches the
:meth:`~django.db.models.query.QuerySet.get` query. In this case, it will raise
-``MultipleObjectsReturned``, which again is an attribute of the model class
-itself.
+:exc:`~django.core.exceptions.MultipleObjectsReturned`, which again is an
+attribute of the model class itself.
Other QuerySet methods
@@ -412,14 +403,13 @@ translates (roughly) into the following SQL::
.. _`Keyword Arguments`: http://docs.python.org/tutorial/controlflow.html#keyword-arguments
-.. versionchanged:: 1.4
- The field specified in a lookup has to be the name of a model field.
- There's one exception though, in case of a
- :class:`~django.db.models.ForeignKey` you can specify the field
- name suffixed with ``_id``. In this case, the value parameter is expected
- to contain the raw value of the foreign model's primary key. For example:
+The field specified in a lookup has to be the name of a model field. There's
+one exception though, in case of a :class:`~django.db.models.ForeignKey` you
+can specify the field name suffixed with ``_id``. In this case, the value
+parameter is expected to contain the raw value of the foreign model's primary
+key. For example:
- >>> Entry.objects.filter(blog_id__exact=4)
+ >>> Entry.objects.filter(blog_id__exact=4)
If you pass an invalid keyword argument, a lookup function will raise
``TypeError``.
@@ -601,6 +591,8 @@ relation may end up filtering on different linked objects.
Filters can reference fields on the model
-----------------------------------------
+.. class:: F
+
In the examples given so far, we have constructed filters that compare
the value of a model field with a constant. But what if you want to compare
the value of a model field with another field on the same model?
@@ -755,6 +747,8 @@ To avoid this problem, simply save the
Complex lookups with Q objects
==============================
+.. class:: Q
+
Keyword argument queries -- in :meth:`~django.db.models.query.QuerySet.filter`,
etc. -- are "AND"ed together. If you need to execute more complex queries (for
example, queries with ``OR`` statements), you can use ``Q`` objects.
@@ -830,7 +824,7 @@ precede the definition of any keyword arguments. For example::
The `OR lookups examples`_ in the Django unit tests show some possible uses
of ``Q``.
- .. _OR lookups examples: https://github.com/django/django/blob/master/tests/modeltests/or_lookups/tests.py
+ .. _OR lookups examples: https://github.com/django/django/blob/master/tests/or_lookups/tests.py
Comparing objects
=================
diff --git a/docs/topics/db/sql.txt b/docs/topics/db/sql.txt
index 310dcb5ae6..34cfa382d3 100644
--- a/docs/topics/db/sql.txt
+++ b/docs/topics/db/sql.txt
@@ -24,9 +24,8 @@ return model instances:
.. method:: Manager.raw(raw_query, params=None, translations=None)
This method method takes a raw SQL query, executes it, and returns a
-:class:`~django.db.models.query.RawQuerySet` instance. This
-:class:`~django.db.models.query.RawQuerySet` instance can be iterated
-over just like an normal QuerySet to provide object instances.
+``django.db.models.query.RawQuerySet`` instance. This ``RawQuerySet`` instance
+can be iterated over just like an normal QuerySet to provide object instances.
This is best illustrated with an example. Suppose you've got the following model::
@@ -156,7 +155,7 @@ of people with their ages calculated by the database::
Jane is 42.
...
-__ http://www.postgresql.org/docs/8.4/static/functions-datetime.html
+__ http://www.postgresql.org/docs/current/static/functions-datetime.html
Passing parameters into ``raw()``
---------------------------------
@@ -202,31 +201,38 @@ perform queries that don't map cleanly to models, or directly execute
In these cases, you can always access the database directly, routing around
the model layer entirely.
-The object ``django.db.connection`` represents the
-default database connection, and ``django.db.transaction`` represents the
-default database transaction. To use the database connection, call
-``connection.cursor()`` to get a cursor object. Then, call
-``cursor.execute(sql, [params])`` to execute the SQL and ``cursor.fetchone()``
-or ``cursor.fetchall()`` to return the resulting rows. After performing a data
-changing operation, you should then call
-``transaction.commit_unless_managed()`` to ensure your changes are committed
-to the database. If your query is purely a data retrieval operation, no commit
-is required. For example::
+The object ``django.db.connection`` represents the default database
+connection. To use the database connection, call ``connection.cursor()`` to
+get a cursor object. Then, call ``cursor.execute(sql, [params])`` to execute
+the SQL and ``cursor.fetchone()`` or ``cursor.fetchall()`` to return the
+resulting rows.
+
+For example::
+
+ from django.db import connection
def my_custom_sql():
- from django.db import connection, transaction
cursor = connection.cursor()
- # Data modifying operation - commit required
cursor.execute("UPDATE bar SET foo = 1 WHERE baz = %s", [self.baz])
- transaction.commit_unless_managed()
- # Data retrieval operation - no commit required
cursor.execute("SELECT foo FROM bar WHERE baz = %s", [self.baz])
row = cursor.fetchone()
return row
+.. versionchanged:: 1.6
+ In Django 1.5 and earlier, after performing a data changing operation, you
+ had to call ``transaction.commit_unless_managed()`` to ensure your changes
+ were committed to the database. Since Django now defaults to database-level
+ autocommit, this isn't necessary any longer.
+
+Note that if you want to include literal percent signs in the query, you have to
+double them in the case you are passing parameters::
+
+ cursor.execute("SELECT foo FROM bar WHERE baz = '30%'")
+ cursor.execute("SELECT foo FROM bar WHERE baz = '30%%' and id = %s", [self.id])
+
If you are using :doc:`more than one database </topics/db/multi-db>`, you can
use ``django.db.connections`` to obtain the connection (and cursor) for a
specific database. ``django.db.connections`` is a dictionary-like
@@ -236,7 +242,6 @@ alias::
from django.db import connections
cursor = connections['my_db_alias'].cursor()
# Your code here...
- transaction.commit_unless_managed(using='my_db_alias')
By default, the Python DB API will return results without their field
names, which means you end up with a ``list`` of values, rather than a
@@ -261,27 +266,18 @@ Here is an example of the difference between the two::
>>> dictfetchall(cursor)
[{'parent_id': None, 'id': 54360982L}, {'parent_id': None, 'id': 54360880L}]
-
-.. _transactions-and-raw-sql:
-
-Transactions and raw SQL
-------------------------
-
-When you make a raw SQL call, Django will automatically mark the
-current transaction as dirty. You must then ensure that the
-transaction containing those calls is closed correctly. See :ref:`the
-notes on the requirements of Django's transaction handling
-<topics-db-transactions-requirements>` for more details.
-
Connections and cursors
-----------------------
``connection`` and ``cursor`` mostly implement the standard Python DB-API
-described in :pep:`249` (except when it comes to :doc:`transaction handling
-</topics/db/transactions>`). If you're not familiar with the Python DB-API, note
-that the SQL statement in ``cursor.execute()`` uses placeholders, ``"%s"``,
-rather than adding parameters directly within the SQL. If you use this
-technique, the underlying database library will automatically add quotes and
-escaping to your parameter(s) as necessary. (Also note that Django expects the
-``"%s"`` placeholder, *not* the ``"?"`` placeholder, which is used by the SQLite
-Python bindings. This is for the sake of consistency and sanity.)
+described in :pep:`249` — except when it comes to :doc:`transaction handling
+</topics/db/transactions>`.
+
+If you're not familiar with the Python DB-API, note that the SQL statement in
+``cursor.execute()`` uses placeholders, ``"%s"``, rather than adding
+parameters directly within the SQL. If you use this technique, the underlying
+database library will automatically escape your parameters as necessary.
+
+Also note that Django expects the ``"%s"`` placeholder, *not* the ``"?"``
+placeholder, which is used by the SQLite Python bindings. This is for the sake
+of consistency and sanity.
diff --git a/docs/topics/db/tablespaces.txt b/docs/topics/db/tablespaces.txt
index 7fcd5588e7..8bf1d07bca 100644
--- a/docs/topics/db/tablespaces.txt
+++ b/docs/topics/db/tablespaces.txt
@@ -68,6 +68,3 @@ PostgreSQL and Oracle support tablespaces. SQLite and MySQL don't.
When you use a backend that lacks support for tablespaces, Django ignores all
tablespace-related options.
-
-.. versionchanged:: 1.4
- Since Django 1.4, the PostgreSQL backend supports tablespaces.
diff --git a/docs/topics/db/transactions.txt b/docs/topics/db/transactions.txt
index 65944abb8b..d48365dc9e 100644
--- a/docs/topics/db/transactions.txt
+++ b/docs/topics/db/transactions.txt
@@ -1,289 +1,366 @@
-==============================
-Managing database transactions
-==============================
+=====================
+Database transactions
+=====================
.. module:: django.db.transaction
-Django gives you a few ways to control how database transactions are managed,
-if you're using a database that supports transactions.
+Django gives you a few ways to control how database transactions are managed.
-Django's default transaction behavior
-=====================================
+Managing database transactions
+==============================
-Django's default behavior is to run with an open transaction which it
-commits automatically when any built-in, data-altering model function is
-called. For example, if you call ``model.save()`` or ``model.delete()``, the
-change will be committed immediately.
+Django's default transaction behavior
+-------------------------------------
-This is much like the auto-commit setting for most databases. As soon as you
-perform an action that needs to write to the database, Django produces the
-``INSERT``/``UPDATE``/``DELETE`` statements and then does the ``COMMIT``.
-There's no implicit ``ROLLBACK``.
+Django's default behavior is to run in autocommit mode. Each query is
+immediately committed to the database. :ref:`See below for details
+<autocommit-details>`.
-Tying transactions to HTTP requests
-===================================
+Django uses transactions or savepoints automatically to guarantee the
+integrity of ORM operations that require multiple queries, especially
+:ref:`delete() <topics-db-queries-delete>` and :ref:`update()
+<topics-db-queries-update>` queries.
-The recommended way to handle transactions in Web requests is to tie them to
-the request and response phases via Django's ``TransactionMiddleware``.
+.. versionchanged:: 1.6
+ Previous version of Django featured :ref:`a more complicated default
+ behavior <transactions-upgrading-from-1.5>`.
-It works like this: When a request starts, Django starts a transaction. If the
-response is produced without problems, Django commits any pending transactions.
-If the view function produces an exception, Django rolls back any pending
-transactions.
+.. _tying-transactions-to-http-requests:
-To activate this feature, just add the ``TransactionMiddleware`` middleware to
-your :setting:`MIDDLEWARE_CLASSES` setting::
+Tying transactions to HTTP requests
+-----------------------------------
- MIDDLEWARE_CLASSES = (
- 'django.middleware.cache.UpdateCacheMiddleware',
- 'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.middleware.common.CommonMiddleware',
- 'django.middleware.transaction.TransactionMiddleware',
- 'django.middleware.cache.FetchFromCacheMiddleware',
- )
+A common way to handle transactions on the web is to wrap each request in a
+transaction. Set :setting:`ATOMIC_REQUESTS <DATABASE-ATOMIC_REQUESTS>` to
+``True`` in the configuration of each database for which you want to enable
+this behavior.
-The order is quite important. The transaction middleware applies not only to
-view functions, but also for all middleware modules that come after it. So if
-you use the session middleware after the transaction middleware, session
-creation will be part of the transaction.
+It works like this. Before calling a view function, Django starts a
+transaction. If the response is produced without problems, Django commits the
+transaction. If the view produces an exception, Django rolls back the
+transaction.
-The various cache middlewares are an exception:
-:class:`~django.middleware.cache.CacheMiddleware`,
-:class:`~django.middleware.cache.UpdateCacheMiddleware`, and
-:class:`~django.middleware.cache.FetchFromCacheMiddleware` are never affected.
-Even when using database caching, Django's cache backend uses its own
-database cursor (which is mapped to its own database connection internally).
+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.
-.. note::
+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::
- 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.
+ def my_view(request):
+ do_stuff()
+ my_view.transactions_per_request = False
-.. _transaction-management-functions:
+.. warning::
-Controlling transaction management in views
-===========================================
+ While the simplicity of this transaction model is appealing, it also makes it
+ inefficient when traffic increases. Opening a transaction for every view has
+ some overhead. The impact on performance depends on the query patterns of your
+ application and on how well your database handles locking.
-For most people, implicit request-based transactions work wonderfully. However,
-if you need more fine-grained control over how transactions are managed, you can
-use a set of functions in ``django.db.transaction`` to control transactions on a
-per-function or per-code-block basis.
+.. admonition:: Per-request transactions and streaming responses
-These functions, described in detail below, can be used in two different ways:
+ When a view returns a :class:`~django.http.StreamingHttpResponse`, reading
+ the contents of the response will often execute code to generate the
+ content. Since the view has already returned, such code runs outside of
+ the transaction.
-* As a decorator_ on a particular function. For example::
+ Generally speaking, it isn't advisable to write to the database while
+ generating a streaming response, since there's no sensible way to handle
+ errors after starting to send the response.
- from django.db import transaction
+In practice, this feature simply wraps every view function in the :func:`atomic`
+decorator described below.
- @transaction.commit_on_success
- def viewfunc(request):
- # ...
- # this code executes inside a transaction
- # ...
+Note that only the execution of your view in enclosed in the transactions.
+Middleware run outside of the transaction, and so does the rendering of
+template responses.
-* As a `context manager`_ around a particular block of code::
+.. versionchanged:: 1.6
+ Django used to provide this feature via ``TransactionMiddleware``, which is
+ now deprecated.
- from django.db import transaction
+Controlling transactions explicitly
+-----------------------------------
- def viewfunc(request):
- # ...
- # this code executes using default transaction management
- # ...
+.. versionadded:: 1.6
- with transaction.commit_on_success():
- # ...
- # this code executes inside a transaction
- # ...
+Django provides a single API to control database transactions.
-Both techniques work with all supported version of Python.
+.. function:: atomic(using=None, savepoint=True)
-.. _decorator: http://docs.python.org/glossary.html#term-decorator
-.. _context manager: http://docs.python.org/glossary.html#term-context-manager
+ This function creates an atomic block for writes to the database.
+ (Atomicity is the defining property of database transactions.)
-For maximum compatibility, all of the examples below show transactions using the
-decorator syntax, but all of the follow functions may be used as context
-managers, too.
+ When the block completes successfully, the changes are committed to the
+ database. When it raises an exception, the changes are rolled back.
-.. note::
+ ``atomic`` can be nested. In this case, when an inner block completes
+ successfully, its effects can still be rolled back if an exception is
+ raised in the outer block at a later point.
- Although the examples below use view functions as examples, these
- decorators and context managers can be used anywhere in your code
- that you need to deal with transactions.
+ ``atomic`` takes a ``using`` argument which should be the name of a
+ database. If this argument isn't provided, Django uses the ``"default"``
+ database.
-.. _topics-db-transactions-autocommit:
+ ``atomic`` is usable both as a `decorator`_::
-.. function:: autocommit
+ from django.db import transaction
- Use the ``autocommit`` decorator to switch a view function to Django's
- default commit behavior, regardless of the global transaction setting.
+ @transaction.atomic
+ def viewfunc(request):
+ # This code executes inside a transaction.
+ do_stuff()
- Example::
+ and as a `context manager`_::
from django.db import transaction
- @transaction.autocommit
def viewfunc(request):
- ....
+ # This code executes in autocommit mode (Django's default).
+ do_stuff()
- @transaction.autocommit(using="my_other_database")
- def viewfunc2(request):
- ....
+ with transaction.atomic():
+ # This code executes inside a transaction.
+ do_more_stuff()
- Within ``viewfunc()``, transactions will be committed as soon as you call
- ``model.save()``, ``model.delete()``, or any other function that writes to
- the database. ``viewfunc2()`` will have this same behavior, but for the
- ``"my_other_database"`` connection.
+ .. _decorator: http://docs.python.org/glossary.html#term-decorator
+ .. _context manager: http://docs.python.org/glossary.html#term-context-manager
-.. function:: commit_on_success
-
- Use the ``commit_on_success`` decorator to use a single transaction for all
- the work done in a function::
+ Wrapping ``atomic`` in a try/except block allows for natural handling of
+ integrity errors::
- from django.db import transaction
+ from django.db import IntegrityError, transaction
- @transaction.commit_on_success
+ @transaction.atomic
def viewfunc(request):
- ....
+ do_stuff()
- @transaction.commit_on_success(using="my_other_database")
- def viewfunc2(request):
- ....
+ try:
+ with transaction.atomic():
+ do_stuff_that_could_fail()
+ except IntegrityError:
+ handle_exception()
- If the function returns successfully, then Django will commit all work done
- within the function at that point. If the function raises an exception,
- though, Django will roll back the transaction.
+ do_more_stuff()
-.. function:: commit_manually
+ In this example, even if ``do_stuff_that_could_fail()`` causes a database
+ error by breaking an integrity constraint, you can execute queries in
+ ``do_more_stuff()``, and the changes from ``do_stuff()`` are still there.
- Use the ``commit_manually`` decorator if you need full control over
- transactions. It tells Django you'll be managing the transaction on your
- own.
+ In order to guarantee atomicity, ``atomic`` disables some APIs. Attempting
+ to commit, roll back, or change the autocommit state of the database
+ connection within an ``atomic`` block will raise an exception.
- Whether you are writing or simply reading from the database, you must
- ``commit()`` or ``rollback()`` explicitly or Django will raise a
- :exc:`TransactionManagementError` exception. This is required when reading
- from the database because ``SELECT`` statements may call functions which
- modify tables, and thus it is impossible to know if any data has been
- modified.
+ Under the hood, Django's transaction management code:
- Manual transaction management looks like this::
+ - opens a transaction when entering the outermost ``atomic`` block;
+ - creates a savepoint when entering an inner ``atomic`` block;
+ - releases or rolls back to the savepoint when exiting an inner block;
+ - commits or rolls back the transaction when exiting the outermost block.
- from django.db import transaction
+ You can disable the creation of savepoints for inner blocks by setting the
+ ``savepoint`` argument to ``False``. If an exception occurs, Django will
+ perform the rollback when exiting the first parent block with a savepoint
+ if there is one, and the outermost block otherwise. Atomicity is still
+ guaranteed by the outer transaction. This option should only be used if
+ the overhead of savepoints is noticeable. It has the drawback of breaking
+ the error handling described above.
- @transaction.commit_manually
- def viewfunc(request):
- ...
- # You can commit/rollback however and whenever you want
- transaction.commit()
- ...
+ You may use ``atomic`` when autocommit is turned off. It will only use
+ savepoints, even for the outermost block, and it will raise an exception
+ if the outermost block is declared with ``savepoint=False``.
- # But you've got to remember to do it yourself!
- try:
- ...
- except:
- transaction.rollback()
- else:
- transaction.commit()
+.. admonition:: Performance considerations
+
+ Open transactions have a performance cost for your database server. To
+ minimize this overhead, keep your transactions as short as possible. This
+ is especially important of you're using :func:`atomic` in long-running
+ processes, outside of Django's request / response cycle.
+
+Autocommit
+==========
- @transaction.commit_manually(using="my_other_database")
- def viewfunc2(request):
- ....
+.. _autocommit-details:
-.. _topics-db-transactions-requirements:
+Why Django uses autocommit
+--------------------------
-Requirements for transaction handling
-=====================================
+In the SQL standards, each SQL query starts a transaction, unless one is
+already in progress. Such transactions must then be committed or rolled back.
-Django requires that every transaction that is opened is closed before
-the completion of a request. If you are using :func:`autocommit` (the
-default commit mode) or :func:`commit_on_success`, this will be done
-for you automatically. However, if you are manually managing
-transactions (using the :func:`commit_manually` decorator), you must
-ensure that the transaction is either committed or rolled back before
-a request is completed.
+This isn't always convenient for application developers. To alleviate this
+problem, most databases provide an autocommit mode. When autocommit is turned
+on, each SQL query is wrapped in its own transaction. In other words, the
+transaction is not only automatically started, but also automatically
+committed.
-This applies to all database operations, not just write operations. Even
-if your transaction only reads from the database, the transaction must
-be committed or rolled back before you complete a request.
+:pep:`249`, the Python Database API Specification v2.0, requires autocommit to
+be initially turned off. Django overrides this default and turns autocommit
+on.
+
+To avoid this, you can :ref:`deactivate the transaction management
+<deactivate-transaction-management>`, but it isn't recommended.
+
+.. versionchanged:: 1.6
+ Before Django 1.6, autocommit was turned off, and it was emulated by
+ forcing a commit after write operations in the ORM.
.. _deactivate-transaction-management:
-How to globally deactivate transaction management
-=================================================
+Deactivating transaction management
+-----------------------------------
+
+You can totally disable Django's transaction management for a given database
+by setting :setting:`AUTOCOMMIT <DATABASE-AUTOCOMMIT>` to ``False`` in its
+configuration. If you do this, Django won't enable autocommit, and won't
+perform any commits. You'll get the regular behavior of the underlying
+database library.
+
+This requires you to commit explicitly every transaction, even those started
+by Django or by third-party libraries. Thus, this is best used in situations
+where you want to run your own transaction-controlling middleware or do
+something really strange.
+
+.. versionchanged:: 1.6
+ This used to be controlled by the ``TRANSACTIONS_MANAGED`` setting.
+
+Low-level APIs
+==============
+
+.. warning::
+
+ Always prefer :func:`atomic` if possible at all. It accounts for the
+ idiosyncrasies of each database and prevents invalid operations.
+
+ The low level APIs are only useful if you're implementing your own
+ transaction management.
+
+.. _managing-autocommit:
+
+Autocommit
+----------
+
+.. versionadded:: 1.6
+
+Django provides a straightforward API in the :mod:`django.db.transaction`
+module to manage the autocommit state of each database connection.
+
+.. function:: get_autocommit(using=None)
+
+.. function:: set_autocommit(autocommit, using=None)
+
+These functions take a ``using`` argument which should be the name of a
+database. If it isn't provided, Django uses the ``"default"`` database.
+
+Autocommit is initially turned on. If you turn it off, it's your
+responsibility to restore it.
+
+Once you turn autocommit off, you get the default behavior of your database
+adapter, and Django won't help you. Although that behavior is specified in
+:pep:`249`, implementations of adapters aren't always consistent with one
+another. Review the documentation of the adapter you're using carefully.
+
+You must ensure that no transaction is active, usually by issuing a
+:func:`commit` or a :func:`rollback`, before turning autocommit back on.
+
+Django will refuse to turn autocommit off when an :func:`atomic` block is
+active, because that would break atomicity.
+
+Transactions
+------------
-Control freaks can totally disable all transaction management by setting
-:setting:`TRANSACTIONS_MANAGED` to ``True`` in the Django settings file.
+A transaction is an atomic set of database queries. Even if your program
+crashes, the database guarantees that either all the changes will be applied,
+or none of them.
-If you do this, Django won't provide any automatic transaction management
-whatsoever. Middleware will no longer implicitly commit transactions, and
-you'll need to roll management yourself. This even requires you to commit
-changes done by middleware somewhere else.
+Django doesn't provide an API to start a transaction. The expected way to
+start a transaction is to disable autocommit with :func:`set_autocommit`.
-Thus, this is best used in situations where you want to run your own
-transaction-controlling middleware or do something really strange. In almost
-all situations, you'll be better off using the default behavior, or the
-transaction middleware, and only modify selected functions as needed.
+Once you're in a transaction, you can choose either to apply the changes
+you've performed until this point with :func:`commit`, or to cancel them with
+:func:`rollback`. These functions are defined in :mod:`django.db.transaction`.
+
+.. function:: commit(using=None)
+
+.. function:: rollback(using=None)
+
+These functions take a ``using`` argument which should be the name of a
+database. If it isn't provided, Django uses the ``"default"`` database.
+
+Django will refuse to commit or to rollback when an :func:`atomic` block is
+active, because that would break atomicity.
.. _topics-db-transactions-savepoints:
Savepoints
-==========
+----------
-A savepoint is a marker within a transaction that enables you to roll back part
-of a transaction, rather than the full transaction. Savepoints are available
-with the PostgreSQL 8, Oracle and MySQL (when using the InnoDB storage engine)
-backends. Other backends provide the savepoint functions, but they're empty
-operations -- they don't actually do anything.
+A savepoint is a marker within a transaction that enables you to roll back
+part of a transaction, rather than the full transaction. Savepoints are
+available with the SQLite (≥ 3.6.8), PostgreSQL, Oracle and MySQL (when using
+the InnoDB storage engine) backends. Other backends provide the savepoint
+functions, but they're empty operations -- they don't actually do anything.
-.. versionchanged:: 1.4
- Savepoint support for the MySQL backend was added in Django 1.4.
+Savepoints aren't especially useful if you are using autocommit, the default
+behavior of Django. However, once you open a transaction with :func:`atomic`,
+you build up a series of database operations awaiting a commit or rollback. If
+you issue a rollback, the entire transaction is rolled back. Savepoints
+provide the ability to perform a fine-grained rollback, rather than the full
+rollback that would be performed by ``transaction.rollback()``.
-Savepoints aren't especially useful if you are using the default
-``autocommit`` behavior of Django. However, if you are using
-``commit_on_success`` or ``commit_manually``, each open transaction will build
-up a series of database operations, awaiting a commit or rollback. If you
-issue a rollback, the entire transaction is rolled back. Savepoints provide
-the ability to perform a fine-grained rollback, rather than the full rollback
-that would be performed by ``transaction.rollback()``.
+.. versionchanged:: 1.6
+
+When the :func:`atomic` decorator is nested, it creates a savepoint to allow
+partial commit or rollback. You're strongly encouraged to use :func:`atomic`
+rather than the functions described below, but they're still part of the
+public API, and there's no plan to deprecate them.
Each of these functions takes a ``using`` argument which should be the name of
a database for which the behavior applies. If no ``using`` argument is
provided then the ``"default"`` database is used.
-Savepoints are controlled by three methods on the transaction object:
+Savepoints are controlled by three functions in :mod:`django.db.transaction`:
+
+.. function:: savepoint(using=None)
+
+ Creates a new savepoint. This marks a point in the transaction that is
+ known to be in a "good" state. Returns the savepoint ID (``sid``).
-.. method:: transaction.savepoint(using=None)
+.. function:: savepoint_commit(sid, using=None)
- Creates a new savepoint. This marks a point in the transaction that
- is known to be in a "good" state.
+ Releases savepoint ``sid``. The changes performed since the savepoint was
+ created become part of the transaction.
- Returns the savepoint ID (sid).
+.. function:: savepoint_rollback(sid, using=None)
-.. method:: transaction.savepoint_commit(sid, using=None)
+ Rolls back the transaction to savepoint ``sid``.
- Updates the savepoint to include any operations that have been performed
- since the savepoint was created, or since the last commit.
+These functions do nothing if savepoints aren't supported or if the database
+is in autocommit mode.
-.. method:: transaction.savepoint_rollback(sid, using=None)
+In addition, there's a utility function:
- Rolls the transaction back to the last point at which the savepoint was
- committed.
+.. function:: clean_savepoints(using=None)
+
+ Resets the counter used to generate unique savepoint IDs.
The following example demonstrates the use of savepoints::
from django.db import transaction
- @transaction.commit_manually
+ # open a transaction
+ @transaction.atomic
def viewfunc(request):
a.save()
- # open transaction now contains a.save()
+ # transaction now contains a.save()
+
sid = transaction.savepoint()
b.save()
- # open transaction now contains a.save() and b.save()
+ # transaction now contains a.save() and b.save()
if want_to_keep_b:
transaction.savepoint_commit(sid)
@@ -292,10 +369,28 @@ The following example demonstrates the use of savepoints::
transaction.savepoint_rollback(sid)
# open transaction now contains only a.save()
- transaction.commit()
+Database-specific notes
+=======================
+
+.. _savepoints-in-sqlite:
+
+Savepoints in SQLite
+--------------------
+
+While SQLite ≥ 3.6.8 supports savepoints, a flaw in the design of the
+:mod:`sqlite3` module makes them hardly usable.
+
+When autocommit is enabled, savepoints don't make sense. When it's disabled,
+:mod:`sqlite3` commits implicitly before savepoint statements. (In fact, it
+commits before any statement other than ``SELECT``, ``INSERT``, ``UPDATE``,
+``DELETE`` and ``REPLACE``.) This bug has two consequences:
+
+- The low level APIs for savepoints are only usable inside a transaction ie.
+ inside an :func:`atomic` block.
+- It's impossible to use :func:`atomic` when autocommit is turned off.
Transactions in MySQL
-=====================
+---------------------
If you're using MySQL, your tables may or may not support transactions; it
depends on your MySQL version and the table types you're using. (By
@@ -303,28 +398,34 @@ depends on your MySQL version and the table types you're using. (By
peculiarities are outside the scope of this article, but the MySQL site has
`information on MySQL transactions`_.
-If your MySQL setup does *not* support transactions, then Django will function
-in auto-commit mode: Statements will be executed and committed as soon as
-they're called. If your MySQL setup *does* support transactions, Django will
-handle transactions as explained in this document.
+If your MySQL setup does *not* support transactions, then Django will always
+function in autocommit mode: statements will be executed and committed as soon
+as they're called. If your MySQL setup *does* support transactions, Django
+will handle transactions as explained in this document.
.. _information on MySQL transactions: http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html
Handling exceptions within PostgreSQL transactions
-==================================================
+--------------------------------------------------
+
+.. note::
+
+ This section is relevant only if you're implementing your own transaction
+ management. This problem cannot occur in Django's default mode and
+ :func:`atomic` handles it automatically.
-When a call to a PostgreSQL cursor raises an exception (typically
-``IntegrityError``), all subsequent SQL in the same transaction will fail with
-the error "current transaction is aborted, queries ignored until end of
-transaction block". Whilst simple use of ``save()`` is unlikely to raise an
-exception in PostgreSQL, there are more advanced usage patterns which
-might, such as saving objects with unique fields, saving using the
+Inside a transaction, when a call to a PostgreSQL cursor raises an exception
+(typically ``IntegrityError``), all subsequent SQL in the same transaction
+will fail with the error "current transaction is aborted, queries ignored
+until end of transaction block". Whilst simple use of ``save()`` is unlikely
+to raise an exception in PostgreSQL, there are more advanced usage patterns
+which might, such as saving objects with unique fields, saving using the
force_insert/force_update flag, or invoking custom SQL.
There are several ways to recover from this sort of error.
Transaction rollback
---------------------
+~~~~~~~~~~~~~~~~~~~~
The first option is to roll back the entire transaction. For example::
@@ -341,13 +442,13 @@ made by ``a.save()`` would be lost, even though that operation raised no error
itself.
Savepoint rollback
-------------------
+~~~~~~~~~~~~~~~~~~
-If you are using PostgreSQL 8 or later, you can use :ref:`savepoints
-<topics-db-transactions-savepoints>` to control the extent of a rollback.
-Before performing a database operation that could fail, you can set or update
-the savepoint; that way, if the operation fails, you can roll back the single
-offending operation, rather than the entire transaction. For example::
+You can use :ref:`savepoints <topics-db-transactions-savepoints>` to control
+the extent of a rollback. Before performing a database operation that could
+fail, you can set or update the savepoint; that way, if the operation fails,
+you can roll back the single offending operation, rather than the entire
+transaction. For example::
a.save() # Succeeds, and never undone by savepoint rollback
try:
@@ -361,25 +462,224 @@ offending operation, rather than the entire transaction. For example::
In this example, ``a.save()`` will not be undone in the case where
``b.save()`` raises an exception.
-Database-level autocommit
--------------------------
+.. _transactions-upgrading-from-1.5:
+
+Changes from Django 1.5 and earlier
+===================================
+
+The features described below were deprecated in Django 1.6 and will be removed
+in Django 1.8. They're documented in order to ease the migration to the new
+transaction management APIs.
+
+Legacy APIs
+-----------
+
+The following functions, defined in ``django.db.transaction``, provided a way
+to control transactions on a per-function or per-code-block basis. They could
+be used as decorators or as context managers, and they accepted a ``using``
+argument, exactly like :func:`atomic`.
+
+.. function:: autocommit
+
+ Enable Django's default autocommit behavior.
+
+ Transactions will be committed as soon as you call ``model.save()``,
+ ``model.delete()``, or any other function that writes to the database.
+
+.. function:: commit_on_success
+
+ Use a single transaction for all the work done in a function.
+
+ If the function returns successfully, then Django will commit all work done
+ within the function at that point. If the function raises an exception,
+ though, Django will roll back the transaction.
+
+.. function:: commit_manually
+
+ Tells Django you'll be managing the transaction on your own.
+
+ Whether you are writing or simply reading from the database, you must
+ ``commit()`` or ``rollback()`` explicitly or Django will raise a
+ :exc:`TransactionManagementError` exception. This is required when reading
+ from the database because ``SELECT`` statements may call functions which
+ modify tables, and thus it is impossible to know if any data has been
+ modified.
+
+.. _transaction-states:
+
+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..
+
+At any time, each database connection is in one of these two states:
+
+- **auto mode**: autocommit is enabled;
+- **managed mode**: autocommit is disabled.
+
+Django starts in auto mode. ``TransactionMiddleware``,
+:func:`commit_on_success` and :func:`commit_manually` activate managed mode;
+:func:`autocommit` activates auto mode.
+
+Internally, Django keeps a stack of states. Activations and deactivations must
+be balanced.
+
+For example, :func:`commit_on_success` switches to managed mode when entering
+the block of code it controls; when exiting the block, it commits or
+rollbacks, and switches back to auto mode.
+
+So :func:`commit_on_success` really has two effects: it changes the
+transaction state and it defines an transaction block. Nesting will give the
+expected results in terms of transaction state, but not in terms of
+transaction semantics. Most often, the inner block will commit, breaking the
+atomicity of the outer block.
+
+:func:`autocommit` and :func:`commit_manually` have similar limitations.
+
+API changes
+-----------
+
+Transaction middleware
+~~~~~~~~~~~~~~~~~~~~~~
-With PostgreSQL 8.2 or later, there is an advanced option to run PostgreSQL
-with :doc:`database-level autocommit </ref/databases>`. If you use this option,
-there is no constantly open transaction, so it is always possible to continue
-after catching an exception. For example::
+In Django 1.6, ``TransactionMiddleware`` is deprecated and replaced
+:setting:`ATOMIC_REQUESTS <DATABASE-ATOMIC_REQUESTS>`. While the general
+behavior is the same, there are a few differences.
- a.save() # succeeds
+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.
+
+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
+
+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
+middleware after the transaction middleware, session creation was part of the
+transaction. :setting:`ATOMIC_REQUESTS <DATABASE-ATOMIC_REQUESTS>` only
+applies to the view itself.
+
+Managing transactions
+~~~~~~~~~~~~~~~~~~~~~
+
+Starting with Django 1.6, :func:`atomic` is the only supported API for
+defining a transaction. Unlike the deprecated APIs, it's nestable and always
+guarantees atomicity.
+
+In most cases, it will be a drop-in replacement for :func:`commit_on_success`.
+
+During the deprecation period, it's possible to use :func:`atomic` within
+:func:`autocommit`, :func:`commit_on_success` or :func:`commit_manually`.
+However, the reverse is forbidden, because nesting the old decorators /
+context managers breaks atomicity.
+
+Managing autocommit
+~~~~~~~~~~~~~~~~~~~
+
+Django 1.6 introduces an explicit :ref:`API for mananging autocommit
+<managing-autocommit>`.
+
+To disable autocommit temporarily, instead of::
+
+ with transaction.commit_manually():
+ # do stuff
+
+you should now use::
+
+ transaction.set_autocommit(False)
try:
- b.save() # Could throw exception
- except IntegrityError:
- pass
- c.save() # succeeds
+ # do stuff
+ finally:
+ transaction.set_autocommit(True)
-.. note::
+To enable autocommit temporarily, instead of::
+
+ with transaction.autocommit():
+ # do stuff
+
+you should now use::
+
+ transaction.set_autocommit(True)
+ try:
+ # do stuff
+ finally:
+ transaction.set_autocommit(False)
+
+Disabling transaction management
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Instead of setting ``TRANSACTIONS_MANAGED = True``, set the ``AUTOCOMMIT`` key
+to ``False`` in the configuration of each database, as explained in
+:ref:`deactivate-transaction-management`.
+
+Backwards incompatibilities
+---------------------------
+
+Since version 1.6, Django uses database-level autocommit in auto mode.
+Previously, it implemented application-level autocommit by triggering a commit
+after each ORM write.
+
+As a consequence, each database query (for instance, an ORM read) started a
+transaction that lasted until the next ORM write. Such "automatic
+transactions" no longer exist in Django 1.6.
+
+There are four known scenarios where this is backwards-incompatible.
+
+Note that managed mode isn't affected at all. This section assumes auto mode.
+See the :ref:`description of modes <transaction-states>` above.
+
+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`.
+
+To check for this problem, look for calls to ``cursor.execute()``. They're
+usually followed by a call to ``transaction.commit_unless_managed()``, which
+isn't useful any more and should be removed.
+
+Select for update
+~~~~~~~~~~~~~~~~~
+
+If you were relying on "automatic transactions" to provide locking between
+:meth:`~django.db.models.query.QuerySet.select_for_update` and a subsequent
+write operation — an extremely fragile design, but nonetheless possible — you
+must wrap the relevant code in :func:`atomic`.
+
+Using a high isolation level
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you were using the "repeatable read" isolation level or higher, and if you
+relied on "automatic transactions" to guarantee consistency between successive
+reads, the new behavior might be backwards-incompatible. To enforce
+consistency, you must wrap such sequences in :func:`atomic`.
+
+MySQL defaults to "repeatable read" and SQLite to "serializable"; they may be
+affected by this problem.
+
+At the "read committed" isolation level or lower, "automatic transactions"
+have no effect on the semantics of any sequence of ORM operations.
+
+PostgreSQL and Oracle default to "read committed" and aren't affected, unless
+you changed the isolation level.
+
+Using unsupported database features
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- This is not the same as the :ref:`autocommit decorator
- <topics-db-transactions-autocommit>`. When using database level autocommit
- there is no database transaction at all. The ``autocommit`` decorator
- still uses transactions, automatically committing each transaction when
- a database modifying operation occurs.
+With triggers, views, or functions, it's possible to make ORM reads result in
+database modifications. Django 1.5 and earlier doesn't deal with this case and
+it's theoretically possible to observe a different behavior after upgrading to
+Django 1.6 or later. In doubt, use :func:`atomic` to enforce integrity.
diff --git a/docs/topics/files.txt b/docs/topics/files.txt
index 66e104759a..c05f98ef7e 100644
--- a/docs/topics/files.txt
+++ b/docs/topics/files.txt
@@ -2,7 +2,10 @@
Managing files
==============
-This document describes Django's file access APIs.
+This document describes Django's file access APIs for files such as those
+uploaded by a user. The lower level APIs are general enough that you could use
+them for other purposes. If you want to handle "static files" (JS, CSS, etc),
+see :doc:`/howto/static-files/index`.
By default, Django stores files locally, using the :setting:`MEDIA_ROOT` and
:setting:`MEDIA_URL` settings. The examples below assume that you're using these
@@ -93,7 +96,7 @@ The following approach may be used to close files automatically::
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:
+may lead to the following error::
IOError: [Errno 24] Too many open files
diff --git a/docs/topics/forms/formsets.txt b/docs/topics/forms/formsets.txt
index 7c1771b758..269ac5b4b6 100644
--- a/docs/topics/forms/formsets.txt
+++ b/docs/topics/forms/formsets.txt
@@ -3,7 +3,9 @@
Formsets
========
-A formset is a layer of abstraction to working with multiple forms on the same
+.. class:: django.forms.formset.BaseFormSet
+
+A formset is a layer of abstraction to work with multiple forms on the same
page. It can be best compared to a data grid. Let's say you have the following
form::
@@ -30,14 +32,14 @@ would with a regular form::
As you can see it only displayed one empty form. The number of empty forms
that is displayed is controlled by the ``extra`` parameter. By default,
-``formset_factory`` defines one extra form; the following example will
-display two blank forms::
+:func:`~django.forms.formsets.formset_factory` defines one extra form; the
+following example will display two blank forms::
>>> ArticleFormSet = formset_factory(ArticleForm, extra=2)
Iterating over the ``formset`` will render the forms in the order they were
created. You can change this order by providing an alternate implementation for
-the :meth:`__iter__()` method.
+the ``__iter__()`` method.
Formsets can also be indexed into, which returns the corresponding form. If you
override ``__iter__``, you will need to also override ``__getitem__`` to have
@@ -82,11 +84,12 @@ list of dictionaries as the initial data.
Limiting the maximum number of forms
------------------------------------
-The ``max_num`` parameter to ``formset_factory`` gives you the ability to
-limit the maximum number of empty forms the formset will display::
+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::
>>> ArticleFormSet = formset_factory(ArticleForm, extra=2, max_num=1)
- >>> formset = ArticleFormset()
+ >>> formset = ArticleFormSet()
>>> for form in formset:
... print(form.as_table())
<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>
@@ -96,8 +99,22 @@ 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.
+A ``max_num`` value of ``None`` (the default) puts a high limit on the number
+of forms displayed (1000). In practice this is equivalent to no limit.
+
+If the number of forms in the initial data exceeds ``max_num``, all initial
+data forms will be displayed regardless. (No extra forms will be displayed.)
+
+By default, ``max_num`` only affects how many forms are displayed and does not
+affect validation. If ``validate_max=True`` is passed to the
+:func:`~django.forms.formsets.formset_factory`, then ``max_num`` will affect
+validation. See :ref:`validate_max`.
+
+.. versionchanged:: 1.6
+ The ``validate_max`` parameter was added to
+ :func:`~django.forms.formsets.formset_factory`. Also, the behavior of
+ ``FormSet`` was brought in line with that of ``ModelFormSet`` so that it
+ displays initial data regardless of ``max_num``.
Formset validation
------------------
@@ -139,8 +156,6 @@ As we can see, ``formset.errors`` is a list whose entries correspond to the
forms in the formset. Validation was performed for each of the two forms, and
the expected error message appears for the second item.
-.. versionadded:: 1.4
-
We can also check if form data differs from the initial data (i.e. the form was
sent without any data)::
@@ -178,7 +193,10 @@ this management data, an exception will be raised::
It is used to keep track of how many form instances are being displayed. If
you are adding new forms via JavaScript, you should increment the count fields
-in this form as well.
+in this form as well. On the other hand, if you are using JavaScript to allow
+deletion of existing objects, then you need to ensure the ones being removed
+are properly marked for deletion by including ``form-#-DELETE`` in the ``POST``
+data. It is expected that all forms are present in the ``POST`` data regardless.
The management form is available as an attribute of the formset
itself. When rendering a formset in a template, you can include all
@@ -248,14 +266,59 @@ The formset ``clean`` method is called after all the ``Form.clean`` methods
have been called. The errors will be found using the ``non_form_errors()``
method on the formset.
+.. _validate_max:
+
+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``.
+
+ >>> ArticleFormSet = formset_factory(ArticleForm, max_num=1, validate_max=True)
+ >>> data = {
+ ... 'form-TOTAL_FORMS': u'2',
+ ... 'form-INITIAL_FORMS': u'0',
+ ... 'form-MAX_NUM_FORMS': u'',
+ ... 'form-0-title': u'Test',
+ ... 'form-0-pub_date': u'1904-06-16',
+ ... 'form-1-title': u'Test 2',
+ ... 'form-1-pub_date': u'1912-06-23',
+ ... }
+ >>> formset = ArticleFormSet(data)
+ >>> formset.is_valid()
+ False
+ >>> formset.errors
+ [{}, {}]
+ >>> formset.non_form_errors()
+ [u'Please submit 1 or fewer forms.']
+
+``validate_max=True`` validates against ``max_num`` strictly even if
+``max_num`` was exceeded because the amount of initial data supplied was
+excessive.
+
+Applications which need more customizable validation of the number of forms
+should use custom formset validation.
+
+.. note::
+
+ Regardless of ``validate_max``, if the number of forms in a data set
+ exceeds ``max_num`` by more than 1000, then the form will fail to validate
+ as if ``validate_max`` were set, and additionally only the first 1000
+ forms above ``max_num`` will be validated. The remainder will be
+ truncated entirely. This is to protect against memory exhaustion attacks
+ using forged POST requests.
+
+.. versionchanged:: 1.6
+ The ``validate_max`` parameter was added to
+ :func:`~django.forms.formsets.formset_factory`.
+
Dealing with ordering and deletion of forms
-------------------------------------------
-Common use cases with a formset is dealing with ordering and deletion of the
-form instances. This has been dealt with for you. The ``formset_factory``
-provides two optional parameters ``can_order`` and ``can_delete`` that will do
-the extra work of adding the extra fields and providing simpler ways of
-getting to that data.
+The :func:`~django.forms.formsets.formset_factory` provides two optional
+parameters ``can_order`` and ``can_delete`` to help with ordering of forms in
+formsets and deletion of forms from a formset.
``can_order``
~~~~~~~~~~~~~
@@ -273,13 +336,13 @@ Lets you create a formset with the ability to order::
... print(form.as_table())
<tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" value="Article #1" 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" value="2008-05-10" id="id_form-0-pub_date" /></td></tr>
- <tr><th><label for="id_form-0-ORDER">Order:</label></th><td><input type="text" name="form-0-ORDER" value="1" id="id_form-0-ORDER" /></td></tr>
+ <tr><th><label for="id_form-0-ORDER">Order:</label></th><td><input type="number" name="form-0-ORDER" value="1" id="id_form-0-ORDER" /></td></tr>
<tr><th><label for="id_form-1-title">Title:</label></th><td><input type="text" name="form-1-title" value="Article #2" id="id_form-1-title" /></td></tr>
<tr><th><label for="id_form-1-pub_date">Pub date:</label></th><td><input type="text" name="form-1-pub_date" value="2008-05-11" id="id_form-1-pub_date" /></td></tr>
- <tr><th><label for="id_form-1-ORDER">Order:</label></th><td><input type="text" name="form-1-ORDER" value="2" id="id_form-1-ORDER" /></td></tr>
+ <tr><th><label for="id_form-1-ORDER">Order:</label></th><td><input type="number" name="form-1-ORDER" value="2" id="id_form-1-ORDER" /></td></tr>
<tr><th><label for="id_form-2-title">Title:</label></th><td><input type="text" name="form-2-title" id="id_form-2-title" /></td></tr>
<tr><th><label for="id_form-2-pub_date">Pub date:</label></th><td><input type="text" name="form-2-pub_date" id="id_form-2-pub_date" /></td></tr>
- <tr><th><label for="id_form-2-ORDER">Order:</label></th><td><input type="text" name="form-2-ORDER" id="id_form-2-ORDER" /></td></tr>
+ <tr><th><label for="id_form-2-ORDER">Order:</label></th><td><input type="number" name="form-2-ORDER" id="id_form-2-ORDER" /></td></tr>
This adds an additional field to each form. This new field is named ``ORDER``
and is an ``forms.IntegerField``. For the forms that came from the initial
diff --git a/docs/topics/forms/index.txt b/docs/topics/forms/index.txt
index 4693de6c7e..ac58acc9e3 100644
--- a/docs/topics/forms/index.txt
+++ b/docs/topics/forms/index.txt
@@ -197,6 +197,14 @@ context variable ``form``. Here's a simple example template::
The form only outputs its own fields; it is up to you to provide the surrounding
``<form>`` tags and the submit button.
+If your form includes uploaded files, be sure to include
+``enctype="multipart/form-data"`` in the ``form`` element. If you wish to write
+a generic template that will work whether or not the form has files, you can
+use the :meth:`~django.forms.Form.is_multipart` attribute on the form::
+
+ <form action="/contact/" method="post"
+ {% if form.is_multipart %}enctype="multipart/form-data"{% endif %}>
+
.. admonition:: Forms and Cross Site Request Forgery protection
Django ships with an easy-to-use :doc:`protection against Cross Site Request
@@ -215,7 +223,7 @@ wrapped in a paragraph. Here's the output for our example template::
<p><label for="id_message">Message:</label>
<input type="text" name="message" id="id_message" /></p>
<p><label for="id_sender">Sender:</label>
- <input type="text" name="sender" id="id_sender" /></p>
+ <input type="email" name="sender" id="id_sender" /></p>
<p><label for="id_cc_myself">Cc myself:</label>
<input type="checkbox" name="cc_myself" id="id_cc_myself" /></p>
<input type="submit" value="Submit" />
@@ -300,9 +308,9 @@ loop::
<p><input type="submit" value="Send message" /></p>
</form>
-Within this loop, ``{{ field }}`` is an instance of :class:`BoundField`.
-``BoundField`` also has the following attributes, which can be useful in your
-templates:
+Within this loop, ``{{ field }}`` is an instance of
+:class:`~django.forms.BoundField`. ``BoundField`` also has the following
+attributes, which can be useful in your templates:
``{{ field.label }}``
The label of the field, e.g. ``Email address``.
@@ -328,7 +336,7 @@ templates:
case, each object in the loop is a simple string containing the error
message.
-``field.is_hidden``
+``{{ field.is_hidden }}``
This attribute is ``True`` if the form field is a hidden field and
``False`` otherwise. It's not particularly useful as a template
variable, but could be useful in conditional tests such as::
@@ -337,6 +345,12 @@ templates:
{# Do something special #}
{% endif %}
+``{{ field.field }}``
+ The :class:`~django.forms.Field` instance from the form class that
+ this :class:`~django.forms.BoundField` wraps. You can use it to access
+ :class:`~django.forms.Field` attributes , e.g.
+ ``{{ char_field.field.max_length }}``.
+
Looping over hidden and visible fields
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/docs/topics/forms/modelforms.txt b/docs/topics/forms/modelforms.txt
index 233346db0d..eaf2bbbaf2 100644
--- a/docs/topics/forms/modelforms.txt
+++ b/docs/topics/forms/modelforms.txt
@@ -222,11 +222,6 @@ supplied, ``save()`` will update that instance. If it's not supplied,
# Save a new Article object from the form's data.
>>> new_article = f.save()
- # Create a form to edit an existing Article.
- >>> a = Article.objects.get(pk=1)
- >>> f = ArticleForm(instance=a)
- >>> f.save()
-
# Create a form to edit an existing Article, but use
# POST data to populate the form.
>>> a = Article.objects.get(pk=1)
@@ -544,11 +539,40 @@ for more on how field cleaning and validation work. Also, your model's
:ref:`Validating objects <validating-objects>` for more information on the
model's ``clean()`` hook.
+.. _modelforms-factory:
+
+ModelForm factory function
+--------------------------
+
+You can create forms from a given model using the standalone function
+:func:`~django.forms.models.modelform_factory`, instead of using a class
+definition. This may be more convenient if you do not have many customizations
+to make::
+
+ >>> from django.forms.models import modelform_factory
+ >>> BookForm = modelform_factory(Book)
+
+This can also be used to make simple modifications to existing forms, for
+example by specifying which fields should be displayed::
+
+ >>> Form = modelform_factory(Book, form=BookForm, fields=("author",))
+
+... or which fields should be excluded::
+
+ >>> Form = modelform_factory(Book, form=BookForm, exclude=("title",))
+
+You can also specify the widgets to be used for a given field::
+
+ >>> from django.forms import Textarea
+ >>> Form = modelform_factory(Book, form=BookForm, widgets={"title": Textarea()})
+
.. _model-formsets:
Model formsets
==============
+.. class:: models.BaseModelFormSet
+
Like :doc:`regular formsets </topics/forms/formsets>`, Django provides a couple
of enhanced formset classes that make it easy to work with Django models. Let's
reuse the ``Author`` model from above::
@@ -572,9 +596,11 @@ with the ``Author`` model. It works just like a regular formset::
<tr><th><label for="id_form-0-birth_date">Birth date:</label></th><td><input type="text" name="form-0-birth_date" id="id_form-0-birth_date" /><input type="hidden" name="form-0-id" id="id_form-0-id" /></td></tr>
.. note::
- ``modelformset_factory`` uses ``formset_factory`` to generate formsets.
- This means that a model formset is just an extension of a basic formset
- that knows how to interact with a particular model.
+
+ :func:`~django.forms.models.modelformset_factory` uses
+ :func:`~django.forms.formsets.formset_factory` to generate formsets. This
+ means that a model formset is just an extension of a basic formset that
+ knows how to interact with a particular model.
Changing the queryset
---------------------
@@ -620,16 +646,28 @@ exclude::
>>> AuthorFormSet = modelformset_factory(Author, exclude=('birth_date',))
+Specifying widgets to use in the form with ``widgets``
+------------------------------------------------------
+
+.. versionadded:: 1.6
+
+Using the ``widgets`` parameter, you can specify a dictionary of values to
+customize the ``ModelForm``'s widget class for a particular field. This
+works the same way as the ``widgets`` dictionary on the inner ``Meta``
+class of a ``ModelForm`` works::
+
+ >>> AuthorFormSet = modelformset_factory(
+ ... Author, widgets={'name': Textarea(attrs={'cols': 80, 'rows': 20})
+
Providing initial values
------------------------
-.. versionadded:: 1.4
-
As with regular formsets, it's possible to :ref:`specify initial data
<formsets-initial-data>` for forms in the formset by specifying an ``initial``
parameter when instantiating the model formset class returned by
-``modelformset_factory``. However, with model formsets, the initial values only
-apply to extra forms, those that aren't bound to an existing object instance.
+:func:`~django.forms.models.modelformset_factory`. However, with model
+formsets, the initial values only apply to extra forms, those that aren't bound
+to an existing object instance.
.. _saving-objects-in-the-formset:
@@ -675,7 +713,8 @@ Limiting the number of editable objects
---------------------------------------
As with regular formsets, you can use the ``max_num`` and ``extra`` parameters
-to ``modelformset_factory`` to limit the number of extra forms displayed.
+to :func:`~django.forms.models.modelformset_factory` to limit the number of
+extra forms displayed.
``max_num`` does not prevent existing objects from being displayed::
@@ -700,8 +739,8 @@ 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>
-A ``max_num`` value of ``None`` (the default) puts no limit on the number of
-forms displayed.
+A ``max_num`` value of ``None`` (the default) puts a high limit on the number
+of forms displayed (1000). In practice this is equivalent to no limit.
Using a model formset in a view
-------------------------------
@@ -827,6 +866,8 @@ primary key that isn't called ``id``, make sure it gets rendered.)
.. highlight:: python
+.. _inline-formsets:
+
Inline formsets
===============
@@ -850,7 +891,9 @@ a particular author, you could do this::
>>> formset = BookFormSet(instance=author)
.. note::
- ``inlineformset_factory`` uses ``modelformset_factory`` and marks
+
+ :func:`~django.forms.models.inlineformset_factory` uses
+ :func:`~django.forms.models.modelformset_factory` and marks
``can_delete=True``.
.. seealso::
@@ -869,7 +912,8 @@ the following model::
to_friend = models.ForeignKey(Friend)
length_in_months = models.IntegerField()
-To resolve this, you can use ``fk_name`` to ``inlineformset_factory``::
+To resolve this, you can use ``fk_name`` to
+:func:`~django.forms.models.inlineformset_factory`::
>>> FriendshipFormSet = inlineformset_factory(Friend, Friendship, fk_name="from_friend")
@@ -895,3 +939,13 @@ of a model. Here's how you can do that::
})
Notice how we pass ``instance`` in both the ``POST`` and ``GET`` cases.
+
+Specifying widgets to use in the inline form
+--------------------------------------------
+
+.. versionadded:: 1.6
+
+``inlineformset_factory`` uses ``modelformset_factory`` and passes most
+of its arguments to ``modelformset_factory``. This means you can use
+the ``widgets`` parameter in much the same way as passing it to
+``modelformset_factory``. See `Specifying widgets to use in the form with widgets`_ above.
diff --git a/docs/topics/http/decorators.txt b/docs/topics/http/decorators.txt
index 83d14a0777..25616a44c0 100644
--- a/docs/topics/http/decorators.txt
+++ b/docs/topics/http/decorators.txt
@@ -39,8 +39,6 @@ a :class:`django.http.HttpResponseNotAllowed` if the conditions are not met.
.. function:: require_safe()
- .. versionadded:: 1.4
-
Decorator to require that a view only accept the GET and HEAD methods.
These methods are commonly considered "safe" because they should not have
the significance of taking an action other than retrieving the requested
diff --git a/docs/topics/http/file-uploads.txt b/docs/topics/http/file-uploads.txt
index b3a830c25e..80bd5f3c44 100644
--- a/docs/topics/http/file-uploads.txt
+++ b/docs/topics/http/file-uploads.txt
@@ -201,7 +201,7 @@ corresponding :class:`~django.db.models.FileField` when calling
return HttpResponseRedirect('/success/url/')
else:
form = ModelFormWithFileField()
- return render('upload.html', {'form': form})
+ return render(request, '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
@@ -221,14 +221,14 @@ field in the model::
return HttpResponseRedirect('/success/url/')
else:
form = UploadFileForm()
- return render('upload.html', {'form': form})
+ return render(request, 'upload.html', {'form': form})
``UploadedFile`` objects
========================
-In addition to those inherited from :class:`File`, all ``UploadedFile`` objects
-define the following methods/attributes:
+In addition to those inherited from :class:`~django.core.files.File`, all
+``UploadedFile`` objects define the following methods/attributes:
.. attribute:: UploadedFile.content_type
diff --git a/docs/topics/http/sessions.txt b/docs/topics/http/sessions.txt
index baf8aa5cb5..f5c688e254 100644
--- a/docs/topics/http/sessions.txt
+++ b/docs/topics/http/sessions.txt
@@ -28,6 +28,8 @@ If you don't want to use sessions, you might as well remove the
``'django.contrib.sessions'`` from your :setting:`INSTALLED_APPS`.
It'll save you a small bit of overhead.
+.. _configuring-sessions:
+
Configuring the session engine
==============================
@@ -110,8 +112,6 @@ server has permissions to read and write to this location.
Using cookie-based sessions
---------------------------
-.. versionadded:: 1.4
-
To use cookies-based sessions, set the :setting:`SESSION_ENGINE` setting to
``"django.contrib.sessions.backends.signed_cookies"``. The session data will be
stored using Django's tools for :doc:`cryptographic signing </topics/signing>`
@@ -264,7 +264,7 @@ You can edit it multiple times.
- ``modification``: last modification of the session, as a
:class:`~datetime.datetime` object. Defaults to the current time.
- ``expiry``: expiry information for the session, as a
- :class:`~datetime.datetime` object, an :class:`int` (in seconds), or
+ :class:`~datetime.datetime` object, an :func:`int` (in seconds), or
``None``. Defaults to the value stored in the session by
:meth:`set_expiry`, if there is one, or ``None``.
@@ -453,6 +453,8 @@ session cookie is sent.
.. versionchanged:: 1.5
The session is not saved if the response's status code is 500.
+.. _browser-length-vs-persistent-sessions:
+
Browser-length sessions vs. persistent sessions
===============================================
@@ -474,6 +476,16 @@ This setting is a global default and can be overwritten at a per-session level
by explicitly calling the :meth:`~backends.base.SessionBase.set_expiry` method
of ``request.session`` as described above in `using sessions in views`_.
+.. note::
+
+ Some browsers (Chrome, for example) provide settings that allow users to
+ continue browsing sessions after closing and re-opening the browser. In
+ some cases, this can interfere with the
+ :setting:`SESSION_EXPIRE_AT_BROWSER_CLOSE` setting and prevent sessions
+ from expiring on browser close. Please be aware of this while testing
+ Django applications which have the
+ :setting:`SESSION_EXPIRE_AT_BROWSER_CLOSE` setting enabled.
+
Clearing the session store
==========================
@@ -501,114 +513,20 @@ session data is stored by the users' browsers.
Settings
========
-A few :doc:`Django settings </ref/settings>` give you control over session
+A few :ref:`Django settings <settings-sessions>` give you control over session
behavior:
-SESSION_ENGINE
---------------
-
-Default: ``django.contrib.sessions.backends.db``
-
-Controls where Django stores session data. Valid values are:
-
-* ``'django.contrib.sessions.backends.db'``
-* ``'django.contrib.sessions.backends.file'``
-* ``'django.contrib.sessions.backends.cache'``
-* ``'django.contrib.sessions.backends.cached_db'``
-* ``'django.contrib.sessions.backends.signed_cookies'``
-
-See `configuring the session engine`_ for more details.
-
-SESSION_FILE_PATH
------------------
-
-Default: ``/tmp/``
-
-If you're using file-based session storage, this sets the directory in
-which Django will store session data.
-
-SESSION_COOKIE_AGE
-------------------
-
-Default: ``1209600`` (2 weeks, in seconds)
-
-The age of session cookies, in seconds.
-
-SESSION_COOKIE_DOMAIN
----------------------
-
-Default: ``None``
-
-The domain to use for session cookies. Set this to a string such as
-``".example.com"`` (note the leading dot!) for cross-domain cookies, or use
-``None`` for a standard domain cookie.
-
-SESSION_COOKIE_HTTPONLY
------------------------
-
-Default: ``True``
-
-Whether to use HTTPOnly flag on the session cookie. If this is set to
-``True``, client-side JavaScript will not to be able to access the
-session cookie.
-
-HTTPOnly_ is a flag included in a Set-Cookie HTTP response header. It
-is not part of the :rfc:`2109` standard for cookies, and it isn't honored
-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.
-
-.. versionchanged:: 1.4
- The default value of the setting was changed from ``False`` to ``True``.
-
-.. _HTTPOnly: https://www.owasp.org/index.php/HTTPOnly
-
-SESSION_COOKIE_NAME
--------------------
-
-Default: ``'sessionid'``
-
-The name of the cookie to use for sessions. This can be whatever you want.
-
-SESSION_COOKIE_PATH
--------------------
-
-Default: ``'/'``
-
-The path set on the session cookie. This should either match the URL path of
-your Django installation or be parent of that path.
-
-This is useful if you have multiple Django instances running under the same
-hostname. They can use different cookie paths, and each instance will only see
-its own session cookie.
-
-SESSION_COOKIE_SECURE
----------------------
-
-Default: ``False``
-
-Whether to use a secure cookie for the session cookie. If this is set to
-``True``, the cookie will be marked as "secure," which means browsers may
-ensure that the cookie is only sent under an HTTPS connection.
-
-SESSION_EXPIRE_AT_BROWSER_CLOSE
--------------------------------
-
-Default: ``False``
-
-Whether to expire the session when the user closes his or her browser. See
-"Browser-length sessions vs. persistent sessions" above.
-
-SESSION_SAVE_EVERY_REQUEST
---------------------------
-
-Default: ``False``
-
-Whether to save the session data on every request. If this is ``False``
-(default), then the session data will only be saved if it has been modified --
-that is, if any of its dictionary values have been assigned or deleted.
-
-.. _Django settings: ../settings/
+* :setting:`SESSION_CACHE_ALIAS`
+* :setting:`SESSION_COOKIE_AGE`
+* :setting:`SESSION_COOKIE_DOMAIN`
+* :setting:`SESSION_COOKIE_HTTPONLY`
+* :setting:`SESSION_COOKIE_NAME`
+* :setting:`SESSION_COOKIE_PATH`
+* :setting:`SESSION_COOKIE_SECURE`
+* :setting:`SESSION_ENGINE`
+* :setting:`SESSION_EXPIRE_AT_BROWSER_CLOSE`
+* :setting:`SESSION_FILE_PATH`
+* :setting:`SESSION_SAVE_EVERY_REQUEST`
Technical details
=================
diff --git a/docs/topics/http/shortcuts.txt b/docs/topics/http/shortcuts.txt
index b1b4700b73..961f0b9d96 100644
--- a/docs/topics/http/shortcuts.txt
+++ b/docs/topics/http/shortcuts.txt
@@ -50,6 +50,9 @@ Optional arguments
The MIME type to use for the resulting document. Defaults to the value of
the :setting:`DEFAULT_CONTENT_TYPE` setting.
+ .. versionchanged:: 1.5
+ This parameter used to be called ``mimetype``.
+
``status``
The status code for the response. Defaults to ``200``.
@@ -87,7 +90,7 @@ This example is equivalent to::
``render_to_response``
======================
-.. function:: render_to_response(template_name[, dictionary][, context_instance][, mimetype])
+.. function:: render_to_response(template_name[, dictionary][, context_instance][, content_type])
Renders a given template with a given context dictionary and returns an
:class:`~django.http.HttpResponse` object with that rendered text.
@@ -121,10 +124,14 @@ Optional arguments
my_data_dictionary,
context_instance=RequestContext(request))
-``mimetype``
+``content_type``
The MIME type to use for the resulting document. Defaults to the value of
the :setting:`DEFAULT_CONTENT_TYPE` setting.
+ .. versionchanged:: 1.5
+ This parameter used to be called ``mimetype``.
+
+
Example
-------
@@ -148,7 +155,7 @@ This example is equivalent to::
t = loader.get_template('myapp/template.html')
c = Context({'foo': 'bar'})
return HttpResponse(t.render(c),
- mimetype="application/xhtml+xml")
+ content_type="application/xhtml+xml")
``redirect``
============
@@ -162,8 +169,9 @@ This example is equivalent to::
* A model: the model's `get_absolute_url()` function will be called.
- * A view name, possibly with arguments: `urlresolvers.reverse()` will
- be used to reverse-resolve the name.
+ * A view name, possibly with arguments: :func:`urlresolvers.reverse
+ <django.core.urlresolvers.reverse>` will be used to reverse-resolve the
+ name.
* A URL, which will be used as-is for the redirect location.
diff --git a/docs/topics/http/urls.txt b/docs/topics/http/urls.txt
index 00c07da6ea..9a96199dba 100644
--- a/docs/topics/http/urls.txt
+++ b/docs/topics/http/urls.txt
@@ -26,10 +26,9 @@ This mapping can be as short or as long as needed. It can reference other
mappings. And, because it's pure Python code, it can be constructed
dynamically.
-.. versionadded:: 1.4
- Django also provides a way to translate URLs according to the active
- language. See the :ref:`internationalization documentation
- <url-internationalization>` for more information.
+Django also provides a way to translate URLs according to the active
+language. See the :ref:`internationalization documentation
+<url-internationalization>` for more information.
.. _how-django-processes-a-request:
@@ -67,13 +66,13 @@ Example
Here's a sample URLconf::
- from django.conf.urls import patterns, url, include
+ from django.conf.urls import patterns, url
urlpatterns = patterns('',
- (r'^articles/2003/$', 'news.views.special_case_2003'),
- (r'^articles/(\d{4})/$', 'news.views.year_archive'),
- (r'^articles/(\d{4})/(\d{2})/$', 'news.views.month_archive'),
- (r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'news.views.article_detail'),
+ url(r'^articles/2003/$', 'news.views.special_case_2003'),
+ url(r'^articles/(\d{4})/$', 'news.views.year_archive'),
+ url(r'^articles/(\d{4})/(\d{2})/$', 'news.views.month_archive'),
+ url(r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'news.views.article_detail'),
)
Notes:
@@ -125,10 +124,10 @@ is ``(?P<name>pattern)``, where ``name`` is the name of the group and
Here's the above example URLconf, rewritten to use named groups::
urlpatterns = patterns('',
- (r'^articles/2003/$', 'news.views.special_case_2003'),
- (r'^articles/(?P<year>\d{4})/$', 'news.views.year_archive'),
- (r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/$', 'news.views.month_archive'),
- (r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/$', 'news.views.article_detail'),
+ url(r'^articles/2003/$', 'news.views.special_case_2003'),
+ url(r'^articles/(?P<year>\d{4})/$', 'news.views.year_archive'),
+ url(r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/$', 'news.views.month_archive'),
+ url(r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/$', 'news.views.article_detail'),
)
This accomplishes exactly the same thing as the previous example, with one
@@ -184,7 +183,7 @@ Each captured argument is sent to the view as a plain Python string, regardless
of what sort of match the regular expression makes. For example, in this
URLconf line::
- (r'^articles/(?P<year>\d{4})/$', 'news.views.year_archive'),
+ url(r'^articles/(?P<year>\d{4})/$', 'news.views.year_archive'),
...the ``year`` argument to ``news.views.year_archive()`` will be a string, not
an integer, even though the ``\d{4}`` will only match integer strings.
@@ -194,13 +193,14 @@ Here's an example URLconf and view::
# URLconf
urlpatterns = patterns('',
- (r'^blog/$', 'blog.views.page'),
- (r'^blog/page(?P<num>\d+)/$', 'blog.views.page'),
+ url(r'^blog/$', 'blog.views.page'),
+ url(r'^blog/page(?P<num>\d+)/$', 'blog.views.page'),
)
# View (in blog/views.py)
def page(request, num="1"):
# Output the appropriate page of blog entries, according to num.
+ ...
In the above example, both URL patterns point to the same view --
``blog.views.page`` -- but the first pattern doesn't capture anything from the
@@ -246,9 +246,6 @@ The variables are:
* ``handler500`` -- See :data:`django.conf.urls.handler500`.
* ``handler403`` -- See :data:`django.conf.urls.handler403`.
-.. versionadded:: 1.4
- ``handler403`` is new in Django 1.4.
-
.. _urlpatterns-view-prefix:
The view prefix
@@ -259,12 +256,12 @@ code duplication.
Here's the example URLconf from the :doc:`Django overview </intro/overview>`::
- from django.conf.urls import patterns, url, include
+ from django.conf.urls import patterns, url
urlpatterns = patterns('',
- (r'^articles/(\d{4})/$', 'news.views.year_archive'),
- (r'^articles/(\d{4})/(\d{2})/$', 'news.views.month_archive'),
- (r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'news.views.article_detail'),
+ url(r'^articles/(\d{4})/$', 'news.views.year_archive'),
+ url(r'^articles/(\d{4})/(\d{2})/$', 'news.views.month_archive'),
+ url(r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'news.views.article_detail'),
)
In this example, each view has a common prefix -- ``'news.views'``.
@@ -274,12 +271,12 @@ each view function.
With this in mind, the above example can be written more concisely as::
- from django.conf.urls import patterns, url, include
+ from django.conf.urls import patterns, url
urlpatterns = patterns('news.views',
- (r'^articles/(\d{4})/$', 'year_archive'),
- (r'^articles/(\d{4})/(\d{2})/$', 'month_archive'),
- (r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'article_detail'),
+ url(r'^articles/(\d{4})/$', 'year_archive'),
+ url(r'^articles/(\d{4})/(\d{2})/$', 'month_archive'),
+ url(r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'article_detail'),
)
Note that you don't put a trailing dot (``"."``) in the prefix. Django puts
@@ -295,25 +292,25 @@ Just add multiple ``patterns()`` objects together, like this:
Old::
- from django.conf.urls import patterns, url, include
+ from django.conf.urls import patterns, url
urlpatterns = patterns('',
- (r'^$', 'myapp.views.app_index'),
- (r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$', 'myapp.views.month_display'),
- (r'^tag/(?P<tag>\w+)/$', 'weblog.views.tag'),
+ url(r'^$', 'myapp.views.app_index'),
+ url(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$', 'myapp.views.month_display'),
+ url(r'^tag/(?P<tag>\w+)/$', 'weblog.views.tag'),
)
New::
- from django.conf.urls import patterns, url, include
+ from django.conf.urls import patterns, url
urlpatterns = patterns('myapp.views',
- (r'^$', 'app_index'),
- (r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$','month_display'),
+ url(r'^$', 'app_index'),
+ url(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$','month_display'),
)
urlpatterns += patterns('weblog.views',
- (r'^tag/(?P<tag>\w+)/$', 'tag'),
+ url(r'^tag/(?P<tag>\w+)/$', 'tag'),
)
.. _including-other-urlconfs:
@@ -327,14 +324,13 @@ essentially "roots" a set of URLs below other ones.
For example, here's an excerpt of the URLconf for the `Django Web site`_
itself. It includes a number of other URLconfs::
- from django.conf.urls import patterns, include
+ from django.conf.urls import include, patterns, url
urlpatterns = patterns('',
# ... snip ...
- (r'^comments/', include('django.contrib.comments.urls')),
- (r'^community/', include('django_website.aggregator.urls')),
- (r'^contact/', include('django_website.contact.urls')),
- (r'^r/', include('django.conf.urls.shortcut')),
+ url(r'^comments/', include('django.contrib.comments.urls')),
+ url(r'^community/', include('django_website.aggregator.urls')),
+ url(r'^contact/', include('django_website.contact.urls')),
# ... snip ...
)
@@ -349,7 +345,7 @@ URLconf Python module defining them as the ``include()`` argument but by using
directly the pattern list as returned by :func:`~django.conf.urls.patterns`
instead. For example, consider this URLconf::
- from django.conf.urls import patterns, url, include
+ from django.conf.urls import include, patterns, url
extra_patterns = patterns('',
url(r'^reports/(?P<id>\d+)/$', 'credit.views.report'),
@@ -358,8 +354,8 @@ instead. For example, consider this URLconf::
urlpatterns = patterns('',
url(r'^$', 'apps.main.views.homepage'),
- (r'^help/', include('apps.help.urls')),
- (r'^credit/', include(extra_patterns)),
+ url(r'^help/', include('apps.help.urls')),
+ url(r'^credit/', include(extra_patterns)),
)
In this example, the ``/credit/reports/`` URL will be handled by the
@@ -375,13 +371,13 @@ the following example is valid::
# In settings/urls/main.py
urlpatterns = patterns('',
- (r'^(?P<username>\w+)/blog/', include('foo.urls.blog')),
+ url(r'^(?P<username>\w+)/blog/', include('foo.urls.blog')),
)
# In foo/urls/blog.py
urlpatterns = patterns('foo.views',
- (r'^$', 'blog.index'),
- (r'^archive/$', 'blog.archive'),
+ url(r'^$', 'blog.index'),
+ url(r'^archive/$', 'blog.archive'),
)
In the above example, the captured ``"username"`` variable is passed to the
@@ -395,13 +391,14 @@ Passing extra options to view functions
URLconfs have a hook that lets you pass extra arguments to your view functions,
as a Python dictionary.
-Any URLconf tuple can have an optional third element, which should be a
-dictionary of extra keyword arguments to pass to the view function.
+The :func:`django.conf.urls.url` function can take an optional third argument
+which should be a dictionary of extra keyword arguments to pass to the view
+function.
For example::
urlpatterns = patterns('blog.views',
- (r'^blog/(?P<year>\d{4})/$', 'year_archive', {'foo': 'bar'}),
+ url(r'^blog/(?P<year>\d{4})/$', 'year_archive', {'foo': 'bar'}),
)
In this example, for a request to ``/blog/2005/``, Django will call
@@ -431,26 +428,26 @@ Set one::
# main.py
urlpatterns = patterns('',
- (r'^blog/', include('inner'), {'blogid': 3}),
+ url(r'^blog/', include('inner'), {'blogid': 3}),
)
# inner.py
urlpatterns = patterns('',
- (r'^archive/$', 'mysite.views.archive'),
- (r'^about/$', 'mysite.views.about'),
+ url(r'^archive/$', 'mysite.views.archive'),
+ url(r'^about/$', 'mysite.views.about'),
)
Set two::
# main.py
urlpatterns = patterns('',
- (r'^blog/', include('inner')),
+ url(r'^blog/', include('inner')),
)
# inner.py
urlpatterns = patterns('',
- (r'^archive/$', 'mysite.views.archive', {'blogid': 3}),
- (r'^about/$', 'mysite.views.about', {'blogid': 3}),
+ url(r'^archive/$', 'mysite.views.archive', {'blogid': 3}),
+ url(r'^about/$', 'mysite.views.about', {'blogid': 3}),
)
Note that extra options will *always* be passed to *every* line in the included
@@ -468,9 +465,9 @@ supported -- you can pass any callable object as the view.
For example, given this URLconf in "string" notation::
urlpatterns = patterns('',
- (r'^archive/$', 'mysite.views.archive'),
- (r'^about/$', 'mysite.views.about'),
- (r'^contact/$', 'mysite.views.contact'),
+ url(r'^archive/$', 'mysite.views.archive'),
+ url(r'^about/$', 'mysite.views.about'),
+ url(r'^contact/$', 'mysite.views.contact'),
)
You can accomplish the same thing by passing objects rather than strings. Just
@@ -479,9 +476,9 @@ be sure to import the objects::
from mysite.views import archive, about, contact
urlpatterns = patterns('',
- (r'^archive/$', archive),
- (r'^about/$', about),
- (r'^contact/$', contact),
+ url(r'^archive/$', archive),
+ url(r'^about/$', about),
+ url(r'^contact/$', contact),
)
The following example is functionally identical. It's just a bit more compact
@@ -491,9 +488,9 @@ each view individually::
from mysite import views
urlpatterns = patterns('',
- (r'^archive/$', views.archive),
- (r'^about/$', views.about),
- (r'^contact/$', views.contact),
+ url(r'^archive/$', views.archive),
+ url(r'^about/$', views.about),
+ url(r'^contact/$', views.contact),
)
The style you use is up to you.
@@ -507,7 +504,7 @@ imported::
from mysite.views import ClassBasedView
urlpatterns = patterns('',
- (r'^myview/$', ClassBasedView.as_view()),
+ url(r'^myview/$', ClassBasedView.as_view()),
)
Reverse resolution of URLs
@@ -616,8 +613,8 @@ your URLconf. For example, these two URL patterns both point to the ``archive``
view::
urlpatterns = patterns('',
- (r'^archive/(\d{4})/$', archive),
- (r'^archive-summary/(\d{4})/$', archive, {'summary': True}),
+ url(r'^archive/(\d{4})/$', archive),
+ url(r'^archive-summary/(\d{4})/$', archive, {'summary': True}),
)
This is completely valid, but it leads to problems when you try to do reverse
@@ -635,7 +632,7 @@ Here's the above example, rewritten to use named URL patterns::
urlpatterns = patterns('',
url(r'^archive/(\d{4})/$', archive, name="full-archive"),
- url(r'^archive-summary/(\d{4})/$', archive, {'summary': True}, "arch-summary"),
+ url(r'^archive-summary/(\d{4})/$', archive, {'summary': True}, name="arch-summary"),
)
With these names in place (``full-archive`` and ``arch-summary``), you can
@@ -647,7 +644,8 @@ target each pattern individually by using its name:
{% 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.
+``name`` parameter to :func:`django.conf.urls.url` allows you to tell them
+apart in templates.
The string used for the URL name can contain any characters you like. You are
not restricted to valid Python names.
@@ -790,7 +788,7 @@ Firstly, you can provide the :term:`application <application namespace>` and
:func:`django.conf.urls.include()` when you construct your URL patterns. For
example,::
- (r'^help/', include('apps.help.urls', namespace='foo', app_name='bar')),
+ url(r'^help/', include('apps.help.urls', namespace='foo', app_name='bar')),
This will include the URLs defined in ``apps.help.urls`` into the
:term:`application namespace` ``'bar'``, with the :term:`instance namespace`
@@ -810,7 +808,7 @@ For example::
url(r'^advanced/$', 'apps.help.views.views.advanced'),
)
- (r'^help/', include(help_patterns, 'bar', 'foo')),
+ url(r'^help/', include(help_patterns, 'bar', 'foo')),
This will include the nominated URL patterns into the given application and
instance namespace.
diff --git a/docs/topics/http/views.txt b/docs/topics/http/views.txt
index caa2882f37..f73ec4f5be 100644
--- a/docs/topics/http/views.txt
+++ b/docs/topics/http/views.txt
@@ -132,6 +132,8 @@ Customizing error views
The 404 (page not found) view
-----------------------------
+.. function:: django.views.defaults.page_not_found(request, template_name='404.html')
+
When you raise an ``Http404`` exception, Django loads a special view devoted
to handling 404 errors. By default, it's the view
``django.views.defaults.page_not_found``, which either produces a very simple
@@ -199,8 +201,6 @@ One thing to note about 500 views:
The 403 (HTTP Forbidden) view
-----------------------------
-.. versionadded:: 1.4
-
In the same vein as the 404 and 500 views, Django has a view to handle 403
Forbidden errors. If a view results in a 403 exception then Django will, by
default, call the view ``django.views.defaults.permission_denied``.
diff --git a/docs/topics/i18n/timezones.txt b/docs/topics/i18n/timezones.txt
index f3bb13ab03..22a0edb073 100644
--- a/docs/topics/i18n/timezones.txt
+++ b/docs/topics/i18n/timezones.txt
@@ -4,8 +4,6 @@
Time zones
==========
-.. versionadded:: 1.4
-
.. _time-zones-overview:
Overview
@@ -312,7 +310,7 @@ time zone is unset, the default time zone applies.
get_current_timezone
~~~~~~~~~~~~~~~~~~~~
-When the :func:`django.core.context_processors.tz` context processor is
+When the ``django.core.context_processors.tz`` context processor is
enabled -- by default, it is -- each :class:`~django.template.RequestContext`
contains a ``TIME_ZONE`` variable that provides the name of the current time
zone.
@@ -456,8 +454,9 @@ zone support.
Fixtures generated with ``USE_TZ = False``, or before Django 1.4, use the
"naive" format. If your project contains such fixtures, after you enable time
-zone support, you'll see :exc:`RuntimeWarning`\ s when you load them. To get
-rid of the warnings, you must convert your fixtures to the "aware" format.
+zone support, you'll see :exc:`~exceptions.RuntimeWarning`\ s when you load
+them. To get rid of the warnings, you must convert your fixtures to the "aware"
+format.
You can regenerate fixtures with :djadmin:`loaddata` then :djadmin:`dumpdata`.
Or, if they're small enough, you can simply edit them to add the UTC offset
@@ -660,7 +659,7 @@ Usage
datetime.datetime(2012, 2, 21, 10, 28, 45, tzinfo=<DstTzInfo 'Europe/Helsinki' EET+2:00:00 STD>)
Note that ``localize`` is a pytz extension to the :class:`~datetime.tzinfo`
- API. Also, you may want to catch :exc:`~pytz.InvalidTimeError`. The
+ API. Also, you may want to catch ``pytz.InvalidTimeError``. The
documentation of pytz contains `more examples`_. You should review it
before attempting to manipulate aware datetimes.
diff --git a/docs/topics/i18n/translation.txt b/docs/topics/i18n/translation.txt
index 65c6fe2445..811425d229 100644
--- a/docs/topics/i18n/translation.txt
+++ b/docs/topics/i18n/translation.txt
@@ -29,7 +29,9 @@ use internationalization, you should take the two seconds to set
:setting:`USE_I18N = False <USE_I18N>` in your settings file. Then Django will
make some optimizations so as not to load the internationalization machinery.
You'll probably also want to remove ``'django.core.context_processors.i18n'``
-from your :setting:`TEMPLATE_CONTEXT_PROCESSORS` setting.
+from your :setting:`TEMPLATE_CONTEXT_PROCESSORS` setting and
+``'django.middleware.locale.LocaleMiddleware'`` from your
+:setting:`MIDDLEWARE_CLASSES` setting.
.. note::
@@ -142,14 +144,22 @@ preceding the string, e.g.::
# Translators: This message appears on the home page only
output = ugettext("Welcome to my site.")
-This also works in templates with the :ttag:`comment` tag:
+The comment will then appear in the resulting ``.po`` file associated with the
+translatable contruct located below it and should also be displayed by most
+translation tools.
-.. code-block:: html+django
+.. note:: Just for completeness, this is the corresponding fragment of the
+ resulting ``.po`` file:
+
+ .. code-block:: po
- {% comment %}Translators: This is a text of the base template {% endcomment %}
+ #. Translators: This message appears on the home page only
+ # path/to/python/file.py:123
+ msgid "Welcome to my site."
+ msgstr ""
-The comment will then appear in the resulting ``.po`` file and should also be
-displayed by most translation tools.
+This also works in templates. See :ref:`translator-comments-in-templates` for
+more details.
Marking strings as no-op
------------------------
@@ -287,8 +297,6 @@ will appear in the ``.po`` file as:
msgid "May"
msgstr ""
-.. versionadded:: 1.4
-
Contextual markers are also supported by the :ttag:`trans` and
:ttag:`blocktrans` template tags.
@@ -345,7 +353,7 @@ It is recommended to always provide explicit
:attr:`~django.db.models.Options.verbose_name` and
:attr:`~django.db.models.Options.verbose_name_plural` options rather than
relying on the fallback English-centric and somewhat naïve determination of
-verbose names Django performs bu looking at the model's class name::
+verbose names Django performs by looking at the model's class name::
from django.utils.translation import ugettext_lazy as _
@@ -408,6 +416,41 @@ convert them to strings, because they should be converted as late as possible
(so that the correct locale is in effect). This necessitates the use of the
helper function described next.
+.. _lazy-plural-translations:
+
+Lazy translations and plural
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. versionadded:: 1.6
+
+When using lazy translation for a plural string (``[u]n[p]gettext_lazy``), you
+generally don't know the ``number`` argument at the time of the string
+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::
+
+ class MyForm(forms.Form):
+ error_message = ungettext_lazy("You only provided %(num)d argument",
+ "You only provided %(num)d arguments", 'num')
+
+ def clean(self):
+ # ...
+ if error:
+ raise forms.ValidationError(self.error_message % {'num': number})
+
+If the string contains exactly one unnamed placeholder, you can interpolate
+directly with the ``number`` argument::
+
+ class MyForm(forms.Form):
+ error_message = ungettext_lazy("You provided %d argument",
+ "You provided %d arguments")
+
+ def clean(self):
+ # ...
+ if error:
+ raise forms.ValidationError(self.error_message % number)
+
+
Joining strings: string_concat()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -507,7 +550,6 @@ It's not possible to mix a template variable inside a string within ``{% trans
%}``. If your translations require strings with variables (placeholders), use
``{% blocktrans %}`` instead.
-.. versionadded:: 1.4
If you'd like to retrieve a translated string without displaying it, you can
use the following syntax::
@@ -533,8 +575,6 @@ or should be used as arguments for other template tags or filters::
{% endfor %}
</p>
-.. versionadded:: 1.4
-
``{% trans %}`` also supports :ref:`contextual markers<contextual-markers>`
using the ``context`` keyword:
@@ -574,8 +614,6 @@ You can use multiple expressions inside a single ``blocktrans`` tag::
.. note:: The previous more verbose format is still supported:
``{% blocktrans with book|title as book_t and author|title as author_t %}``
-.. versionchanged:: 1.4
-
If resolving one of the block arguments fails, blocktrans will fall back to
the default language by deactivating the currently active language
temporarily with the :func:`~django.utils.translation.deactivate_all`
@@ -620,8 +658,6 @@ be retrieved (and stored) beforehand::
This is a URL: {{ the_url }}
{% endblocktrans %}
-.. versionadded:: 1.4
-
``{% blocktrans %}`` also supports :ref:`contextual
markers<contextual-markers>` using the ``context`` keyword:
@@ -629,6 +665,63 @@ markers<contextual-markers>` using the ``context`` keyword:
{% blocktrans with name=user.username context "greeting" %}Hi {{ name }}{% endblocktrans %}
+.. _translator-comments-in-templates:
+
+Comments for translators in templates
+-------------------------------------
+
+Just like with :ref:`Python code <translator-comments>`, these notes for
+translators can be specified using comments, either with the :ttag:`comment`
+tag:
+
+.. code-block:: html+django
+
+ {% comment %}Translators: View verb{% endcomment %}
+ {% trans "View" %}
+
+ {% comment %}Translators: Short intro blurb{% endcomment %}
+ <p>{% blocktrans %}A multiline translatable
+ literal.{% endblocktrans %}</p>
+
+or with the ``{#`` ... ``#}`` :ref:`one-line comment constructs <template-comments>`:
+
+.. code-block:: html+django
+
+ {# Translators: Label of a button that triggers search{% endcomment #}
+ <button type="submit">{% trans "Go" %}</button>
+
+ {# Translators: This is a text of the base template #}
+ {% blocktrans %}Ambiguous translatable block of text{% endblocktrans %}
+
+.. note:: Just for completeness, these are the corresponding fragments of the
+ resulting ``.po`` file:
+
+ .. code-block:: po
+
+ #. Translators: View verb
+ # path/to/template/file.html:10
+ msgid "View"
+ msgstr ""
+
+ #. Translators: Short intro blurb
+ # path/to/template/file.html:13
+ msgid ""
+ "A multiline translatable"
+ "literal."
+ msgstr ""
+
+ # ...
+
+ #. Translators: Label of a button that triggers search
+ # path/to/template/file.html:100
+ msgid "Go"
+ msgstr ""
+
+ #. Translators:
+ # path/to/template/file.html:103
+ msgid "Ambiguous translatable block of text"
+ msgstr ""
+
.. _template-translation-vars:
Other tags
@@ -853,6 +946,52 @@ This isn't as fast as string interpolation in Python, so keep it to those
cases where you really need it (for example, in conjunction with ``ngettext``
to produce proper pluralizations).
+Note on performance
+-------------------
+
+The :func:`~django.views.i18n.javascript_catalog` view generates the catalog
+from ``.mo`` files on every request. Since its output is constant — at least
+for a given version of a site — it's a good candidate for caching.
+
+Server-side caching will reduce CPU load. It's easily implemented with the
+:func:`~django.views.decorators.cache.cache_page` decorator. To trigger cache
+invalidation when your translations change, provide a version-dependant key
+prefix, as shown in the example below, or map the view at a version-dependant
+URL.
+
+.. code-block:: python
+
+ from django.views.decorators.cache import cache_page
+ from django.views.i18n import javascript_catalog
+
+ # The value returned by get_version() must change when translations change.
+ @cache_page(86400, key_prefix='js18n-%s' % get_version())
+ def cached_javascript_catalog(request, domain='djangojs', packages=None):
+ return javascript_catalog(request, domain, packages)
+
+Client-side caching will save bandwidth and make your site load faster. If
+you're using ETags (:setting:`USE_ETAGS = True <USE_ETAGS>`), you're already
+covered. Otherwise, you can apply :ref:`conditional decorators
+<conditional-decorators>`. In the following example, the cache is invalidated
+whenever your restart your application server.
+
+.. code-block:: python
+
+ from django.utils import timezone
+ from django.views.decorators.http import last_modified
+ from django.views.i18n import javascript_catalog
+
+ last_modified_date = timezone.now()
+ @last_modified(lambda req, **kw: last_modified_date)
+ def cached_javascript_catalog(request, domain='djangojs', packages=None):
+ return javascript_catalog(request, domain, packages)
+
+You can even pre-generate the javascript catalog as part of your deployment
+procedure and serve it as a static file. This radical technique is implemented
+in django-statici18n_.
+
+.. _django-statici18n: http://django-statici18n.readthedocs.org/en/latest/
+
.. _url-internationalization:
Internationalization: in URL patterns
@@ -928,7 +1067,7 @@ function. Example::
:func:`~django.conf.urls.i18n.i18n_patterns` is only allowed in your root
URLconf. Using it within an included URLconf will throw an
- :exc:`ImproperlyConfigured` exception.
+ :exc:`~django.core.exceptions.ImproperlyConfigured` exception.
.. warning::
@@ -1248,6 +1387,8 @@ The ``set_language`` redirect view
.. highlightlang:: python
+.. currentmodule:: django.views.i18n
+
.. function:: set_language(request)
As a convenience, Django comes with a view, :func:`django.views.i18n.set_language`,
@@ -1383,9 +1524,14 @@ If you want to let each individual user specify which language he or she
prefers, use ``LocaleMiddleware``. ``LocaleMiddleware`` enables language
selection based on data from the request. It customizes content for each user.
-To use ``LocaleMiddleware``, add ``'django.middleware.locale.LocaleMiddleware'``
-to your :setting:`MIDDLEWARE_CLASSES` setting. Because middleware order
-matters, you should follow these guidelines:
+``LocaleMiddleware`` is enabled in the default settings file: the
+:setting:`MIDDLEWARE_CLASSES` setting contains
+``'django.middleware.locale.LocaleMiddleware'``.
+
+.. versionchanged:: 1.6
+ In previous versions, ``LocaleMiddleware`` wasn't enabled by default.
+
+Because middleware order matters, you should follow these guidelines:
* Make sure it's one of the first middlewares installed.
* It should come after ``SessionMiddleware``, because ``LocaleMiddleware``
@@ -1408,8 +1554,6 @@ For example, your :setting:`MIDDLEWARE_CLASSES` might look like this::
``LocaleMiddleware`` tries to determine the user's language preference by
following this algorithm:
-.. versionchanged:: 1.4
-
* First, it looks for the language prefix in the requested URL. This is
only performed when you are using the ``i18n_patterns`` function in your
root URLconf. See :ref:`url-internationalization` for more information
diff --git a/docs/topics/index.txt b/docs/topics/index.txt
index 72f5090b15..f8f60b2953 100644
--- a/docs/topics/index.txt
+++ b/docs/topics/index.txt
@@ -13,13 +13,14 @@ Introductions to all the key parts of Django you'll need to know:
templates
class-based-views/index
files
- testing
- auth
+ testing/index
+ auth/index
cache
conditional-view-processing
signing
email
i18n/index
+ localflavor
logging
pagination
python3
diff --git a/docs/topics/install.txt b/docs/topics/install.txt
index b71033f319..1c99dc5d5a 100644
--- a/docs/topics/install.txt
+++ b/docs/topics/install.txt
@@ -135,7 +135,7 @@ table once ``syncdb`` has created it. After creating a database user with these
permissions, you'll specify the details in your project's settings file,
see :setting:`DATABASES` for details.
-If you're using Django's :doc:`testing framework</topics/testing>` to test
+If you're using Django's :doc:`testing framework</topics/testing/index>` to test
database queries, Django will need permission to create a test database.
.. _PostgreSQL: http://www.postgresql.org/
@@ -148,7 +148,7 @@ database queries, Django will need permission to create a test database.
.. _Oracle: http://www.oracle.com/
.. _Sybase SQL Anywhere: http://code.google.com/p/sqlany-django/
.. _IBM DB2: http://code.google.com/p/ibm-db/
-.. _Microsoft SQL Server 2005: http://code.google.com/p/django-mssql/
+.. _Microsoft SQL Server 2005: https://bitbucket.org/Manfre/django-mssql/
.. _Firebird: http://code.google.com/p/django-firebird/
.. _ODBC: http://code.google.com/p/django-pyodbc/
.. _removing-old-versions-of-django:
diff --git a/docs/ref/contrib/localflavor.txt b/docs/topics/localflavor.txt
index 9bb27e6e74..8ae435463d 100644
--- a/docs/ref/contrib/localflavor.txt
+++ b/docs/topics/localflavor.txt
@@ -2,15 +2,10 @@
The "local flavor" add-ons
==========================
-.. module:: django.contrib.localflavor
- :synopsis: A collection of various Django snippets that are useful only for
- a particular country or culture.
-
Historically, Django has shipped with ``django.contrib.localflavor`` --
assorted pieces of code that are useful for particular countries or cultures.
-Starting with Django 1.5, we've started the process of moving the code to
-outside packages (i.e., packages distributed separately from Django), for
-easier maintenance and to trim the size of Django's codebase.
+This code is now distributed separately from Django, for easier maintenance
+and to trim the size of Django's codebase.
The localflavor packages are named ``django-localflavor-*``, where the asterisk
is an `ISO 3166 country code`_. For example: ``django-localflavor-us`` is the
@@ -37,38 +32,7 @@ file.
.. _ISO 3166 country code: http://www.iso.org/iso/country_codes.htm
-How to migrate
-==============
-
-If you've used the old ``django.contrib.localflavor`` package, follow these two
-easy steps to update your code:
-
-1. Install the appropriate third-party ``django-localflavor-*`` package(s).
- Go to https://github.com/django/ and find the package for your country.
-
-2. Change your app's import statements to reference the new packages.
-
- For example, change this::
-
- from django.contrib.localflavor.fr.forms import FRPhoneNumberField
-
- ...to this::
-
- from django_localflavor_fr.forms import FRPhoneNumberField
-
-The code in the new packages is the same (it was copied directly from Django),
-so you don't have to worry about backwards compatibility in terms of
-functionality. Only the imports have changed.
-
-Deprecation policy
-==================
-
-In Django 1.5, importing from ``django.contrib.localflavor`` will result in a
-``DeprecationWarning``. This means your code will still work, but you should
-change it as soon as possible.
-
-In Django 1.6, importing from ``django.contrib.localflavor`` will no longer
-work.
+.. _localflavor-packages:
Supported countries
===================
@@ -90,6 +54,7 @@ The following countries have django-localflavor- packages.
* Finland: https://github.com/django/django-localflavor-fi
* France: https://github.com/django/django-localflavor-fr
* Germany: https://github.com/django/django-localflavor-de
+* Greece: https://github.com/spapas/django-localflavor-gr
* Hong Kong: https://github.com/django/django-localflavor-hk
* Iceland: https://github.com/django/django-localflavor-is
* India: https://github.com/django/django-localflavor-in
@@ -121,28 +86,47 @@ The following countries have django-localflavor- packages.
* United States of America: https://github.com/django/django-localflavor-us
* Uruguay: https://github.com/django/django-localflavor-uy
-django.contrib.localflavor.generic
-==================================
+Internationalization of localflavors
+====================================
-The ``django.contrib.localflavor.generic`` package, which hasn't been removed from
-Django yet, contains useful code that is not specific to one particular country
-or culture. Currently, it defines date, datetime and split datetime input
-fields based on those from :doc:`forms </topics/forms/index>`, but with non-US
-default formats. Here's an example of how to use them::
+To activate translations for a ``localflavor`` application, you must include
+the application's name (e.g. ``django_localflavor_jp``) in the
+:setting:`INSTALLED_APPS` setting, so the internationalization system can find
+the catalog, as explained in :ref:`how-django-discovers-translations`.
- from django import forms
- from django.contrib.localflavor import generic
+.. _localflavor-how-to-migrate:
- class MyForm(forms.Form):
- my_date_field = generic.forms.DateField()
+How to migrate
+==============
-Internationalization of localflavor
-===================================
+If you've used the old ``django.contrib.localflavor`` package, follow these two
+easy steps to update your code:
+
+1. Install the appropriate third-party ``django-localflavor-*`` package(s).
+ Go to https://github.com/django/ and find the package for your country.
+
+2. Change your app's import statements to reference the new packages.
+
+ For example, change this::
+
+ from django.contrib.localflavor.fr.forms import FRPhoneNumberField
+
+ ...to this::
-Localflavor has its own catalog of translations, in the directory
-``django/contrib/localflavor/locale``, and it's not loaded automatically like
-Django's general catalog in ``django/conf/locale``. If you want localflavor's
-texts to be translated, like form fields error messages, you must include
-:mod:`django.contrib.localflavor` in the :setting:`INSTALLED_APPS` setting, so
-the internationalization system can find the catalog, as explained in
-:ref:`how-django-discovers-translations`.
+ from django_localflavor_fr.forms import FRPhoneNumberField
+
+The code in the new packages is the same (it was copied directly from Django),
+so you don't have to worry about backwards compatibility in terms of
+functionality. Only the imports have changed.
+
+.. _localflavor-deprecation-policy:
+
+Deprecation policy
+==================
+
+In Django 1.5, importing from ``django.contrib.localflavor`` will result in a
+``DeprecationWarning``. This means your code will still work, but you should
+change it as soon as possible.
+
+In Django 1.6, importing from ``django.contrib.localflavor`` will no longer
+work.
diff --git a/docs/topics/logging.txt b/docs/topics/logging.txt
index 652ad397ff..cb22a57e84 100644
--- a/docs/topics/logging.txt
+++ b/docs/topics/logging.txt
@@ -141,7 +141,7 @@ error log record will be written.
Naming loggers
--------------
-The call to :meth:`logging.getLogger()` obtains (creating, if
+The call to :func:`logging.getLogger()` obtains (creating, if
necessary) an instance of a logger. The logger instance is identified
by a name. This name is used to identify the logger for configuration
purposes.
@@ -242,7 +242,7 @@ An example
The full documentation for `dictConfig format`_ is the best source of
information about logging configuration dictionaries. However, to give
you a taste of what is possible, here is an example of a fairly
-complex logging setup, configured using :meth:`logging.dictConfig`::
+complex logging setup, configured using :func:`logging.config.dictConfig`::
LOGGING = {
'version': 1,
@@ -308,7 +308,7 @@ This logging configuration does the following things:
* ``simple``, that just outputs the log level name (e.g.,
``DEBUG``) and the log message.
- The `format` string is a normal Python formatting string
+ The ``format`` string is a normal Python formatting string
describing the details that are to be output on each logging
line. The full list of detail that can be output can be
found in the `formatter documentation`_.
@@ -317,12 +317,12 @@ This logging configuration does the following things:
message, plus the time, process, thread and module that
generate the log message.
-* Defines one filter -- :class:`project.logging.SpecialFilter`,
+* Defines one filter -- ``project.logging.SpecialFilter``,
using the alias ``special``. If this filter required additional
arguments at time of construction, they can be provided as
additional keys in the filter configuration dictionary. In this
case, the argument ``foo`` will be given a value of ``bar`` when
- instantiating the :class:`SpecialFilter`.
+ instantiating the ``SpecialFilter``.
* Defines three handlers:
@@ -330,7 +330,7 @@ This logging configuration does the following things:
higher) message to ``/dev/null``.
* ``console``, a StreamHandler, which will print any ``DEBUG``
- (or higher) message to stderr. This handler uses the `simple` output
+ (or higher) message to stderr. This handler uses the ``simple`` output
format.
* ``mail_admins``, an AdminEmailHandler, which will email any
@@ -365,7 +365,7 @@ logger, you can specify your own configuration scheme.
The :setting:`LOGGING_CONFIG` setting defines the callable that will
be used to configure Django's loggers. By default, it points at
-Python's :meth:`logging.dictConfig()` method. However, if you want to
+Python's :func:`logging.config.dictConfig()` function. However, if you want to
use a different configuration process, you can use any other callable
that takes a single argument. The contents of :setting:`LOGGING` will
be provided as the value of that argument when logging is configured.
@@ -504,14 +504,12 @@ logging module.
.. class:: CallbackFilter(callback)
- .. versionadded:: 1.4
-
This filter accepts a callback function (which should accept a single
argument, the record to be logged), and calls it for each record that passes
through the filter. Handling of that record will not proceed if the callback
returns False.
- For instance, to filter out :class:`~django.http.UnreadablePostError`
+ For instance, to filter out :exc:`~django.http.UnreadablePostError`
(raised when a user cancels an upload) from the admin emails, you would
create a filter function::
@@ -542,13 +540,11 @@ logging module.
.. class:: RequireDebugFalse()
- .. versionadded:: 1.4
-
This filter will only pass on records when settings.DEBUG is False.
This filter is used as follows in the default :setting:`LOGGING`
configuration to ensure that the :class:`AdminEmailHandler` only sends error
- emails to admins when :setting:`DEBUG` is `False`::
+ emails to admins when :setting:`DEBUG` is ``False``::
'filters': {
'require_debug_false': {
@@ -568,7 +564,7 @@ logging module.
.. versionadded:: 1.5
This filter is similar to :class:`RequireDebugFalse`, except that records are
- passed only when :setting:`DEBUG` is `True`.
+ passed only when :setting:`DEBUG` is ``True``.
.. _default-logging-configuration:
@@ -580,8 +576,8 @@ with ``ERROR`` or ``CRITICAL`` level are sent to :class:`AdminEmailHandler`, as
long as the :setting:`DEBUG` setting is set to ``False``.
All messages reaching the ``django`` catch-all logger when :setting:`DEBUG` is
-`True` are sent ot the console. They are simply discarded (sent to
-``NullHandler``) when :setting:`DEBUG` is `False`.
+``True`` are sent to the console. They are simply discarded (sent to
+``NullHandler``) when :setting:`DEBUG` is ``False``.
.. versionchanged:: 1.5
diff --git a/docs/topics/pagination.txt b/docs/topics/pagination.txt
index 6c3ab77b11..17747c22ff 100644
--- a/docs/topics/pagination.txt
+++ b/docs/topics/pagination.txt
@@ -126,12 +126,6 @@ pages along with any interesting information from the objects themselves::
</span>
</div>
-.. versionchanged:: 1.4
- Previously, you would need to use
- ``{% for contact in contacts.object_list %}``, since the ``Page``
- object was not iterable.
-
-
``Paginator`` objects
=====================
@@ -205,8 +199,8 @@ Attributes
.. exception:: InvalidPage
- A base class for exceptions raised when a paginator is passed an invalid
- page number.
+ A base class for exceptions raised when a paginator is passed an invalid
+ page number.
The :meth:`Paginator.page` method raises an exception if the requested page is
invalid (i.e., not an integer) or contains no objects. Generally, it's enough
@@ -234,7 +228,6 @@ using :meth:`Paginator.page`.
.. class:: Page(object_list, number, paginator)
-.. versionadded:: 1.4
A page acts like a sequence of :attr:`Page.object_list` when using
``len()`` or iterating it directly.
diff --git a/docs/topics/python3.txt b/docs/topics/python3.txt
index e6dc165399..33f5fcd4c0 100644
--- a/docs/topics/python3.txt
+++ b/docs/topics/python3.txt
@@ -78,8 +78,8 @@ wherever possible and avoid the ``b`` prefixes.
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.
+Python 2's :func:`unicode` type was renamed :func:`str` in Python 3,
+:func:`str` was renamed ``bytes()``, and :func:`basestring` disappeared.
six_ provides :ref:`tools <string-handling-with-six>` to deal with these
changes.
@@ -131,35 +131,36 @@ and ``SafeText`` respectively.
For forwards compatibility, the new names work as of Django 1.4.2.
-:meth:`__str__` and :meth:`__unicode__` methods
------------------------------------------------
+:meth:`~object.__str__` and :meth:`~object.__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.
+In Python 2, the object model specifies :meth:`~object.__str__` and
+:meth:`~object.__unicode__` methods. If these methods exist, they must return
+``str`` (bytes) and ``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.
+The ``print`` statement and the :func:`str` built-in call
+:meth:`~object.__str__` to determine the human-readable representation of an
+object. The :func:`unicode` built-in calls :meth:`~object.__unicode__` if it
+exists, and otherwise falls back to :meth:`~object.__str__` and decodes the
+result with the system encoding. Conversely, the
+:class:`~django.db.models.Model` base class automatically derives
+:meth:`~object.__str__` from :meth:`~object.__unicode__` by encoding to UTF-8.
-In Python 3, there's simply :meth:`__str__`, which must return :class:`str`
+In Python 3, there's simply :meth:`~object.__str__`, which must return ``str``
(text).
-(It is also possible to define :meth:`__bytes__`, but Django application have
+(It is also possible to define ``__bytes__()``, but Django application have
little use for that method, because they hardly ever deal with
-:class:`bytes`.)
+``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
+Django provides a simple way to define :meth:`~object.__str__` and
+:meth:`~object.__unicode__` methods that work on Python 2 and 3: you must
+define a :meth:`~object.__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::
+:meth:`~object.__unicode__` and :meth:`~object.__str__` methods (replacing the
+original :meth:`~object.__str__` method in the process). Here's an example::
from __future__ import unicode_literals
from django.utils.encoding import python_2_unicode_compatible
@@ -173,8 +174,8 @@ This technique is the best match for Django's porting philosophy.
For forwards compatibility, this decorator is available as of Django 1.4.2.
-Finally, note that :meth:`__repr__` must return a :class:`str` on all versions
-of Python.
+Finally, note that :meth:`~object.__repr__` must return a ``str`` on all
+versions of Python.
:class:`dict` and :class:`dict`-like classes
--------------------------------------------
@@ -186,20 +187,20 @@ 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.
+It also contains an undocumented ``iterlists`` function that works well for
+``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.
+- headers are always ``str`` objects,
+- input and output streams are always ``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
+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
response and a unicode string as arguments.
@@ -236,11 +237,10 @@ 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::
+In Python 3, there aren't any automatic conversions between ``str`` and
+``bytes``, and the :mod:`codecs` module became more strict. :meth:`str.encode`
+always returns ``bytes``, and ``bytes.decode`` always returns ``str``. As a
+consequence, the following pattern is sometimes necessary::
value = value.encode('ascii', 'ignore').decode('ascii')
@@ -343,7 +343,7 @@ meaning of ``str`` changed. To test these types, use the following idioms::
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`.
+:data:`six.binary_type`.
``long``
~~~~~~~~
@@ -356,7 +356,7 @@ The ``long`` type no longer exists in Python 3. ``1L`` is a syntax error. Use
``xrange``
~~~~~~~~~~
-Import :func:`six.moves.xrange` wherever you use ``xrange``.
+Import ``six.moves.xrange`` wherever you use ``xrange``.
Moved modules
~~~~~~~~~~~~~
@@ -391,21 +391,19 @@ function.
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.
+The version of six bundled with Django includes a few extras.
.. function:: assertRaisesRegex(testcase, *args, **kwargs)
This replaces ``testcase.assertRaisesRegexp`` on Python 2, and
``testcase.assertRaisesRegex`` on Python 3. ``assertRaisesRegexp`` still
- exists in current Python3 versions, but issues a warning.
+ exists in current Python 3 versions, but issues a warning.
+
+.. function:: assertRegex(testcase, *args, **kwargs)
+
+ This replaces ``testcase.assertRegexpMatches`` on Python 2, and
+ ``testcase.assertRegex`` on Python 3. ``assertRegexpMatches`` still
+ exists in current Python 3 versions, but issues a warning.
In addition to six' defaults moves, Django's version provides ``thread`` as
diff --git a/docs/topics/security.txt b/docs/topics/security.txt
index 169f9ac773..566202eefa 100644
--- a/docs/topics/security.txt
+++ b/docs/topics/security.txt
@@ -38,7 +38,7 @@ in unauthorized JavaScript execution, depending on how the browser renders
imperfect HTML.
It is also important to be particularly careful when using ``is_safe`` with
-custom template tags, the :ttag:`safe` template tag, :mod:`mark_safe
+custom template tags, the :tfilter:`safe` template tag, :mod:`mark_safe
<django.utils.safestring>`, and when autoescape is turned off.
In addition, if you are using the template system to output something other
@@ -48,13 +48,6 @@ escaping.
You should also be very careful when storing HTML in the database, especially
when that HTML is retrieved and displayed.
-Markup library
---------------
-
-If you use :mod:`django.contrib.markup`, you need to ensure that the filters are
-only used on trusted input, or that you have correctly configured them to ensure
-they do not allow raw HTML output. See the documentation of that module for more
-information.
Cross site request forgery (CSRF) protection
============================================
@@ -76,8 +69,8 @@ POST to your Web site and have another logged in user unwittingly submit that
form. The malicious user would have to know the nonce, which is user specific
(using a cookie).
-When deployed with :ref:`HTTPS <security-recommendation-ssl>`,
-``CsrfViewMiddleware`` will check that the HTTP referer header is set to a
+When deployed with :ref:`HTTPS <security-recommendation-ssl>`,
+``CsrfViewMiddleware`` will check that the HTTP referer header is set to a
URL on the same origin (including subdomain and port). Because HTTPS
provides additional security, it is imperative to ensure connections use HTTPS
where it is available by forwarding insecure connection requests and using
@@ -167,47 +160,40 @@ server, there are some additional steps you may need:
.. _host-headers-virtual-hosting:
-Host headers and virtual hosting
-================================
+Host header validation
+======================
-Django uses the ``Host`` header provided by the client to construct URLs
-in certain cases. While these values are sanitized to prevent Cross
-Site Scripting attacks, they can be used for Cross-Site Request
-Forgery and cache poisoning attacks in some circumstances. We
-recommend you ensure your Web server is configured such that:
+Django uses the ``Host`` header provided by the client to construct URLs in
+certain cases. While these values are sanitized to prevent Cross Site Scripting
+attacks, a fake ``Host`` value can be used for Cross-Site Request Forgery,
+cache poisoning attacks, and poisoning links in emails.
-* It always validates incoming HTTP ``Host`` headers against the expected
- host name.
-* Disallows requests with no ``Host`` header.
-* Is *not* configured with a catch-all virtual host that forwards requests
- to a Django application.
+Because even seemingly-secure webserver configurations are susceptible to fake
+``Host`` headers, Django validates ``Host`` headers against the
+:setting:`ALLOWED_HOSTS` setting in the
+:meth:`django.http.HttpRequest.get_host()` method.
-Additionally, as of 1.3.1, Django requires you to explicitly enable support for
-the ``X-Forwarded-Host`` header if your configuration requires it.
+This validation only applies via :meth:`~django.http.HttpRequest.get_host()`;
+if your code accesses the ``Host`` header directly from ``request.META`` you
+are bypassing this security protection.
-Configuration for Apache
-------------------------
+For more details see the full :setting:`ALLOWED_HOSTS` documentation.
-The easiest way to get the described behavior in Apache is as follows. Create
-a `virtual host`_ using the ServerName_ and ServerAlias_ directives to restrict
-the domains Apache reacts to. Please keep in mind that while the directives do
-support ports the match is only performed against the hostname. This means that
-the ``Host`` header could still contain a port pointing to another webserver on
-the same machine. The next step is to make sure that your newly created virtual
-host is not also the default virtual host. Apache uses the first virtual host
-found in the configuration file as default virtual host. As such you have to
-ensure that you have another virtual host which will act as catch-all virtual
-host. Just add one if you do not have one already, there is nothing special
-about it aside from ensuring it is the first virtual host in the configuration
-file. Debian/Ubuntu users usually don't have to take any action, since Apache
-ships with a default virtual host in ``sites-available`` which is linked into
-``sites-enabled`` as ``000-default`` and included from ``apache2.conf``. Just
-make sure not to name your site ``000-abc``, since files are included in
-alphabetical order.
+.. warning::
-.. _virtual host: http://httpd.apache.org/docs/2.2/vhosts/
-.. _ServerName: http://httpd.apache.org/docs/2.2/mod/core.html#servername
-.. _ServerAlias: http://httpd.apache.org/docs/2.2/mod/core.html#serveralias
+ Previous versions of this document recommended configuring your webserver to
+ ensure it validates incoming HTTP ``Host`` headers. While this is still
+ recommended, in many common webservers a configuration that seems to
+ validate the ``Host`` header may not in fact do so. For instance, even if
+ Apache is configured such that your Django site is served from a non-default
+ virtual host with the ``ServerName`` set, it is still possible for an HTTP
+ request to match this virtual host and supply a fake ``Host`` header. Thus,
+ Django now requires that you set :setting:`ALLOWED_HOSTS` explicitly rather
+ than relying on webserver configuration.
+
+Additionally, as of 1.3.1, Django requires you to explicitly enable support for
+the ``X-Forwarded-Host`` header (via the :setting:`USE_X_FORWARDED_HOST`
+setting) if your configuration requires it.
.. _additional-security-topics:
diff --git a/docs/topics/serialization.txt b/docs/topics/serialization.txt
index 28f600e223..ce39f6cd28 100644
--- a/docs/topics/serialization.txt
+++ b/docs/topics/serialization.txt
@@ -26,6 +26,8 @@ to (see `Serialization formats`_) and a
argument can be any iterator that yields Django model instances, but it'll
almost always be a QuerySet).
+.. function:: django.core.serializers.get_serializer(format)
+
You can also use a serializer object directly::
XMLSerializer = serializers.get_serializer("xml")
@@ -43,7 +45,7 @@ This is useful if you want to serialize data directly to a file-like object
Calling :func:`~django.core.serializers.get_serializer` with an unknown
:ref:`format <serialization-formats>` will raise a
- :class:`~django.core.serializers.SerializerDoesNotExist` exception.
+ ``django.core.serializers.SerializerDoesNotExist`` exception.
Subset of fields
~~~~~~~~~~~~~~~~
@@ -88,11 +90,11 @@ If you only serialize the Restaurant model::
data = serializers.serialize('xml', Restaurant.objects.all())
-the fields on the serialized output will only contain the `serves_hot_dogs`
-attribute. The `name` attribute of the base class will be ignored.
+the fields on the serialized output will only contain the ``serves_hot_dogs``
+attribute. The ``name`` attribute of the base class will be ignored.
-In order to fully serialize your Restaurant instances, you will need to
-serialize the Place models as well::
+In order to fully serialize your ``Restaurant`` instances, you will need to
+serialize the ``Place`` models as well::
all_objects = list(Restaurant.objects.all()) + list(Place.objects.all())
data = serializers.serialize('xml', all_objects)
@@ -115,6 +117,16 @@ object and any associated relationship data.
Calling ``DeserializedObject.save()`` saves the object to the database.
+.. note::
+
+ If the ``pk`` attribute in the serialized data doesn't exist or is
+ null, a new instance will be saved to the database.
+
+.. versionchanged:: 1.6
+
+In previous versions of Django, the ``pk`` attribute had to be present
+on the serialized data or a ``DeserializationError`` would be raised.
+
This ensures that deserializing is a non-destructive operation even if the
data in your serialized representation doesn't match what's currently in the
database. Usually, working with these ``DeserializedObject`` instances looks
@@ -160,11 +172,82 @@ Identifier Information
.. _json: http://json.org/
.. _PyYAML: http://www.pyyaml.org/
-Notes for specific serialization formats
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+XML
+~~~
+
+The basic XML serialization format is quite simple::
+
+ <?xml version="1.0" encoding="utf-8"?>
+ <django-objects version="1.0">
+ <object pk="123" model="sessions.session">
+ <field type="DateTimeField" name="expire_date">2013-01-16T08:16:59.844560+00:00</field>
+ <!-- ... -->
+ </object>
+ </django-objects>
+
+The whole collection of objects that is either serialized or de-serialized is
+represented by a ``<django-objects>``-tag which contains multiple
+``<object>``-elements. Each such object has two attributes: "pk" and "model",
+the latter being represented by the name of the app ("sessions") and the
+lowercase name of the model ("session") separated by a dot.
+
+Each field of the object is serialized as a ``<field>``-element sporting the
+fields "type" and "name". The text content of the element represents the value
+that should be stored.
+
+Foreign keys and other relational fields are treated a little bit differently::
+
+ <object pk="27" model="auth.permission">
+ <!-- ... -->
+ <field to="contenttypes.contenttype" name="content_type" rel="ManyToOneRel">9</field>
+ <!-- ... -->
+ </object>
+
+In this example we specify that the auth.Permission object with the PK 24 has
+a foreign key to the contenttypes.ContentType instance with the PK 9.
-json
-^^^^
+ManyToMany-relations are exported for the model that binds them. For instance,
+the auth.User model has such a relation to the auth.Permission model::
+
+ <object pk="1" model="auth.user">
+ <!-- ... -->
+ <field to="auth.permission" name="user_permissions" rel="ManyToManyRel">
+ <object pk="46"></object>
+ <object pk="47"></object>
+ </field>
+ </object>
+
+This example links the given user with the permission models with PKs 46 and 47.
+
+JSON
+~~~~
+
+When staying with the same example data as before it would be serialized as
+JSON in the following way::
+
+ [
+ {
+ "pk": "4b678b301dfd8a4e0dad910de3ae245b",
+ "model": "sessions.session",
+ "fields": {
+ "expire_date": "2013-01-16T08:16:59.844Z",
+ ...
+ }
+ }
+ ]
+
+The formatting here is a bit simpler than with XML. The whole collection
+is just represented as an array and the objects are represented by JSON objects
+with three properties: "pk", "model" and "fields". "fields" is again an object
+containing each field's name and value as property and property-value
+respectively.
+
+Foreign keys just have the PK of the linked object as property value.
+ManyToMany-relations are serialized for the model that defines them and are
+represented as a list of PKs.
+
+Date and datetime related types are treated in a special way by the JSON
+serializer to make the format compatible with `ECMA-262`_.
Be aware that not all Django output can be passed unmodified to :mod:`json`.
In particular, :ref:`lazy translation objects <lazy-translations>` need a
@@ -173,14 +256,29 @@ In particular, :ref:`lazy translation objects <lazy-translations>` need a
import json
from django.utils.functional import Promise
from django.utils.encoding import force_text
+ from django.core.serializers.json import DjangoJSONEncoder
- class LazyEncoder(json.JSONEncoder):
+ class LazyEncoder(DjangoJSONEncoder):
def default(self, obj):
if isinstance(obj, Promise):
return force_text(obj)
return super(LazyEncoder, self).default(obj)
.. _special encoder: http://docs.python.org/library/json.html#encoders-and-decoders
+.. _ecma-262: http://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.15
+
+YAML
+~~~~
+
+YAML serialization looks quite similar to JSON. The object list is serialized
+as a sequence mappings with the keys "pk", "model" and "fields". Each field is
+again a mapping with the key being name of the field and the value the value::
+
+ - fields: {expire_date: !!timestamp '2013-01-16 08:16:59.844560+00:00'}
+ model: sessions.session
+ pk: 4b678b301dfd8a4e0dad910de3ae245b
+
+Referential fields are again just represented by the PK or sequence of PKs.
.. _topics-serialization-natural-keys:
@@ -193,7 +291,7 @@ This strategy works well for most objects, but it can cause difficulty in some
circumstances.
Consider the case of a list of objects that have a foreign key referencing
-:class:`~django.contrib.conttenttypes.models.ContentType`. If you're going to
+:class:`~django.contrib.contenttypes.models.ContentType`. If you're going to
serialize an object that refers to a content type, then you need to have a way
to refer to that content type to begin with. Since ``ContentType`` objects are
automatically created by Django during the database synchronization process,
@@ -341,7 +439,7 @@ When ``use_natural_keys=True`` is specified, Django will use the
type that defines the method.
If you are using :djadmin:`dumpdata` to generate serialized data, you
-use the `--natural` command line flag to generate natural keys.
+use the :djadminopt:`--natural` command line flag to generate natural keys.
.. note::
@@ -360,7 +458,7 @@ Dependencies during serialization
Since natural keys rely on database lookups to resolve references, it
is important that the data exists before it is referenced. You can't make
-a `forward reference` with natural keys -- the data you're referencing
+a "forward reference" with natural keys -- the data you're referencing
must exist before you include a natural key reference to that data.
To accommodate this limitation, calls to :djadmin:`dumpdata` that use
diff --git a/docs/topics/settings.txt b/docs/topics/settings.txt
index 88fa7b6864..fa26297988 100644
--- a/docs/topics/settings.txt
+++ b/docs/topics/settings.txt
@@ -32,6 +32,8 @@ Because a settings file is a Python module, the following apply:
Designating the settings
========================
+.. envvar:: DJANGO_SETTINGS_MODULE
+
When you use Django, you have to tell it which settings you're using. Do this
by using an environment variable, ``DJANGO_SETTINGS_MODULE``.
@@ -260,4 +262,3 @@ It boils down to this: Use exactly one of either ``configure()`` or
``DJANGO_SETTINGS_MODULE``. Not both, and not neither.
.. _@login_required: ../authentication/#the-login-required-decorator
-
diff --git a/docs/topics/signals.txt b/docs/topics/signals.txt
index 1078d0372c..d611da4a37 100644
--- a/docs/topics/signals.txt
+++ b/docs/topics/signals.txt
@@ -30,7 +30,7 @@ notifications:
* :data:`django.db.models.signals.m2m_changed`
- Sent when a :class:`ManyToManyField` on a model is changed.
+ Sent when a :class:`~django.db.models.ManyToManyField` on a model is changed.
* :data:`django.core.signals.request_started` &
:data:`django.core.signals.request_finished`
@@ -213,7 +213,8 @@ Defining signals
All signals are :class:`django.dispatch.Signal` instances. The
``providing_args`` is a list of the names of arguments the signal will provide
-to listeners.
+to listeners. This is purely documentational, however, as there is nothing that
+checks that the signal actually provides these arguments to its listeners.
For example:
diff --git a/docs/topics/signing.txt b/docs/topics/signing.txt
index 07de97e2f3..68afd6962a 100644
--- a/docs/topics/signing.txt
+++ b/docs/topics/signing.txt
@@ -5,8 +5,6 @@ Cryptographic signing
.. module:: django.core.signing
:synopsis: Django's signing framework.
-.. versionadded:: 1.4
-
The golden rule of Web application security is to never trust data from
untrusted sources. Sometimes it can be useful to pass data through an
untrusted medium. Cryptographically signed values can be passed through an
@@ -60,6 +58,7 @@ You can retrieve the original value using the ``unsign`` method::
If the signature or value have been altered in any way, a
``django.core.signing.BadSignature`` exception will be raised::
+ >>> from django.core import signing
>>> value += 'm'
>>> try:
... original = signer.unsign(value)
diff --git a/docs/topics/templates.txt b/docs/topics/templates.txt
index fb2119515b..58a3ee9870 100644
--- a/docs/topics/templates.txt
+++ b/docs/topics/templates.txt
@@ -250,6 +250,8 @@ You can also create your own custom template tags; see
tags and filters available for a given site. See
:doc:`/ref/contrib/admin/admindocs`.
+.. _template-comments:
+
Comments
========
diff --git a/docs/topics/_images/django_unittest_classes_hierarchy.graffle b/docs/topics/testing/_images/django_unittest_classes_hierarchy.graffle
index 7211c0f3be..7211c0f3be 100644
--- a/docs/topics/_images/django_unittest_classes_hierarchy.graffle
+++ b/docs/topics/testing/_images/django_unittest_classes_hierarchy.graffle
diff --git a/docs/topics/_images/django_unittest_classes_hierarchy.pdf b/docs/topics/testing/_images/django_unittest_classes_hierarchy.pdf
index cedaba22ac..cedaba22ac 100644
--- a/docs/topics/_images/django_unittest_classes_hierarchy.pdf
+++ b/docs/topics/testing/_images/django_unittest_classes_hierarchy.pdf
Binary files differ
diff --git a/docs/topics/_images/django_unittest_classes_hierarchy.svg b/docs/topics/testing/_images/django_unittest_classes_hierarchy.svg
index 0482f044dd..0482f044dd 100644
--- a/docs/topics/_images/django_unittest_classes_hierarchy.svg
+++ b/docs/topics/testing/_images/django_unittest_classes_hierarchy.svg
diff --git a/docs/topics/testing/advanced.txt b/docs/topics/testing/advanced.txt
new file mode 100644
index 0000000000..26dc8ee1ae
--- /dev/null
+++ b/docs/topics/testing/advanced.txt
@@ -0,0 +1,425 @@
+=======================
+Advanced testing topics
+=======================
+
+The request factory
+===================
+
+.. module:: django.test.client
+
+.. class:: RequestFactory
+
+The :class:`~django.test.client.RequestFactory` shares the same API as
+the test client. However, instead of behaving like a browser, the
+RequestFactory provides a way to generate a request instance that can
+be used as the first argument to any view. This means you can test a
+view function the same way as you would test any other function -- as
+a black box, with exactly known inputs, testing for specific outputs.
+
+The API for the :class:`~django.test.client.RequestFactory` is a slightly
+restricted subset of the test client API:
+
+* It only has access to the HTTP methods :meth:`~Client.get()`,
+ :meth:`~Client.post()`, :meth:`~Client.put()`,
+ :meth:`~Client.delete()`, :meth:`~Client.head()` and
+ :meth:`~Client.options()`.
+
+* These methods accept all the same arguments *except* for
+ ``follows``. Since this is just a factory for producing
+ requests, it's up to you to handle the response.
+
+* It does not support middleware. Session and authentication
+ attributes must be supplied by the test itself if required
+ for the view to function properly.
+
+Example
+-------
+
+The following is a simple unit test using the request factory::
+
+ from django.utils import unittest
+ from django.test.client import RequestFactory
+
+ class SimpleTest(unittest.TestCase):
+ def setUp(self):
+ # Every test needs access to the request factory.
+ self.factory = RequestFactory()
+
+ def test_details(self):
+ # Create an instance of a GET request.
+ request = self.factory.get('/customer/details')
+
+ # Test my_view() as if it were deployed at /customer/details
+ response = my_view(request)
+ self.assertEqual(response.status_code, 200)
+
+.. _topics-testing-advanced-multidb:
+
+Tests and multiple databases
+============================
+
+.. _topics-testing-masterslave:
+
+Testing master/slave configurations
+-----------------------------------
+
+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,
+and as a result, data created on the master won't be seen on the
+slave.
+
+To compensate for this, Django allows you to define that a database is
+a *test mirror*. Consider the following (simplified) example database
+configuration::
+
+ DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.mysql',
+ 'NAME': 'myproject',
+ 'HOST': 'dbmaster',
+ # ... plus some other settings
+ },
+ 'slave': {
+ 'ENGINE': 'django.db.backends.mysql',
+ 'NAME': 'myproject',
+ 'HOST': 'dbslave',
+ 'TEST_MIRROR': 'default'
+ # ... plus some other settings
+ }
+ }
+
+In this setup, we have two database servers: ``dbmaster``, described
+by the database alias ``default``, and ``dbslave`` described by the
+alias ``slave``. As you might expect, ``dbslave`` has been configured
+by the database administrator as a read slave of ``dbmaster``, so in
+normal activity, any write to ``default`` will appear on ``slave``.
+
+If Django created two independent test databases, this would break any
+tests that expected replication to occur. However, the ``slave``
+database has been configured as a test mirror (using the
+:setting:`TEST_MIRROR` setting), indicating that under testing,
+``slave`` should be treated as a mirror of ``default``.
+
+When the test environment is configured, a test version of ``slave``
+will *not* be created. Instead the connection to ``slave``
+will be redirected to point at ``default``. As a result, writes to
+``default`` will appear on ``slave`` -- but because they are actually
+the same database, not because there is data replication between the
+two databases.
+
+.. _topics-testing-creation-dependencies:
+
+Controlling creation order for test databases
+---------------------------------------------
+
+By default, Django will always create the ``default`` database first.
+However, no guarantees are made on the creation order of any other
+databases in your test setup.
+
+If your database configuration requires a specific creation order, you
+can specify the dependencies that exist using the
+:setting:`TEST_DEPENDENCIES` setting. Consider the following
+(simplified) example database configuration::
+
+ DATABASES = {
+ 'default': {
+ # ... db settings
+ 'TEST_DEPENDENCIES': ['diamonds']
+ },
+ 'diamonds': {
+ # ... db settings
+ },
+ 'clubs': {
+ # ... db settings
+ 'TEST_DEPENDENCIES': ['diamonds']
+ },
+ 'spades': {
+ # ... db settings
+ 'TEST_DEPENDENCIES': ['diamonds','hearts']
+ },
+ 'hearts': {
+ # ... db settings
+ 'TEST_DEPENDENCIES': ['diamonds','clubs']
+ }
+ }
+
+Under this configuration, the ``diamonds`` database will be created first,
+as it is the only database alias without dependencies. The ``default`` and
+``clubs`` alias will be created next (although the order of creation of this
+pair is not guaranteed); then ``hearts``; and finally ``spades``.
+
+If there are any circular dependencies in the
+:setting:`TEST_DEPENDENCIES` definition, an ``ImproperlyConfigured``
+exception will be raised.
+
+Running tests outside the test runner
+=====================================
+
+If you want to run tests outside of ``./manage.py test`` -- for example,
+from a shell prompt -- you will need to set up the test
+environment first. Django provides a convenience method to do this::
+
+ >>> from django.test.utils import setup_test_environment
+ >>> setup_test_environment()
+
+This convenience method sets up the test database, and puts other
+Django features into modes that allow for repeatable testing.
+
+The call to :meth:`~django.test.utils.setup_test_environment` is made
+automatically as part of the setup of ``./manage.py test``. You only
+need to manually invoke this method if you're not using running your
+tests via Django's test runner.
+
+.. _other-testing-frameworks:
+
+Using different testing frameworks
+==================================
+
+Clearly, :mod:`doctest` and :mod:`unittest` are not the only Python testing
+frameworks. While Django doesn't provide explicit support for alternative
+frameworks, it does provide a way to invoke tests constructed for an
+alternative framework as if they were normal Django tests.
+
+When you run ``./manage.py test``, Django looks at the :setting:`TEST_RUNNER`
+setting to determine what to do. By default, :setting:`TEST_RUNNER` points to
+``'django.test.simple.DjangoTestSuiteRunner'``. This class defines the default Django
+testing behavior. This behavior involves:
+
+#. Performing global pre-test setup.
+
+#. Looking for unit tests and doctests in the ``models.py`` and
+ ``tests.py`` files in each installed application.
+
+#. Creating the test databases.
+
+#. Running ``syncdb`` to install models and initial data into the test
+ databases.
+
+#. Running the unit tests and doctests that are found.
+
+#. Destroying the test databases.
+
+#. Performing global post-test teardown.
+
+If you define your own test runner class and point :setting:`TEST_RUNNER` at
+that class, Django will execute your test runner whenever you run
+``./manage.py test``. In this way, it is possible to use any test framework
+that can be executed from Python code, or to modify the Django test execution
+process to satisfy whatever testing requirements you may have.
+
+.. _topics-testing-test_runner:
+
+Defining a test runner
+----------------------
+
+.. currentmodule:: django.test.simple
+
+A test runner is a class defining a ``run_tests()`` method. Django ships
+with a ``DjangoTestSuiteRunner`` class that defines the default Django
+testing behavior. This class defines the ``run_tests()`` entry point,
+plus a selection of other methods that are used to by ``run_tests()`` to
+set up, execute and tear down the test suite.
+
+.. class:: DjangoTestSuiteRunner(verbosity=1, interactive=True, failfast=True, **kwargs)
+
+ ``verbosity`` determines the amount of notification and debug information
+ that will be printed to the console; ``0`` is no output, ``1`` is normal
+ output, and ``2`` is verbose output.
+
+ If ``interactive`` is ``True``, the test suite has permission to ask the
+ user for instructions when the test suite is executed. An example of this
+ behavior would be asking for permission to delete an existing test
+ database. If ``interactive`` is ``False``, the test suite must be able to
+ run without any manual intervention.
+
+ If ``failfast`` is ``True``, the test suite will stop running after the
+ first test failure is detected.
+
+ Django will, from time to time, extend the capabilities of
+ the test runner by adding new arguments. The ``**kwargs`` declaration
+ allows for this expansion. If you subclass ``DjangoTestSuiteRunner`` or
+ write your own test runner, ensure accept and handle the ``**kwargs``
+ parameter.
+
+ Your test runner may also define additional command-line options.
+ If you add an ``option_list`` attribute to a subclassed test runner,
+ those options will be added to the list of command-line options that
+ the :djadmin:`test` command can use.
+
+Attributes
+~~~~~~~~~~
+
+.. attribute:: DjangoTestSuiteRunner.option_list
+
+ This is the tuple of ``optparse`` options which will be fed into the
+ management command's ``OptionParser`` for parsing arguments. See the
+ documentation for Python's ``optparse`` module for more details.
+
+Methods
+~~~~~~~
+
+.. method:: DjangoTestSuiteRunner.run_tests(test_labels, extra_tests=None, **kwargs)
+
+ Run the test suite.
+
+ ``test_labels`` is a list of strings describing the tests to be run. A test
+ label can take one of three forms:
+
+ * ``app.TestCase.test_method`` -- Run a single test method in a test
+ case.
+ * ``app.TestCase`` -- Run all the test methods in a test case.
+ * ``app`` -- Search for and run all tests in the named application.
+
+ If ``test_labels`` has a value of ``None``, the test runner should run
+ search for tests in all the applications in :setting:`INSTALLED_APPS`.
+
+ ``extra_tests`` is a list of extra ``TestCase`` instances to add to the
+ suite that is executed by the test runner. These extra tests are run
+ in addition to those discovered in the modules listed in ``test_labels``.
+
+ This method should return the number of tests that failed.
+
+.. method:: DjangoTestSuiteRunner.setup_test_environment(**kwargs)
+
+ Sets up the test environment ready for testing.
+
+.. method:: DjangoTestSuiteRunner.build_suite(test_labels, extra_tests=None, **kwargs)
+
+ Constructs a test suite that matches the test labels provided.
+
+ ``test_labels`` is a list of strings describing the tests to be run. A test
+ label can take one of three forms:
+
+ * ``app.TestCase.test_method`` -- Run a single test method in a test
+ case.
+ * ``app.TestCase`` -- Run all the test methods in a test case.
+ * ``app`` -- Search for and run all tests in the named application.
+
+ If ``test_labels`` has a value of ``None``, the test runner should run
+ search for tests in all the applications in :setting:`INSTALLED_APPS`.
+
+ ``extra_tests`` is a list of extra ``TestCase`` instances to add to the
+ suite that is executed by the test runner. These extra tests are run
+ in addition to those discovered in the modules listed in ``test_labels``.
+
+ Returns a ``TestSuite`` instance ready to be run.
+
+.. method:: DjangoTestSuiteRunner.setup_databases(**kwargs)
+
+ Creates the test databases.
+
+ Returns a data structure that provides enough detail to undo the changes
+ that have been made. This data will be provided to the ``teardown_databases()``
+ function at the conclusion of testing.
+
+.. method:: DjangoTestSuiteRunner.run_suite(suite, **kwargs)
+
+ Runs the test suite.
+
+ Returns the result produced by the running the test suite.
+
+.. method:: DjangoTestSuiteRunner.teardown_databases(old_config, **kwargs)
+
+ Destroys the test databases, restoring pre-test conditions.
+
+ ``old_config`` is a data structure defining the changes in the
+ database configuration that need to be reversed. It is the return
+ value of the ``setup_databases()`` method.
+
+.. method:: DjangoTestSuiteRunner.teardown_test_environment(**kwargs)
+
+ Restores the pre-test environment.
+
+.. method:: DjangoTestSuiteRunner.suite_result(suite, result, **kwargs)
+
+ Computes and returns a return code based on a test suite, and the result
+ from that test suite.
+
+
+Testing utilities
+-----------------
+
+.. module:: django.test.utils
+ :synopsis: Helpers to write custom test runners.
+
+To assist in the creation of your own test runner, Django provides a number of
+utility methods in the ``django.test.utils`` module.
+
+.. function:: setup_test_environment()
+
+ Performs any global pre-test setup, such as the installing the
+ instrumentation of the template rendering system and setting up
+ the dummy email outbox.
+
+.. function:: teardown_test_environment()
+
+ Performs any global post-test teardown, such as removing the black
+ magic hooks into the template system and restoring normal email
+ services.
+
+.. currentmodule:: django.db.connection.creation
+
+The creation module of the database backend (``connection.creation``)
+also provides some utilities that can be useful during testing.
+
+.. function:: create_test_db([verbosity=1, autoclobber=False])
+
+ Creates a new test database and runs ``syncdb`` against it.
+
+ ``verbosity`` has the same behavior as in ``run_tests()``.
+
+ ``autoclobber`` describes the behavior that will occur if a
+ database with the same name as the test database is discovered:
+
+ * If ``autoclobber`` is ``False``, the user will be asked to
+ approve destroying the existing database. ``sys.exit`` is
+ called if the user does not approve.
+
+ * If autoclobber is ``True``, the database will be destroyed
+ without consulting the user.
+
+ Returns the name of the test database that it created.
+
+ ``create_test_db()`` has the side effect of modifying the value of
+ :setting:`NAME` in :setting:`DATABASES` to match the name of the test
+ database.
+
+.. function:: destroy_test_db(old_database_name, [verbosity=1])
+
+ Destroys the database whose name is the value of :setting:`NAME` in
+ :setting:`DATABASES`, and sets :setting:`NAME` to the value of
+ ``old_database_name``.
+
+ The ``verbosity`` argument has the same behavior as for
+ :class:`~django.test.simple.DjangoTestSuiteRunner`.
+
+.. _topics-testing-code-coverage:
+
+Integration with coverage.py
+============================
+
+Code coverage describes how much source code has been tested. It shows which
+parts of your code are being exercised by tests and which are not. It's an
+important part of testing applications, so it's strongly recommended to check
+the coverage of your tests.
+
+Django can be easily integrated with `coverage.py`_, a tool for measuring code
+coverage of Python programs. First, `install coverage.py`_. Next, run the
+following from your project folder containing ``manage.py``::
+
+ coverage run --source='.' manage.py test myapp
+
+This runs your tests and collects coverage data of the executed files in your
+project. You can see a report of this data by typing following command::
+
+ coverage report
+
+Note that some Django code was executed while running tests, but it is not
+listed here because of the ``source`` flag passed to the previous command.
+
+For more options like annotated HTML listings detailing missed lines, see the
+`coverage.py`_ docs.
+
+.. _coverage.py: http://nedbatchelder.com/code/coverage/
+.. _install coverage.py: http://pypi.python.org/pypi/coverage
diff --git a/docs/topics/testing/doctests.txt b/docs/topics/testing/doctests.txt
new file mode 100644
index 0000000000..5036e946a9
--- /dev/null
+++ b/docs/topics/testing/doctests.txt
@@ -0,0 +1,81 @@
+===================
+Django and doctests
+===================
+
+Doctests use Python's standard :mod:`doctest` module, which searches your
+docstrings for statements that resemble a session of the Python interactive
+interpreter. A full explanation of how :mod:`doctest` works is out of the scope
+of this document; read Python's official documentation for the details.
+
+.. admonition:: What's a **docstring**?
+
+ A good explanation of docstrings (and some guidelines for using them
+ effectively) can be found in :pep:`257`:
+
+ A docstring is a string literal that occurs as the first statement in
+ a module, function, class, or method definition. Such a docstring
+ becomes the ``__doc__`` special attribute of that object.
+
+ For example, this function has a docstring that describes what it does::
+
+ def add_two(num):
+ "Return the result of adding two to the provided number."
+ return num + 2
+
+ Because tests often make great documentation, putting tests directly in
+ your docstrings is an effective way to document *and* test your code.
+
+As with unit tests, for a given Django application, the test runner looks for
+doctests in two places:
+
+* The ``models.py`` file. You can define module-level doctests and/or a
+ doctest for individual models. It's common practice to put
+ application-level doctests in the module docstring and model-level
+ doctests in the model docstrings.
+
+* A file called ``tests.py`` in the application directory -- i.e., the
+ directory that holds ``models.py``. This file is a hook for any and all
+ doctests you want to write that aren't necessarily related to models.
+
+This example doctest is equivalent to the example given in the unittest section
+above::
+
+ # models.py
+
+ from django.db import models
+
+ class Animal(models.Model):
+ """
+ An animal that knows how to make noise
+
+ # Create some animals
+ >>> lion = Animal.objects.create(name="lion", sound="roar")
+ >>> cat = Animal.objects.create(name="cat", sound="meow")
+
+ # Make 'em speak
+ >>> lion.speak()
+ 'The lion says "roar"'
+ >>> cat.speak()
+ 'The cat says "meow"'
+ """
+ name = models.CharField(max_length=20)
+ sound = models.CharField(max_length=20)
+
+ def speak(self):
+ return 'The %s says "%s"' % (self.name, self.sound)
+
+When you :ref:`run your tests <running-tests>`, the test runner will find this
+docstring, notice that portions of it look like an interactive Python session,
+and execute those lines while checking that the results match.
+
+In the case of model tests, note that the test runner takes care of creating
+its own test database. That is, any test that accesses a database -- by
+creating and saving model instances, for example -- will not affect your
+production database. However, the database is not refreshed between doctests,
+so if your doctest requires a certain state you should consider flushing the
+database or loading a fixture. (See the section on :ref:`fixtures
+<topics-testing-fixtures>` for more on this.) Note that to use this feature,
+the database user Django is connecting as must have ``CREATE DATABASE``
+rights.
+
+For more details about :mod:`doctest`, see the Python documentation.
diff --git a/docs/topics/testing/index.txt b/docs/topics/testing/index.txt
new file mode 100644
index 0000000000..94e88bdf04
--- /dev/null
+++ b/docs/topics/testing/index.txt
@@ -0,0 +1,111 @@
+=================
+Testing in Django
+=================
+
+.. toctree::
+ :hidden:
+
+ overview
+ doctests
+ advanced
+
+Automated testing is an extremely useful bug-killing tool for the modern
+Web developer. You can use a collection of tests -- a **test suite** -- to
+solve, or avoid, a number of problems:
+
+* When you're writing new code, you can use tests to validate your code
+ works as expected.
+
+* When you're refactoring or modifying old code, you can use tests to
+ ensure your changes haven't affected your application's behavior
+ unexpectedly.
+
+Testing a Web application is a complex task, because a Web application is made
+of several layers of logic -- from HTTP-level request handling, to form
+validation and processing, to template rendering. With Django's test-execution
+framework and assorted utilities, you can simulate requests, insert test data,
+inspect your application's output and generally verify your code is doing what
+it should be doing.
+
+The best part is, it's really easy.
+
+Unit tests v. doctests
+======================
+
+There are two primary ways to write tests with Django, corresponding to the
+two test frameworks that ship in the Python standard library. The two
+frameworks are:
+
+* **Unit tests** -- tests that are expressed as methods on a Python class
+ that subclasses :class:`unittest.TestCase` or Django's customized
+ :class:`~django.test.TestCase`. For example::
+
+ import unittest
+
+ class MyFuncTestCase(unittest.TestCase):
+ def testBasic(self):
+ a = ['larry', 'curly', 'moe']
+ self.assertEqual(my_func(a, 0), 'larry')
+ self.assertEqual(my_func(a, 1), 'curly')
+
+* **Doctests** -- tests that are embedded in your functions' docstrings and
+ are written in a way that emulates a session of the Python interactive
+ interpreter. For example::
+
+ def my_func(a_list, idx):
+ """
+ >>> a = ['larry', 'curly', 'moe']
+ >>> my_func(a, 0)
+ 'larry'
+ >>> my_func(a, 1)
+ 'curly'
+ """
+ return a_list[idx]
+
+Which should I use?
+-------------------
+
+Because Django supports both of the standard Python test frameworks, it's up to
+you and your tastes to decide which one to use. You can even decide to use
+*both*.
+
+For developers new to testing, however, this choice can seem confusing. Here,
+then, are a few key differences to help you decide which approach is right for
+you:
+
+* If you've been using Python for a while, :mod:`doctest` will probably feel
+ more "pythonic". It's designed to make writing tests as easy as possible,
+ so it requires no overhead of writing classes or methods. You simply put
+ tests in docstrings. This has the added advantage of serving as
+ documentation (and correct documentation, at that!). However, while
+ doctests are good for some simple example code, they are not very good if
+ you want to produce either high quality, comprehensive tests or high
+ quality documentation. Test failures are often difficult to debug
+ as it can be unclear exactly why the test failed. Thus, doctests should
+ generally be avoided and used primarily for documentation examples only.
+
+* The :mod:`unittest` framework will probably feel very familiar to
+ developers coming from Java. :mod:`unittest` is inspired by Java's JUnit,
+ so you'll feel at home with this method if you've used JUnit or any test
+ framework inspired by JUnit.
+
+* If you need to write a bunch of tests that share similar code, then
+ you'll appreciate the :mod:`unittest` framework's organization around
+ classes and methods. This makes it easy to abstract common tasks into
+ common methods. The framework also supports explicit setup and/or cleanup
+ routines, which give you a high level of control over the environment
+ in which your test cases are run.
+
+* If you're writing tests for Django itself, you should use :mod:`unittest`.
+
+Where to go from here
+=====================
+
+As unit tests are preferred in Django, we treat them in detail in the
+:doc:`overview` document.
+
+:doc:`doctests` describes Django-specific features when using doctests.
+
+You can also use any *other* Python test framework, Django provides an API and
+tools for that kind of integration. They are described in the
+:ref:`other-testing-frameworks` section of :doc:`advanced`.
diff --git a/docs/topics/testing.txt b/docs/topics/testing/overview.txt
index 8c11e32a55..259c39618b 100644
--- a/docs/topics/testing.txt
+++ b/docs/topics/testing/overview.txt
@@ -5,69 +5,17 @@ Testing Django applications
.. module:: django.test
:synopsis: Testing tools for Django applications.
-Automated testing is an extremely useful bug-killing tool for the modern
-Web developer. You can use a collection of tests -- a **test suite** -- to
-solve, or avoid, a number of problems:
+.. seealso::
-* When you're writing new code, you can use tests to validate your code
- works as expected.
+ The :doc:`testing tutorial </intro/tutorial05>` and the
+ :doc:`advanced testing topics </topics/testing/advanced>`.
-* When you're refactoring or modifying old code, you can use tests to
- ensure your changes haven't affected your application's behavior
- unexpectedly.
-
-Testing a Web application is a complex task, because a Web application is made
-of several layers of logic -- from HTTP-level request handling, to form
-validation and processing, to template rendering. With Django's test-execution
-framework and assorted utilities, you can simulate requests, insert test data,
-inspect your application's output and generally verify your code is doing what
-it should be doing.
-
-The best part is, it's really easy.
-
-This document is split into two primary sections. First, we explain how to
-write tests with Django. Then, we explain how to run them.
+This document is split into two primary sections. First, we explain how to write
+tests with Django. Then, we explain how to run them.
Writing tests
=============
-There are two primary ways to write tests with Django, corresponding to the
-two test frameworks that ship in the Python standard library. The two
-frameworks are:
-
-* **Unit tests** -- tests that are expressed as methods on a Python class
- that subclasses :class:`unittest.TestCase` or Django's customized
- :class:`TestCase`. For example::
-
- import unittest
-
- class MyFuncTestCase(unittest.TestCase):
- def testBasic(self):
- a = ['larry', 'curly', 'moe']
- self.assertEqual(my_func(a, 0), 'larry')
- self.assertEqual(my_func(a, 1), 'curly')
-
-* **Doctests** -- tests that are embedded in your functions' docstrings and
- are written in a way that emulates a session of the Python interactive
- interpreter. For example::
-
- def my_func(a_list, idx):
- """
- >>> a = ['larry', 'curly', 'moe']
- >>> my_func(a, 0)
- 'larry'
- >>> my_func(a, 1)
- 'curly'
- """
- return a_list[idx]
-
-We'll discuss choosing the appropriate test framework later, however, most
-experienced developers prefer unit tests. You can also use any *other* Python
-test framework, as we'll explain in a bit.
-
-Writing unit tests
-------------------
-
Django's unit tests use a Python standard library module: :mod:`unittest`. This
module defines tests in class-based approach.
@@ -80,7 +28,7 @@ module defines tests in class-based approach.
backported for Python 2.5 compatibility.
To access this library, Django provides the
- :mod:`django.utils.unittest` module alias. If you are using Python
+ ``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.
@@ -115,8 +63,8 @@ Here is an example :class:`unittest.TestCase` subclass::
class AnimalTestCase(unittest.TestCase):
def setUp(self):
- self.lion = Animal.objects.create(name="lion", sound="roar")
- self.cat = Animal.objects.create(name="cat", sound="meow")
+ self.lion = Animal(name="lion", sound="roar")
+ self.cat = Animal(name="cat", sound="meow")
def test_animals_can_speak(self):
"""Animals that can speak are correctly identified"""
@@ -139,121 +87,17 @@ For more details about :mod:`unittest`, see the Python documentation.
.. _suggested organization: http://docs.python.org/library/unittest.html#organizing-tests
-Writing doctests
-----------------
-
-Doctests use Python's standard :mod:`doctest` module, which searches your
-docstrings for statements that resemble a session of the Python interactive
-interpreter. A full explanation of how :mod:`doctest` works is out of the scope
-of this document; read Python's official documentation for the details.
-
-.. admonition:: What's a **docstring**?
-
- A good explanation of docstrings (and some guidelines for using them
- effectively) can be found in :pep:`257`:
-
- A docstring is a string literal that occurs as the first statement in
- a module, function, class, or method definition. Such a docstring
- becomes the ``__doc__`` special attribute of that object.
-
- For example, this function has a docstring that describes what it does::
-
- def add_two(num):
- "Return the result of adding two to the provided number."
- return num + 2
-
- Because tests often make great documentation, putting tests directly in
- your docstrings is an effective way to document *and* test your code.
-
-As with unit tests, for a given Django application, the test runner looks for
-doctests in two places:
-
-* The ``models.py`` file. You can define module-level doctests and/or a
- doctest for individual models. It's common practice to put
- application-level doctests in the module docstring and model-level
- doctests in the model docstrings.
-
-* A file called ``tests.py`` in the application directory -- i.e., the
- directory that holds ``models.py``. This file is a hook for any and all
- doctests you want to write that aren't necessarily related to models.
-
-This example doctest is equivalent to the example given in the unittest section
-above::
-
- # models.py
-
- from django.db import models
-
- class Animal(models.Model):
- """
- An animal that knows how to make noise
-
- # Create some animals
- >>> lion = Animal.objects.create(name="lion", sound="roar")
- >>> cat = Animal.objects.create(name="cat", sound="meow")
-
- # Make 'em speak
- >>> lion.speak()
- 'The lion says "roar"'
- >>> cat.speak()
- 'The cat says "meow"'
- """
- name = models.CharField(max_length=20)
- sound = models.CharField(max_length=20)
-
- def speak(self):
- return 'The %s says "%s"' % (self.name, self.sound)
-
-When you :ref:`run your tests <running-tests>`, the test runner will find this
-docstring, notice that portions of it look like an interactive Python session,
-and execute those lines while checking that the results match.
-
-In the case of model tests, note that the test runner takes care of creating
-its own test database. That is, any test that accesses a database -- by
-creating and saving model instances, for example -- will not affect your
-production database. However, the database is not refreshed between doctests,
-so if your doctest requires a certain state you should consider flushing the
-database or loading a fixture. (See the section on fixtures, below, for more
-on this.) Note that to use this feature, the database user Django is connecting
-as must have ``CREATE DATABASE`` rights.
-
-For more details about :mod:`doctest`, see the Python documentation.
-
-Which should I use?
--------------------
-
-Because Django supports both of the standard Python test frameworks, it's up to
-you and your tastes to decide which one to use. You can even decide to use
-*both*.
-
-For developers new to testing, however, this choice can seem confusing. Here,
-then, are a few key differences to help you decide which approach is right for
-you:
-
-* If you've been using Python for a while, :mod:`doctest` will probably feel
- more "pythonic". It's designed to make writing tests as easy as possible,
- so it requires no overhead of writing classes or methods. You simply put
- tests in docstrings. This has the added advantage of serving as
- documentation (and correct documentation, at that!). However, while
- doctests are good for some simple example code, they are not very good if
- you want to produce either high quality, comprehensive tests or high
- quality documentation. Test failures are often difficult to debug
- as it can be unclear exactly why the test failed. Thus, doctests should
- generally be avoided and used primarily for documentation examples only.
+.. warning::
-* The :mod:`unittest` framework will probably feel very familiar to
- developers coming from Java. :mod:`unittest` is inspired by Java's JUnit,
- so you'll feel at home with this method if you've used JUnit or any test
- framework inspired by JUnit.
+ If your tests rely on database access such as creating or querying models,
+ be sure to create your test classes as subclasses of
+ :class:`django.test.TestCase` rather than :class:`unittest.TestCase`.
-* If you need to write a bunch of tests that share similar code, then
- you'll appreciate the :mod:`unittest` framework's organization around
- classes and methods. This makes it easy to abstract common tasks into
- common methods. The framework also supports explicit setup and/or cleanup
- routines, which give you a high level of control over the environment
- in which your test cases are run.
-
-* If you're writing tests for Django itself, you should use :mod:`unittest`.
+ In the example above, we instantiate some models but do not save them to
+ the database. Using :class:`unittest.TestCase` avoids the cost of running
+ each test in a transaction and flushing the database, but for most
+ applications the scope of tests you will be able to write this way will
+ be fairly limited, so it's easiest to use :class:`django.test.TestCase`.
.. _running-tests:
@@ -329,23 +173,7 @@ be reported, and any test databases created by the run will not be destroyed.
flag areas in your code that aren't strictly wrong but could benefit
from a better implementation.
-Running tests outside the test runner
--------------------------------------
-
-If you want to run tests outside of ``./manage.py test`` -- for example,
-from a shell prompt -- you will need to set up the test
-environment first. Django provides a convenience method to do this::
-
- >>> from django.test.utils import setup_test_environment
- >>> setup_test_environment()
-
-This convenience method sets up the test database, and puts other
-Django features into modes that allow for repeatable testing.
-
-The call to :meth:`~django.test.utils.setup_test_environment` is made
-automatically as part of the setup of `./manage.py test`. You only
-need to manually invoke this method if you're not using running your
-tests via Django's test runner.
+.. _the-test-database:
The test database
-----------------
@@ -367,9 +195,9 @@ entirely!). If you want to use a different database name, specify
Aside from using a separate database, the test runner will otherwise
use all of the same database settings you have in your settings file:
-:setting:`ENGINE`, :setting:`USER`, :setting:`HOST`, etc. The test
-database is created by the user specified by :setting:`USER`, so you'll need
-to make sure that the given user account has sufficient privileges to
+:setting:`ENGINE <DATABASE-ENGINE>`, :setting:`USER`, :setting:`HOST`, etc. The
+test database is created by the user specified by :setting:`USER`, so you'll
+need to make sure that the given user account has sufficient privileges to
create a new database on the system.
For fine-grained control over the character encoding of your test
@@ -388,100 +216,9 @@ advanced settings.
your tests. *It is a bad idea to have such import-time database queries in
your code* anyway - rewrite your code so that it doesn't do this.
-.. _topics-testing-masterslave:
-
-Testing master/slave configurations
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-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,
-and as a result, data created on the master won't be seen on the
-slave.
-
-To compensate for this, Django allows you to define that a database is
-a *test mirror*. Consider the following (simplified) example database
-configuration::
-
- DATABASES = {
- 'default': {
- 'ENGINE': 'django.db.backends.mysql',
- 'NAME': 'myproject',
- 'HOST': 'dbmaster',
- # ... plus some other settings
- },
- 'slave': {
- 'ENGINE': 'django.db.backends.mysql',
- 'NAME': 'myproject',
- 'HOST': 'dbslave',
- 'TEST_MIRROR': 'default'
- # ... plus some other settings
- }
- }
-
-In this setup, we have two database servers: ``dbmaster``, described
-by the database alias ``default``, and ``dbslave`` described by the
-alias ``slave``. As you might expect, ``dbslave`` has been configured
-by the database administrator as a read slave of ``dbmaster``, so in
-normal activity, any write to ``default`` will appear on ``slave``.
-
-If Django created two independent test databases, this would break any
-tests that expected replication to occur. However, the ``slave``
-database has been configured as a test mirror (using the
-:setting:`TEST_MIRROR` setting), indicating that under testing,
-``slave`` should be treated as a mirror of ``default``.
-
-When the test environment is configured, a test version of ``slave``
-will *not* be created. Instead the connection to ``slave``
-will be redirected to point at ``default``. As a result, writes to
-``default`` will appear on ``slave`` -- but because they are actually
-the same database, not because there is data replication between the
-two databases.
-
-.. _topics-testing-creation-dependencies:
-
-Controlling creation order for test databases
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.. seealso::
-By default, Django will always create the ``default`` database first.
-However, no guarantees are made on the creation order of any other
-databases in your test setup.
-
-If your database configuration requires a specific creation order, you
-can specify the dependencies that exist using the
-:setting:`TEST_DEPENDENCIES` setting. Consider the following
-(simplified) example database configuration::
-
- DATABASES = {
- 'default': {
- # ... db settings
- 'TEST_DEPENDENCIES': ['diamonds']
- },
- 'diamonds': {
- # ... db settings
- },
- 'clubs': {
- # ... db settings
- 'TEST_DEPENDENCIES': ['diamonds']
- },
- 'spades': {
- # ... db settings
- 'TEST_DEPENDENCIES': ['diamonds','hearts']
- },
- 'hearts': {
- # ... db settings
- 'TEST_DEPENDENCIES': ['diamonds','clubs']
- }
- }
-
-Under this configuration, the ``diamonds`` database will be created first,
-as it is the only database alias without dependencies. The ``default`` and
-``clubs`` alias will be created next (although the order of creation of this
-pair is not guaranteed); then ``hearts``; and finally ``spades``.
-
-If there are any circular dependencies in the
-:setting:`TEST_DEPENDENCIES` definition, an ``ImproperlyConfigured``
-exception will be raised.
+ The :ref:`advanced multi-db testing topics <topics-testing-advanced-multidb>`.
Order in which tests are executed
---------------------------------
@@ -598,36 +335,6 @@ to a faster hashing algorithm::
Don't forget to also include in :setting:`PASSWORD_HASHERS` any hashing
algorithm used in fixtures, if any.
-.. _topics-testing-code-coverage:
-
-Integration with coverage.py
-----------------------------
-
-Code coverage describes how much source code has been tested. It shows which
-parts of your code are being exercised by tests and which are not. It's an
-important part of testing applications, so it's strongly recommended to check
-the coverage of your tests.
-
-Django can be easily integrated with `coverage.py`_, a tool for measuring code
-coverage of Python programs. First, `install coverage.py`_. Next, run the
-following from your project folder containing ``manage.py``::
-
- coverage run --source='.' manage.py test myapp
-
-This runs your tests and collects coverage data of the executed files in your
-project. You can see a report of this data by typing following command::
-
- coverage report
-
-Note that some Django code was executed while running tests, but it is not
-listed here because of the ``source`` flag passed to the previous command.
-
-For more options like annotated HTML listings detailing missed lines, see the
-`coverage.py`_ docs.
-
-.. _coverage.py: http://nedbatchelder.com/code/coverage/
-.. _install coverage.py: http://pypi.python.org/pypi/coverage
-
Testing tools
=============
@@ -926,6 +633,14 @@ Use the ``django.test.client.Client`` class to make requests.
The ``follow`` and ``extra`` arguments act the same as for
:meth:`Client.get`.
+ .. method:: Client.patch(path, data='', content_type='application/octet-stream', follow=False, **extra)
+
+ Makes a PATCH request on the provided ``path`` and returns a
+ ``Response`` object. Useful for testing RESTful interfaces.
+
+ The ``follow`` and ``extra`` arguments act the same as for
+ :meth:`Client.get`.
+
.. method:: Client.delete(path, data='', content_type='application/octet-stream', follow=False, **extra)
Makes an DELETE request on the provided ``path`` and returns a
@@ -944,7 +659,7 @@ Use the ``django.test.client.Client`` class to make requests.
.. method:: Client.login(**credentials)
- If your site uses Django's :doc:`authentication system</topics/auth>`
+ If your site uses Django's :doc:`authentication system</topics/auth/index>`
and you deal with logging in users, you can use the test client's
``login()`` method to simulate the effect of a user logging into the
site.
@@ -988,7 +703,7 @@ Use the ``django.test.client.Client`` class to make requests.
.. method:: Client.logout()
- If your site uses Django's :doc:`authentication system</topics/auth>`,
+ If your site uses Django's :doc:`authentication system</topics/auth/index>`,
the ``logout()`` method can be used to simulate the effect of a user
logging out of your site.
@@ -1124,60 +839,14 @@ The following is a simple unit test using the test client::
# Check that the rendered context contains 5 customers.
self.assertEqual(len(response.context['customers']), 5)
-The request factory
--------------------
-
-.. class:: RequestFactory
-
-The :class:`~django.test.client.RequestFactory` shares the same API as
-the test client. However, instead of behaving like a browser, the
-RequestFactory provides a way to generate a request instance that can
-be used as the first argument to any view. This means you can test a
-view function the same way as you would test any other function -- as
-a black box, with exactly known inputs, testing for specific outputs.
-
-The API for the :class:`~django.test.client.RequestFactory` is a slightly
-restricted subset of the test client API:
-
-* It only has access to the HTTP methods :meth:`~Client.get()`,
- :meth:`~Client.post()`, :meth:`~Client.put()`,
- :meth:`~Client.delete()`, :meth:`~Client.head()` and
- :meth:`~Client.options()`.
-
-* These methods accept all the same arguments *except* for
- ``follows``. Since this is just a factory for producing
- requests, it's up to you to handle the response.
-
-* It does not support middleware. Session and authentication
- attributes must be supplied by the test itself if required
- for the view to function properly.
-
-Example
-~~~~~~~
-
-The following is a simple unit test using the request factory::
-
- from django.utils import unittest
- from django.test.client import RequestFactory
-
- class SimpleTest(unittest.TestCase):
- def setUp(self):
- # Every test needs access to the request factory.
- self.factory = RequestFactory()
+.. seealso::
- def test_details(self):
- # Create an instance of a GET request.
- request = self.factory.get('/customer/details')
-
- # Test my_view() as if it were deployed at /customer/details
- response = my_view(request)
- self.assertEqual(response.status_code, 200)
+ :class:`django.test.client.RequestFactory`
-Test cases
-----------
+.. _django-testcase-subclasses:
Provided test case classes
-~~~~~~~~~~~~~~~~~~~~~~~~~~
+--------------------------
.. currentmodule:: django.test
@@ -1191,40 +860,42 @@ Normal Python unit test classes extend a base class of
:width: 508
:height: 391
-Regardless of the version of Python you're using, if you've installed
-:mod:`unittest2`, :mod:`django.utils.unittest` will point to that library.
-
-TestCase
-^^^^^^^^
+ Hierarchy of Django unit testing classes
-.. class:: TestCase()
+Regardless of the version of Python you're using, if you've installed
+``unittest2``, ``django.utils.unittest`` will point to that library.
-This class provides some additional capabilities that can be useful for testing
-Web sites.
+SimpleTestCase
+~~~~~~~~~~~~~~
-Converting a normal :class:`unittest.TestCase` to a Django :class:`TestCase` is
-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:
+.. class:: SimpleTestCase()
-* Automatic loading of fixtures.
+A very thin subclass of :class:`unittest.TestCase`, it extends it with some
+basic functionality like:
-* Wraps each test in a transaction.
+* 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>`
-* Creates a TestClient instance.
+If you need any of the other more complex and heavyweight Django-specific
+features like:
-* Django-specific assertions for testing for things like redirection and form
- errors.
+* 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>`.
+* Test :ref:`skipping based on database backend features <skipping-tests>`.
+* The remaining specialized :ref:`assert* <assertions>` methods.
-.. versionchanged:: 1.5
- The order in which tests are run has changed. See `Order in which tests are
- executed`_.
+then you should use :class:`~django.test.TransactionTestCase` or
+:class:`~django.test.TestCase` instead.
-``TestCase`` inherits from :class:`~django.test.TransactionTestCase`.
+``SimpleTestCase`` inherits from ``django.utils.unittest.TestCase``.
TransactionTestCase
-^^^^^^^^^^^^^^^^^^^
+~~~~~~~~~~~~~~~~~~~
.. class:: TransactionTestCase()
@@ -1295,36 +966,183 @@ to test the effects of commit and rollback:
Using ``reset_sequences = True`` will slow down the test, since the primary
key reset is an relatively expensive database operation.
-SimpleTestCase
-^^^^^^^^^^^^^^
+TestCase
+~~~~~~~~
-.. class:: SimpleTestCase()
+.. class:: TestCase()
+
+This class provides some additional capabilities that can be useful for testing
+Web sites.
-.. versionadded:: 1.4
+Converting a normal :class:`unittest.TestCase` to a Django :class:`TestCase` is
+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:
-A very thin subclass of :class:`unittest.TestCase`, it extends it with some
-basic functionality like:
+* Automatic loading of fixtures.
-* 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>`
+* Wraps each test in a transaction.
-If you need any of the other more complex and heavyweight Django-specific
-features like:
+* Creates a TestClient instance.
-* 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>`.
-* Test :ref:`skipping based on database backend features <skipping-tests>`.
-* The remaining specialized :ref:`assert* <assertions>` methods.
+* Django-specific assertions for testing for things like redirection and form
+ errors.
-then you should use :class:`~django.test.TransactionTestCase` or
-:class:`~django.test.TestCase` instead.
+.. 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`.
+
+.. _live-test-server:
+
+LiveServerTestCase
+~~~~~~~~~~~~~~~~~~
+
+.. class:: LiveServerTestCase()
+
+``LiveServerTestCase`` does basically the same as
+:class:`~django.test.TransactionTestCase` with one extra feature: it launches a
+live Django server in the background on setup, and shuts it down on teardown.
+This allows the use of automated test clients other than the
+:ref:`Django dummy client <test-client>` such as, for example, the Selenium_
+client, to execute a series of functional tests inside a browser and simulate a
+real user's actions.
+
+By default the live server's address is ``'localhost:8081'`` and the full URL
+can be accessed during the tests with ``self.live_server_url``. If you'd like
+to change the default address (in the case, for example, where the 8081 port is
+already taken) then you may pass a different one to the :djadmin:`test` command
+via the :djadminopt:`--liveserver` option, for example:
+
+.. code-block:: bash
+
+ ./manage.py test --liveserver=localhost:8082
+
+Another way of changing the default server address is by setting the
+`DJANGO_LIVE_TEST_SERVER_ADDRESS` environment variable somewhere in your
+code (for example, in a :ref:`custom test runner<topics-testing-test_runner>`):
+
+.. code-block:: python
+
+ import os
+ os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS'] = 'localhost:8082'
+
+In the case where the tests are run by multiple processes in parallel (for
+example, in the context of several simultaneous `continuous integration`_
+builds), the processes will compete for the same address, and therefore your
+tests might randomly fail with an "Address already in use" error. To avoid this
+problem, you can pass a comma-separated list of ports or ranges of ports (at
+least as many as the number of potential parallel processes). For example:
+
+.. code-block:: bash
+
+ ./manage.py test --liveserver=localhost:8082,8090-8100,9000-9200,7041
+
+Then, during test execution, each new live test server will try every specified
+port until it finds one that is free and takes it.
+
+.. _continuous integration: http://en.wikipedia.org/wiki/Continuous_integration
+
+To demonstrate how to use ``LiveServerTestCase``, let's write a simple Selenium
+test. First of all, you need to install the `selenium package`_ into your
+Python path:
+
+.. code-block:: bash
+
+ pip install selenium
+
+Then, add a ``LiveServerTestCase``-based test to your app's tests module
+(for example: ``myapp/tests.py``). The code for this test may look as follows:
+
+.. code-block:: python
+
+ from django.test import LiveServerTestCase
+ from selenium.webdriver.firefox.webdriver import WebDriver
+
+ class MySeleniumTests(LiveServerTestCase):
+ fixtures = ['user-data.json']
+
+ @classmethod
+ def setUpClass(cls):
+ cls.selenium = WebDriver()
+ super(MySeleniumTests, cls).setUpClass()
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.selenium.quit()
+ super(MySeleniumTests, cls).tearDownClass()
+
+ def test_login(self):
+ self.selenium.get('%s%s' % (self.live_server_url, '/login/'))
+ username_input = self.selenium.find_element_by_name("username")
+ username_input.send_keys('myuser')
+ password_input = self.selenium.find_element_by_name("password")
+ password_input.send_keys('secret')
+ self.selenium.find_element_by_xpath('//input[@value="Log in"]').click()
+
+Finally, you may run the test as follows:
+
+.. code-block:: bash
+
+ ./manage.py test myapp.MySeleniumTests.test_login
+
+This example will automatically open Firefox then go to the login page, enter
+the credentials and press the "Log in" button. Selenium offers other drivers in
+case you do not have Firefox installed or wish to use another browser. The
+example above is just a tiny fraction of what the Selenium client can do; check
+out the `full reference`_ for more details.
+
+.. _Selenium: http://seleniumhq.org/
+.. _selenium package: http://pypi.python.org/pypi/selenium
+.. _full reference: http://selenium-python.readthedocs.org/en/latest/api.html
+.. _Firefox: http://www.mozilla.com/firefox/
+
+.. note::
+
+ ``LiveServerTestCase`` makes use of the :doc:`staticfiles contrib app
+ </howto/static-files/index>` so you'll need to have your project configured
+ accordingly (in particular by setting :setting:`STATIC_URL`).
+
+.. note::
+
+ When using an in-memory SQLite database to run the tests, the same database
+ connection will be shared by two threads in parallel: the thread in which
+ the live server is run and the thread in which the test case is run. It's
+ important to prevent simultaneous database queries via this shared
+ connection by the two threads, as that may sometimes randomly cause the
+ tests to fail. So you need to ensure that the two threads don't access the
+ database at the same time. In particular, this means that in some cases
+ (for example, just after clicking a link or submitting a form), you might
+ need to check that a response is received by Selenium and that the next
+ page is loaded before proceeding with further test execution.
+ Do this, for example, by making Selenium wait until the ``<body>`` HTML tag
+ is found in the response (requires Selenium > 2.13):
+
+ .. code-block:: python
+
+ def test_login(self):
+ from selenium.webdriver.support.wait import WebDriverWait
+ timeout = 2
+ ...
+ self.selenium.find_element_by_xpath('//input[@value="Log in"]').click()
+ # Wait until the response is received
+ WebDriverWait(self.selenium, timeout).until(
+ lambda driver: driver.find_element_by_tag_name('body'))
+
+ The tricky thing here is that there's really no such thing as a "page load,"
+ especially in modern Web apps that generate HTML dynamically after the
+ server generates the initial document. So, simply checking for the presence
+ of ``<body>`` in the response might not necessarily be appropriate for all
+ use cases. Please refer to the `Selenium FAQ`_ and
+ `Selenium documentation`_ for more information.
+
+ .. _Selenium FAQ: http://code.google.com/p/selenium/wiki/FrequentlyAskedQuestions#Q:_WebDriver_fails_to_find_elements_/_Does_not_block_on_page_loa
+ .. _Selenium documentation: http://seleniumhq.org/docs/04_webdriver_advanced.html#explicit-waits
-``SimpleTestCase`` inherits from :class:`django.utils.unittest.TestCase`.
+Test cases features
+-------------------
Default test client
~~~~~~~~~~~~~~~~~~~
@@ -1385,6 +1203,7 @@ attribute::
def test_my_stuff(self):
# Here self.client is an instance of MyTestClient...
+ call_some_test_code()
.. _topics-testing-fixtures:
@@ -1533,8 +1352,6 @@ Overriding settings
.. method:: TestCase.settings
-.. versionadded:: 1.4
-
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`)
@@ -1563,7 +1380,7 @@ in the ``with`` block and reset its value to the previous state afterwards.
.. function:: override_settings
In case you want to override a setting for just one test method or even the
-whole :class:`TestCase` class, Django provides the
+whole :class:`~django.test.TestCase` class, Django provides the
:func:`~django.test.utils.override_settings` decorator (see :pep:`318`). It's
used like this::
@@ -1598,6 +1415,14 @@ The decorator can also be applied to test case classes::
the original ``LoginTestCase`` is still equally affected by the
decorator.
+You can also simulate the absence of a setting by deleting it after settings
+have been overriden, like this::
+
+ @override_settings()
+ def test_something(self):
+ del settings.LOGIN_URL
+ ...
+
When overriding settings, make sure to handle the cases in which your app's
code uses a cache or similar feature that retains state even if the
setting is changed. Django provides the
@@ -1623,7 +1448,7 @@ 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.
-For more detail on email services during tests, see `Email services`_.
+For more detail on email services during tests, see `Email services`_ below.
.. _assertions:
@@ -1646,8 +1471,6 @@ your test suite.
.. method:: SimpleTestCase.assertRaisesMessage(expected_exception, expected_message, callable_obj=None, *args, **kwargs)
- .. versionadded:: 1.4
-
Asserts that execution of callable ``callable_obj`` raised the
``expected_exception`` exception and that such exception has an
``expected_message`` representation. Any other outcome is reported as a
@@ -1656,8 +1479,6 @@ your test suite.
.. method:: SimpleTestCase.assertFieldOutput(self, fieldclass, valid, invalid, field_args=None, field_kwargs=None, empty_value=u'')
- .. versionadded:: 1.4
-
Asserts that a form field behaves correctly with various inputs.
:param fieldclass: the class of the field to be tested.
@@ -1667,7 +1488,7 @@ your test suite.
error messages.
:param field_args: the args passed to instantiate the field.
:param field_kwargs: the kwargs passed to instantiate the field.
- :param empty_value: the expected clean output for inputs in ``EMPTY_VALUES``.
+ :param empty_value: the expected clean output for inputs in ``empty_values``.
For example, the following code tests that an ``EmailField`` accepts
"a@a.com" as a valid email address, but rejects "aaa" with a reasonable
@@ -1682,8 +1503,6 @@ your test suite.
that ``text`` appears in the content of the response. If ``count`` is
provided, ``text`` must occur exactly ``count`` times in the response.
- .. versionadded:: 1.4
-
Set ``html`` to ``True`` to handle ``text`` as HTML. The comparison with
the response content will be based on HTML semantics instead of
character-by-character equality. Whitespace is ignored in most cases,
@@ -1695,8 +1514,6 @@ your test suite.
Asserts that a ``Response`` instance produced the given ``status_code`` and
that ``text`` does not appears in the content of the response.
- .. versionadded:: 1.4
-
Set ``html`` to ``True`` to handle ``text`` as HTML. The comparison with
the response content will be based on HTML semantics instead of
character-by-character equality. Whitespace is ignored in most cases,
@@ -1725,8 +1542,6 @@ your test suite.
The name is a string such as ``'admin/index.html'``.
- .. versionadded:: 1.4
-
You can use this as a context manager, like this::
with self.assertTemplateUsed('index.html'):
@@ -1739,8 +1554,6 @@ your test suite.
Asserts that the template with the given name was *not* used in rendering
the response.
- .. versionadded:: 1.4
-
You can use this as a context manager in the same way as
:meth:`~TestCase.assertTemplateUsed`.
@@ -1767,12 +1580,6 @@ your test suite.
provide an implicit ordering, you can set the ``ordered`` parameter to
``False``, which turns the comparison into a Python set comparison.
- .. versionchanged:: 1.4
- The ``ordered`` parameter is new in version 1.4. In earlier versions,
- you would need to ensure the queryset is ordered consistently, possibly
- via an explicit ``order_by()`` call on the queryset prior to
- 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
@@ -1799,8 +1606,6 @@ your test suite.
.. method:: SimpleTestCase.assertHTMLEqual(html1, html2, msg=None)
- .. versionadded:: 1.4
-
Asserts that the strings ``html1`` and ``html2`` are equal. The comparison
is based on HTML semantics. The comparison takes following things into
account:
@@ -1830,8 +1635,6 @@ your test suite.
.. method:: SimpleTestCase.assertHTMLNotEqual(html1, html2, msg=None)
- .. versionadded:: 1.4
-
Asserts that the strings ``html1`` and ``html2`` are *not* equal. The
comparison is based on HTML semantics. See
:meth:`~SimpleTestCase.assertHTMLEqual` for details.
@@ -1939,7 +1742,7 @@ test if the database doesn't support a specific named feature.
The decorators use a string identifier to describe database features.
This string corresponds to attributes of the database connection
-features class. See :class:`~django.db.backends.BaseDatabaseFeatures`
+features class. See ``django.db.backends.BaseDatabaseFeatures``
class for a full list of database features that can be used as a basis
for skipping tests.
@@ -1969,376 +1772,3 @@ under MySQL with MyISAM tables)::
@skipUnlessDBFeature('supports_transactions')
def test_transaction_behavior(self):
# ... conditional test code
-
-Live test server
-----------------
-
-.. versionadded:: 1.4
-
-.. currentmodule:: django.test
-
-.. class:: LiveServerTestCase()
-
-``LiveServerTestCase`` does basically the same as
-:class:`~django.test.TransactionTestCase` with one extra feature: it launches a
-live Django server in the background on setup, and shuts it down on teardown.
-This allows the use of automated test clients other than the
-:ref:`Django dummy client <test-client>` such as, for example, the Selenium_
-client, to execute a series of functional tests inside a browser and simulate a
-real user's actions.
-
-By default the live server's address is `'localhost:8081'` and the full URL
-can be accessed during the tests with ``self.live_server_url``. If you'd like
-to change the default address (in the case, for example, where the 8081 port is
-already taken) then you may pass a different one to the :djadmin:`test` command
-via the :djadminopt:`--liveserver` option, for example:
-
-.. code-block:: bash
-
- ./manage.py test --liveserver=localhost:8082
-
-Another way of changing the default server address is by setting the
-`DJANGO_LIVE_TEST_SERVER_ADDRESS` environment variable somewhere in your
-code (for example, in a :ref:`custom test runner<topics-testing-test_runner>`):
-
-.. code-block:: python
-
- import os
- os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS'] = 'localhost:8082'
-
-In the case where the tests are run by multiple processes in parallel (for
-example, in the context of several simultaneous `continuous integration`_
-builds), the processes will compete for the same address, and therefore your
-tests might randomly fail with an "Address already in use" error. To avoid this
-problem, you can pass a comma-separated list of ports or ranges of ports (at
-least as many as the number of potential parallel processes). For example:
-
-.. code-block:: bash
-
- ./manage.py test --liveserver=localhost:8082,8090-8100,9000-9200,7041
-
-Then, during test execution, each new live test server will try every specified
-port until it finds one that is free and takes it.
-
-.. _continuous integration: http://en.wikipedia.org/wiki/Continuous_integration
-
-To demonstrate how to use ``LiveServerTestCase``, let's write a simple Selenium
-test. First of all, you need to install the `selenium package`_ into your
-Python path:
-
-.. code-block:: bash
-
- pip install selenium
-
-Then, add a ``LiveServerTestCase``-based test to your app's tests module
-(for example: ``myapp/tests.py``). The code for this test may look as follows:
-
-.. code-block:: python
-
- from django.test import LiveServerTestCase
- from selenium.webdriver.firefox.webdriver import WebDriver
-
- class MySeleniumTests(LiveServerTestCase):
- fixtures = ['user-data.json']
-
- @classmethod
- def setUpClass(cls):
- cls.selenium = WebDriver()
- super(MySeleniumTests, cls).setUpClass()
-
- @classmethod
- def tearDownClass(cls):
- cls.selenium.quit()
- super(MySeleniumTests, cls).tearDownClass()
-
- def test_login(self):
- self.selenium.get('%s%s' % (self.live_server_url, '/login/'))
- username_input = self.selenium.find_element_by_name("username")
- username_input.send_keys('myuser')
- password_input = self.selenium.find_element_by_name("password")
- password_input.send_keys('secret')
- self.selenium.find_element_by_xpath('//input[@value="Log in"]').click()
-
-Finally, you may run the test as follows:
-
-.. code-block:: bash
-
- ./manage.py test myapp.MySeleniumTests.test_login
-
-This example will automatically open Firefox then go to the login page, enter
-the credentials and press the "Log in" button. Selenium offers other drivers in
-case you do not have Firefox installed or wish to use another browser. The
-example above is just a tiny fraction of what the Selenium client can do; check
-out the `full reference`_ for more details.
-
-.. _Selenium: http://seleniumhq.org/
-.. _selenium package: http://pypi.python.org/pypi/selenium
-.. _full reference: http://selenium-python.readthedocs.org/en/latest/api.html
-.. _Firefox: http://www.mozilla.com/firefox/
-
-.. note::
-
- ``LiveServerTestCase`` makes use of the :doc:`staticfiles contrib app
- </howto/static-files>` so you'll need to have your project configured
- accordingly (in particular by setting :setting:`STATIC_URL`).
-
-.. note::
-
- When using an in-memory SQLite database to run the tests, the same database
- connection will be shared by two threads in parallel: the thread in which
- the live server is run and the thread in which the test case is run. It's
- important to prevent simultaneous database queries via this shared
- connection by the two threads, as that may sometimes randomly cause the
- tests to fail. So you need to ensure that the two threads don't access the
- database at the same time. In particular, this means that in some cases
- (for example, just after clicking a link or submitting a form), you might
- need to check that a response is received by Selenium and that the next
- page is loaded before proceeding with further test execution.
- Do this, for example, by making Selenium wait until the `<body>` HTML tag
- is found in the response (requires Selenium > 2.13):
-
- .. code-block:: python
-
- def test_login(self):
- from selenium.webdriver.support.wait import WebDriverWait
- timeout = 2
- ...
- self.selenium.find_element_by_xpath('//input[@value="Log in"]').click()
- # Wait until the response is received
- WebDriverWait(self.selenium, timeout).until(
- lambda driver: driver.find_element_by_tag_name('body'))
-
- The tricky thing here is that there's really no such thing as a "page load,"
- especially in modern Web apps that generate HTML dynamically after the
- server generates the initial document. So, simply checking for the presence
- of `<body>` in the response might not necessarily be appropriate for all
- use cases. Please refer to the `Selenium FAQ`_ and
- `Selenium documentation`_ for more information.
-
- .. _Selenium FAQ: http://code.google.com/p/selenium/wiki/FrequentlyAskedQuestions#Q:_WebDriver_fails_to_find_elements_/_Does_not_block_on_page_loa
- .. _Selenium documentation: http://seleniumhq.org/docs/04_webdriver_advanced.html#explicit-waits
-
-Using different testing frameworks
-==================================
-
-Clearly, :mod:`doctest` and :mod:`unittest` are not the only Python testing
-frameworks. While Django doesn't provide explicit support for alternative
-frameworks, it does provide a way to invoke tests constructed for an
-alternative framework as if they were normal Django tests.
-
-When you run ``./manage.py test``, Django looks at the :setting:`TEST_RUNNER`
-setting to determine what to do. By default, :setting:`TEST_RUNNER` points to
-``'django.test.simple.DjangoTestSuiteRunner'``. This class defines the default Django
-testing behavior. This behavior involves:
-
-#. Performing global pre-test setup.
-
-#. Looking for unit tests and doctests in the ``models.py`` and
- ``tests.py`` files in each installed application.
-
-#. Creating the test databases.
-
-#. Running ``syncdb`` to install models and initial data into the test
- databases.
-
-#. Running the unit tests and doctests that are found.
-
-#. Destroying the test databases.
-
-#. Performing global post-test teardown.
-
-If you define your own test runner class and point :setting:`TEST_RUNNER` at
-that class, Django will execute your test runner whenever you run
-``./manage.py test``. In this way, it is possible to use any test framework
-that can be executed from Python code, or to modify the Django test execution
-process to satisfy whatever testing requirements you may have.
-
-.. _topics-testing-test_runner:
-
-Defining a test runner
-----------------------
-
-.. currentmodule:: django.test.simple
-
-A test runner is a class defining a ``run_tests()`` method. Django ships
-with a ``DjangoTestSuiteRunner`` class that defines the default Django
-testing behavior. This class defines the ``run_tests()`` entry point,
-plus a selection of other methods that are used to by ``run_tests()`` to
-set up, execute and tear down the test suite.
-
-.. class:: DjangoTestSuiteRunner(verbosity=1, interactive=True, failfast=True, **kwargs)
-
- ``verbosity`` determines the amount of notification and debug information
- that will be printed to the console; ``0`` is no output, ``1`` is normal
- output, and ``2`` is verbose output.
-
- If ``interactive`` is ``True``, the test suite has permission to ask the
- user for instructions when the test suite is executed. An example of this
- behavior would be asking for permission to delete an existing test
- database. If ``interactive`` is ``False``, the test suite must be able to
- run without any manual intervention.
-
- If ``failfast`` is ``True``, the test suite will stop running after the
- first test failure is detected.
-
- Django will, from time to time, extend the capabilities of
- the test runner by adding new arguments. The ``**kwargs`` declaration
- allows for this expansion. If you subclass ``DjangoTestSuiteRunner`` or
- write your own test runner, ensure accept and handle the ``**kwargs``
- parameter.
-
- .. versionadded:: 1.4
-
- Your test runner may also define additional command-line options.
- If you add an ``option_list`` attribute to a subclassed test runner,
- those options will be added to the list of command-line options that
- the :djadmin:`test` command can use.
-
-Attributes
-~~~~~~~~~~
-
-.. attribute:: DjangoTestSuiteRunner.option_list
-
- .. versionadded:: 1.4
-
- This is the tuple of ``optparse`` options which will be fed into the
- management command's ``OptionParser`` for parsing arguments. See the
- documentation for Python's ``optparse`` module for more details.
-
-Methods
-~~~~~~~
-
-.. method:: DjangoTestSuiteRunner.run_tests(test_labels, extra_tests=None, **kwargs)
-
- Run the test suite.
-
- ``test_labels`` is a list of strings describing the tests to be run. A test
- label can take one of three forms:
-
- * ``app.TestCase.test_method`` -- Run a single test method in a test
- case.
- * ``app.TestCase`` -- Run all the test methods in a test case.
- * ``app`` -- Search for and run all tests in the named application.
-
- If ``test_labels`` has a value of ``None``, the test runner should run
- search for tests in all the applications in :setting:`INSTALLED_APPS`.
-
- ``extra_tests`` is a list of extra ``TestCase`` instances to add to the
- suite that is executed by the test runner. These extra tests are run
- in addition to those discovered in the modules listed in ``test_labels``.
-
- This method should return the number of tests that failed.
-
-.. method:: DjangoTestSuiteRunner.setup_test_environment(**kwargs)
-
- Sets up the test environment ready for testing.
-
-.. method:: DjangoTestSuiteRunner.build_suite(test_labels, extra_tests=None, **kwargs)
-
- Constructs a test suite that matches the test labels provided.
-
- ``test_labels`` is a list of strings describing the tests to be run. A test
- label can take one of three forms:
-
- * ``app.TestCase.test_method`` -- Run a single test method in a test
- case.
- * ``app.TestCase`` -- Run all the test methods in a test case.
- * ``app`` -- Search for and run all tests in the named application.
-
- If ``test_labels`` has a value of ``None``, the test runner should run
- search for tests in all the applications in :setting:`INSTALLED_APPS`.
-
- ``extra_tests`` is a list of extra ``TestCase`` instances to add to the
- suite that is executed by the test runner. These extra tests are run
- in addition to those discovered in the modules listed in ``test_labels``.
-
- Returns a ``TestSuite`` instance ready to be run.
-
-.. method:: DjangoTestSuiteRunner.setup_databases(**kwargs)
-
- Creates the test databases.
-
- Returns a data structure that provides enough detail to undo the changes
- that have been made. This data will be provided to the ``teardown_databases()``
- function at the conclusion of testing.
-
-.. method:: DjangoTestSuiteRunner.run_suite(suite, **kwargs)
-
- Runs the test suite.
-
- Returns the result produced by the running the test suite.
-
-.. method:: DjangoTestSuiteRunner.teardown_databases(old_config, **kwargs)
-
- Destroys the test databases, restoring pre-test conditions.
-
- ``old_config`` is a data structure defining the changes in the
- database configuration that need to be reversed. It is the return
- value of the ``setup_databases()`` method.
-
-.. method:: DjangoTestSuiteRunner.teardown_test_environment(**kwargs)
-
- Restores the pre-test environment.
-
-.. method:: DjangoTestSuiteRunner.suite_result(suite, result, **kwargs)
-
- Computes and returns a return code based on a test suite, and the result
- from that test suite.
-
-
-Testing utilities
------------------
-
-.. module:: django.test.utils
- :synopsis: Helpers to write custom test runners.
-
-To assist in the creation of your own test runner, Django provides a number of
-utility methods in the ``django.test.utils`` module.
-
-.. function:: setup_test_environment()
-
- Performs any global pre-test setup, such as the installing the
- instrumentation of the template rendering system and setting up
- the dummy email outbox.
-
-.. function:: teardown_test_environment()
-
- Performs any global post-test teardown, such as removing the black
- magic hooks into the template system and restoring normal email
- services.
-
-.. currentmodule:: django.db.connection.creation
-
-The creation module of the database backend (``connection.creation``)
-also provides some utilities that can be useful during testing.
-
-.. function:: create_test_db([verbosity=1, autoclobber=False])
-
- Creates a new test database and runs ``syncdb`` against it.
-
- ``verbosity`` has the same behavior as in ``run_tests()``.
-
- ``autoclobber`` describes the behavior that will occur if a
- database with the same name as the test database is discovered:
-
- * If ``autoclobber`` is ``False``, the user will be asked to
- approve destroying the existing database. ``sys.exit`` is
- called if the user does not approve.
-
- * If autoclobber is ``True``, the database will be destroyed
- without consulting the user.
-
- Returns the name of the test database that it created.
-
- ``create_test_db()`` has the side effect of modifying the value of
- :setting:`NAME` in :setting:`DATABASES` to match the name of the test
- database.
-
-.. function:: destroy_test_db(old_database_name, [verbosity=1])
-
- Destroys the database whose name is the value of :setting:`NAME` in
- :setting:`DATABASES`, and sets :setting:`NAME` to the value of
- ``old_database_name``.
-
- The ``verbosity`` argument has the same behavior as for
- :class:`~django.test.simple.DjangoTestSuiteRunner`.