diff options
| author | Honza Král <honza.kral@gmail.com> | 2009-11-07 17:09:09 +0000 |
|---|---|---|
| committer | Honza Král <honza.kral@gmail.com> | 2009-11-07 17:09:09 +0000 |
| commit | 30ea350dabad28b0e524feabce434d446e013d6f (patch) | |
| tree | 880b6b01d6cbf9423943025f77d8b4ad0b460e83 /docs | |
| parent | dfe495fbe8e360ee3b3cd8b29e55ee19d86fc9d2 (diff) | |
[soc2009/model-validation] Merged to trunk at r11724
git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/model-validation@11725 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'docs')
26 files changed, 1054 insertions, 252 deletions
diff --git a/docs/faq/install.txt b/docs/faq/install.txt index fb8005c7e7..28a89ccc1f 100644 --- a/docs/faq/install.txt +++ b/docs/faq/install.txt @@ -18,7 +18,7 @@ How do I get started? What are Django's prerequisites? -------------------------------- -Django requires Python_, specifically any version of Python from 2.3 +Django requires Python_, specifically any version of Python from 2.4 through 2.6. No other Python libraries are required for basic Django usage. @@ -42,30 +42,35 @@ PostgreSQL fans, and MySQL_, `SQLite 3`_, and Oracle_ are also supported. .. _`SQLite 3`: http://www.sqlite.org/ .. _Oracle: http://www.oracle.com/ -Do I lose anything by using Python 2.3 versus newer Python versions, such as Python 2.5? ----------------------------------------------------------------------------------------- +Do I lose anything by using Python 2.4 versus newer Python versions, such as Python 2.5 or 2.6? +----------------------------------------------------------------------------------------------- -Not in the core framework. Currently, Django itself officially -supports any version of Python from 2.3 through 2.6, -inclusive. However, some add-on components may require a more recent -Python version; the ``django.contrib.gis`` component, for example, -requires at least Python 2.4, and third-party applications for use -with Django are, of course, free to set their own version -requirements. +Not in the core framework. Currently, Django itself officially supports any +version of Python from 2.4 through 2.6, inclusive. However, newer versions of +Python are often faster, have more features, and are better supported. +Third-party applications for use with Django are, of course, free to set their +own version requirements. -Please note, however, that over the next year or two Django will begin -dropping support for older Python versions as part of a migration -which will end with Django running on Python 3.0 (see next question -for details). So if you're just starting out with Python, it's -recommended that you use the latest 2.x release (currently, Python -2.6). This will let you take advantage of the numerous improvements -and optimizations to the Python language since version 2.3, and will -help ease the process of dropping support for older Python versions on -the road to Python 3.0. +Over the next year or two Django will begin dropping support for older Python +versions as part of a migration which will end with Django running on Python 3 +(see below for details). -Can I use Django with Python 3.0? +All else being equal, we recommend that you use the latest 2.x release +(currently Python 2.6). This will let you take advantage of the numerous +improvements and optimizations to the Python language since version 2.4, and +will help ease the process of dropping support for older Python versions on +the road to Python 3. + +Can I use Django with Python 2.3? --------------------------------- +Django 1.1 (and earlier) supported Python 2.3. Django 1.2 and newer does not. +We highly recommend you upgrade Python if at all possible, but Django 1.1 will +continue to work on Python 2.3. + +Can I use Django with Python 3? +------------------------------- + Not at the moment. Python 3.0 introduced a number of backwards-incompatible changes to the Python language, and although these changes are generally a good thing for Python's future, it will diff --git a/docs/internals/committers.txt b/docs/internals/committers.txt index 7326532ec9..803c3140c7 100644 --- a/docs/internals/committers.txt +++ b/docs/internals/committers.txt @@ -148,7 +148,6 @@ Joseph Kocherhans .. _brian rosner: http://oebfare.com/ .. _eldarion: http://eldarion.com/ -.. _pinax: http://pinaxproject.com/ .. _django dose: http://djangodose.com/ `Gary Wilson`_ @@ -189,6 +188,18 @@ Karen Tracey Karen lives in Apex, NC, USA. +`Jannis Leidel`_ + Jannis graduated in media design from `Bauhaus-University Weimar`_, + is the author of a number of pluggable Django apps and likes to + contribute to Open Source projects like Pinax_. He currently works as + a freelance web developer and designer. + + Jannis lives in Berlin, Germany. + +.. _Jannis Leidel: http://jezdez.com/ +.. _Bauhaus-University Weimar: http://www.uni-weimar.de/ +.. _pinax: http://pinaxproject.com/ + Specialists ----------- diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index 7e7f4c6338..480b527d6b 100644 --- a/docs/internals/deprecation.txt +++ b/docs/internals/deprecation.txt @@ -13,6 +13,21 @@ their deprecation, as per the :ref:`Django deprecation policy hooking up admin URLs. This has been deprecated since the 1.1 release. + * 1.4 + * ``CsrfResponseMiddleware``. This has been deprecated since the 1.2 + release, in favour of the template tag method for inserting the CSRF + token. ``CsrfMiddleware``, which combines ``CsrfResponseMiddleware`` + and ``CsrfViewMiddleware``, is also deprecated. + + * The old imports for CSRF functionality (``django.contrib.csrf.*``), + which moved to core in 1.2, will be removed. + + * ``SMTPConnection``. The 1.2 release deprecated the ``SMTPConnection`` + class in favor of a generic E-mail backend API. + + * The many to many SQL generation functions on the database backends + will be removed. These have been deprecated since the 1.2 release. + * 2.0 * ``django.views.defaults.shortcut()``. This function has been moved to ``django.contrib.contenttypes.views.shortcut()`` as part of the diff --git a/docs/internals/release-process.txt b/docs/internals/release-process.txt index 6d4ad9e8c9..e990ab8ab6 100644 --- a/docs/internals/release-process.txt +++ b/docs/internals/release-process.txt @@ -56,7 +56,7 @@ 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 -``PendingDeprecationWarning`` but will continue to work. Version ``A.B+3`` will +``DeprecationWarning`` but will continue to work. Version ``A.B+3`` will remove the feature entirely. So, for example, if we decided to remove a function that existed in Django 1.0: diff --git a/docs/intro/install.txt b/docs/intro/install.txt index 237c208f2a..d0776a6ea3 100644 --- a/docs/intro/install.txt +++ b/docs/intro/install.txt @@ -12,7 +12,7 @@ Install Python -------------- Being a Python Web framework, Django requires Python. It works with any Python -version from 2.3 to 2.6 (due to backwards +version from 2.4 to 2.6 (due to backwards incompatibilities in Python 3.0, Django does not currently work with Python 3.0; see :ref:`the Django FAQ <faq-install>` for more information on supported Python versions and the 3.0 transition), but we recommend installing Python 2.5 or later. If you do so, you won't need to set up a database just yet: Python 2.5 or later includes a lightweight database called SQLite_. diff --git a/docs/intro/tutorial01.txt b/docs/intro/tutorial01.txt index 69c8d0f3db..afda1f28a2 100644 --- a/docs/intro/tutorial01.txt +++ b/docs/intro/tutorial01.txt @@ -281,6 +281,7 @@ That'll create a directory :file:`polls`, which is laid out like this:: polls/ __init__.py models.py + tests.py views.py This directory structure will house the poll application. diff --git a/docs/intro/tutorial02.txt b/docs/intro/tutorial02.txt index 203c945c02..ad1bd9d990 100644 --- a/docs/intro/tutorial02.txt +++ b/docs/intro/tutorial02.txt @@ -34,11 +34,11 @@ activate the admin site for your installation, do these three things: * 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 below the - "Uncomment the next two lines..." comment. 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: + * 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: .. versionchanged:: 1.1 The method for adding admin urls has changed in Django 1.1. diff --git a/docs/intro/tutorial03.txt b/docs/intro/tutorial03.txt index 238dc63f71..1438a9e776 100644 --- a/docs/intro/tutorial03.txt +++ b/docs/intro/tutorial03.txt @@ -171,15 +171,23 @@ and put the following Python code in it:: This is the simplest view possible. Go to "/polls/" in your browser, and you should see your text. -Now add the following view. It's slightly different, because it takes an -argument (which, remember, is passed in from whatever was captured by the -regular expression in the URLconf):: +Now lets add a few more views. These views are slightly different, because +they take an argument (which, remember, is passed in from whatever was +captured by the regular expression in the URLconf):: def detail(request, poll_id): return HttpResponse("You're looking at poll %s." % poll_id) -Take a look in your browser, at "/polls/34/". It'll display whatever ID you -provide in the URL. + def results(request, poll_id): + return HttpResponse("You're looking at the results of poll %s." % poll_id) + + def vote(request, poll_id): + return HttpResponse("You're voting on poll %s." % poll_id) + +Take a look in your browser, at "/polls/34/". It'll run the `detail()` method +and display whatever ID you provide in the URL. Try "/polls/34/results/" and +"/polls/34/vote/" too -- these will display the placeholder results and voting +pages. Write views that actually do something ====================================== @@ -467,10 +475,10 @@ Copy the file ``mysite/urls.py`` to ``mysite/polls/urls.py``. Then, change ``mysite/urls.py`` to remove the poll-specific URLs and insert an :func:`~django.conf.urls.defaults.include`:: - ... + # ... urlpatterns = patterns('', (r'^polls/', include('mysite.polls.urls')), - ... + # ... :func:`~django.conf.urls.defaults.include`, simply, references another URLconf. Note that the regular expression doesn't have a ``$`` (end-of-string match diff --git a/docs/intro/tutorial04.txt b/docs/intro/tutorial04.txt index 28ace85ca8..394fc25ea8 100644 --- a/docs/intro/tutorial04.txt +++ b/docs/intro/tutorial04.txt @@ -21,6 +21,7 @@ tutorial, so that the template contains an HTML ``<form>`` element: {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %} <form action="/polls/{{ poll.id }}/vote/" method="post"> + {% csrf_token %} {% for choice in poll.choice_set.all %} <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" /> <label for="choice{{ forloop.counter }}">{{ choice.choice }}</label><br /> @@ -46,17 +47,41 @@ A quick rundown: * ``forloop.counter`` indicates how many times the :ttag:`for` tag has gone through its loop + * Since we are creating a POST form (which can have the effect of modifying + data), we unfortunately need to worry about Cross Site Request Forgeries. + Thankfully, you don't have to worry too hard, because Django comes with + very easy-to-use system for protecting against it. In short, all POST + forms that are targetted at internal URLs need the ``{% csrf_token %}`` + template tag adding. + +The ``{% csrf_token %}`` tag requires information from the request object, which +is not normally accessible from within the template context. To fix this, a +small adjustment needs to be made to the ``detail`` view, so that it looks like +the following:: + + from django.template import RequestContext + # ... + def detail(request, poll_id): + p = get_object_or_404(Poll, pk=poll_id) + return render_to_response('polls/detail.html', {'poll': p}, + context_instance=RequestContext(request)) + +The details of how this works are explained in the documentation for +:ref:`RequestContext <subclassing-context-requestcontext>`. + Now, let's create a Django view that handles the submitted data and does something with it. Remember, in :ref:`Tutorial 3 <intro-tutorial03>`, we created a URLconf for the polls application that includes this line:: (r'^(?P<poll_id>\d+)/vote/$', 'vote'), -So let's create a ``vote()`` function in ``mysite/polls/views.py``:: +We also created a dummy implementation of the ``vote()`` function. Let's +create a real version. Add the following to ``mysite/polls/views.py``:: from django.shortcuts import get_object_or_404, render_to_response - from django.http import HttpResponseRedirect + from django.http import HttpResponseRedirect, HttpResponse from django.core.urlresolvers import reverse + from django.template import RequestContext from mysite.polls.models import Choice, Poll # ... def vote(request, poll_id): @@ -68,7 +93,7 @@ So let's create a ``vote()`` function in ``mysite/polls/views.py``:: return render_to_response('polls/detail.html', { 'poll': p, 'error_message': "You didn't select a choice.", - }) + }, context_instance=RequestContext(request)) else: selected_choice.votes += 1 selected_choice.save() diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt index c1e05eda1d..0f746bf01b 100644 --- a/docs/ref/contrib/admin/index.txt +++ b/docs/ref/contrib/admin/index.txt @@ -770,7 +770,7 @@ documented in :ref:`topics-http-urls`:: However, the ``self.my_view`` function registered above suffers from two problems: - * It will *not* perform and permission checks, so it will be accessible to + * It will *not* perform any permission checks, so it will be accessible to the general public. * It will *not* provide any header details to prevent caching. This means if the page retrieves data from the database, and caching middleware is @@ -1048,16 +1048,70 @@ automatically:: FriendshipInline, ] +Working with Many-to-Many Models +-------------------------------- + +.. versionadded:: 1.2 + +By default, admin widgets for many-to-many relations will be displayed +on whichever model contains the actual reference to the ``ManyToManyField``. +Depending on your ``ModelAdmin`` definition, each many-to-many field in your +model will be represented by a standard HTML ``<select multiple>``, a +horizontal or vertical filter, or a ``raw_id_admin`` widget. However, it is +also possible to to replace these widgets with inlines. + +Suppose we have the following models:: + + class Person(models.Model): + name = models.CharField(max_length=128) + + class Group(models.Model): + name = models.CharField(max_length=128) + members = models.ManyToManyField(Person, related_name='groups') + +If you want to display many-to-many relations using an inline, you can do +so by defining an ``InlineModelAdmin`` object for the relationship:: + + class MembershipInline(admin.TabularInline): + model = Group.members.through + + class PersonAdmin(admin.ModelAdmin): + inlines = [ + MembershipInline, + ] + + class GroupAdmin(admin.ModelAdmin): + inlines = [ + MembershipInline, + ] + exclude = ('members',) + +There are two features worth noting in this example. + +Firstly - the ``MembershipInline`` class references ``Group.members.through``. +The ``through`` attribute is a reference to the model that manages the +many-to-many relation. This model is automatically created by Django when you +define a many-to-many field. + +Secondly, the ``GroupAdmin`` must manually exclude the ``members`` field. +Django displays an admin widget for a many-to-many field on the model that +defines the relation (in this case, ``Group``). If you want to use an inline +model to represent the many-to-many relationship, you must tell Django's admin +to *not* display this widget - otherwise you will end up with two widgets on +your admin page for managing the relation. + +In all other respects, the ``InlineModelAdmin`` is exactly the same as any +other. You can customize the appearance using any of the normal +``InlineModelAdmin`` properties. + Working with Many-to-Many Intermediary Models ---------------------------------------------- -By default, admin widgets for many-to-many relations will be displayed inline -on whichever model contains the actual reference to the ``ManyToManyField``. -However, when you specify an intermediary model using the ``through`` -argument to a ``ManyToManyField``, the admin will not display a widget by -default. This is because each instance of that intermediary model requires -more information than could be displayed in a single widget, and the layout -required for multiple widgets will vary depending on the intermediate model. +When you specify an intermediary model using the ``through`` argument to a +``ManyToManyField``, the admin will not display a widget by default. This is +because each instance of that intermediary model requires more information +than could be displayed in a single widget, and the layout required for +multiple widgets will vary depending on the intermediate model. However, we still want to be able to edit that information inline. Fortunately, this is easy to do with inline admin models. Suppose we have the following diff --git a/docs/ref/contrib/comments/index.txt b/docs/ref/contrib/comments/index.txt index 880be34101..6ee109782f 100644 --- a/docs/ref/contrib/comments/index.txt +++ b/docs/ref/contrib/comments/index.txt @@ -216,6 +216,13 @@ should know about: it with a warning field; if you use the comment form with a custom template you should be sure to do the same. +The comments app also depends on the more general :ref:`Cross Site Request +Forgery protection < ref-contrib-csrf>` that comes with Django. As described in +the documentation, it is best to use ``CsrfViewMiddleware``. However, if you +are not using that, you will need to use the ``csrf_protect`` decorator on any +views that include the comment form, in order for those views to be able to +output the CSRF token and cookie. + .. _honeypot: http://en.wikipedia.org/wiki/Honeypot_(computing) More information diff --git a/docs/ref/contrib/csrf.txt b/docs/ref/contrib/csrf.txt index 1b6b6102de..c1bdb59cd1 100644 --- a/docs/ref/contrib/csrf.txt +++ b/docs/ref/contrib/csrf.txt @@ -4,121 +4,421 @@ Cross Site Request Forgery protection ===================================== -.. module:: django.contrib.csrf +.. module:: django.middleware.csrf :synopsis: Protects against Cross Site Request Forgeries -The CsrfMiddleware class provides easy-to-use protection against +The CSRF middleware and template tag provides easy-to-use protection against `Cross Site Request Forgeries`_. This type of attack occurs when a malicious -Web site creates a link or form button that is intended to perform some action -on your Web site, using the credentials of a logged-in user who is tricked -into clicking on the link in their browser. +Web site contains a link, a form button or some javascript that is intended to +perform some action on your Web site, using the credentials of a logged-in user +who visits the malicious site in their browser. A related type of attack, +'login CSRF', where an attacking site tricks a user's browser into logging into +a site with someone else's credentials, is also covered. -The first defense against CSRF attacks is to ensure that GET requests -are side-effect free. POST requests can then be protected by adding this -middleware into your list of installed middleware. +The first defense against CSRF attacks is to ensure that GET requests are +side-effect free. POST requests can then be protected by following the steps +below. + +.. versionadded:: 1.2 + The 'contrib' apps, including the admin, use the functionality described + here. Because it is security related, a few things have been added to core + functionality to allow this to happen without any required upgrade steps. .. _Cross Site Request Forgeries: http://www.squarefree.com/securitytips/web-developers.html#CSRF How to use it ============= -Add the middleware ``'django.contrib.csrf.middleware.CsrfMiddleware'`` to -your list of middleware classes, :setting:`MIDDLEWARE_CLASSES`. It needs to process -the response after the SessionMiddleware, so must come before it in the -list. It also must process the response before things like compression -happen to the response, so it must come after GZipMiddleware in the -list. +.. versionchanged:: 1.2 + The template tag functionality (the recommended way to use this) was added + in version 1.2. The previous method (still available) is described under + `Legacy method`_. + +To enable CSRF protection for your views, follow these steps: + + 1. Add the middleware + ``'django.middleware.csrf.CsrfViewMiddleware'`` to your list of + middleware classes, :setting:`MIDDLEWARE_CLASSES`. (It should come + before ``CsrfResponseMiddleware`` if that is being used, and before any + view middleware that assume that CSRF attacks have been dealt with.) + + Alternatively, you can use the decorator + ``django.views.decorators.csrf.csrf_protect`` on particular views you + want to protect (see below). + + 2. In any template that uses a POST form, use the ``csrf_token`` tag inside + the ``<form>`` element if the form is for an internal URL, e.g.:: + + <form action="" method="POST">{% csrf_token %} + + This should not be done for POST forms that target external URLs, since + that would cause the CSRF token to be leaked, leading to a vulnerability. + + 3. In the corresponding view functions, ensure that the + ``'django.core.context_processors.csrf'`` context processor is + being used. Usually, this can be done in one of two ways: + + 1. Use RequestContext, which always uses + ``'django.core.context_processors.csrf'`` (no matter what your + TEMPLATE_CONTEXT_PROCESSORS setting). If you are using + generic views or contrib apps, you are covered already, since these + apps use RequestContext throughout. + + 2. Manually import and use the processor to generate the CSRF token and + add it to the template context. e.g.:: + + from django.core.context_processors import csrf + from django.shortcuts import render_to_response + + def my_view(request): + c = {} + c.update(csrf(request)) + # ... view code here + return render_to_response("a_template.html", c) + + You may want to write your own ``render_to_response`` wrapper that + takes care of this step for you. + +The utility script ``extras/csrf_migration_helper.py`` can help to automate the +finding of code and templates that may need to be upgraded. It contains full +help on how to use it. + +The decorator method +-------------------- + +Rather than adding ``CsrfViewMiddleware`` as a blanket protection, you can use +the ``csrf_protect`` decorator, which has exactly the same functionality, on +particular views that need the protection. It must be used **both** on views +that insert the CSRF token in the output, and on those that accept the POST form +data. (These are often the same view function, but not always). It is used like +this:: + + from django.views.decorators.csrf import csrf_protect + from django.template import RequestContext + + @csrf_protect + def my_view(request): + c = {} + # ... + return render_to_response("a_template.html", c, + context_instance=RequestContext(request)) + +Use of the decorator is **not recommended** by itself, since if you forget to +use it, you will have a security hole. The 'belt and braces' strategy of using +both is fine, and will incur minimal overhead. + +Legacy method +------------- + +In Django 1.1, the template tag did not exist. Instead, a post-processing +middleware that re-wrote POST forms to include the CSRF token was used. If you +are upgrading a site from version 1.1 or earlier, please read this section and +the `Upgrading notes`_ below. The post-processing middleware is still available +as ``CsrfResponseMiddleware``, and it can be used by following these steps: + + 1. Follow step 1 above to install ``CsrfViewMiddleware``. + + 2. Add ``'django.middleware.csrf.CsrfResponseMiddleware'`` to your + :setting:`MIDDLEWARE_CLASSES` setting. + + ``CsrfResponseMiddleware`` needs to process the response before things + like compression or setting ofETags happen to the response, so it must + come after ``GZipMiddleware``, ``CommonMiddleware`` and + ``ConditionalGetMiddleware`` in the list. It also must come after + ``CsrfViewMiddleware``. + +Use of the ``CsrfResponseMiddleware`` is not recommended because of the +performance hit it imposes, and because of a potential security problem (see +below). It can be used as an interim measure until applications have been +updated to use the ``{% csrf_token %}`` tag. It is deprecated and will be +removed in Django 1.4. + +Django 1.1 and earlier provided a single ``CsrfMiddleware`` class. This is also +still available for backwards compatibility. It combines the functions of the +two middleware. + +Note also that previous versions of these classes depended on the sessions +framework, but this dependency has now been removed, with backward compatibility +support so that upgrading will not produce any issues. + +Security of legacy method +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The post-processing ``CsrfResponseMiddleware`` adds the CSRF token to all POST +forms (unless the view has been decorated with ``csrf_response_exempt``). If +the POST form has an external untrusted site as its target, rather than an +internal page, that site will be sent the CSRF token when the form is submitted. +Armed with this leaked information, that site will then be able to successfully +launch a CSRF attack on your site against that user. The +``@csrf_response_exempt`` decorator can be used to fix this, but only if the +page doesn't also contain internal forms that require the token. + +Upgrading notes +--------------- + +When upgrading to version 1.2 or later, you may have applications that rely on +the old post-processing functionality for CSRF protection, or you may not have +enabled any CSRF protection. This section outlines the steps necessary for a +smooth upgrade, without having to fix all the applications to use the new +template tag method immediately. + +First of all, the location of the middleware and related functions have +changed. There are backwards compatible stub files so that old imports will +continue to work for now, but they are deprecated and will be removed in Django +1.4. The following changes have been made: + + * Middleware have been moved to ``django.middleware.csrf`` + * Decorators have been moved to ``django.views.decorators.csrf`` -The ``CsrfMiddleware`` class is actually composed of two middleware: -``CsrfViewMiddleware`` which performs the checks on incoming requests, -and ``CsrfResponseMiddleware`` which performs post-processing of the -result. This allows the individual components to be used and/or -replaced instead of using ``CsrfMiddleware``. +====================================================== ============================================== + Old New +====================================================== ============================================== +django.contrib.csrf.middleware.CsrfMiddleware django.middleware.csrf.CsrfMiddleware +django.contrib.csrf.middleware.CsrfViewMiddleware django.middleware.csrf.CsrfViewMiddleware +django.contrib.csrf.middleware.CsrfResponseMiddleware django.middleware.csrf.CsrfResponseMiddleware +django.contrib.csrf.middleware.csrf_exempt django.views.decorators.csrf_exempt +django.contrib.csrf.middleware.csrf_view_exempt django.views.decorators.csrf_view_exempt +django.contrib.csrf.middleware.csrf_response_exempt django.views.decorators.csrf_response_exempt +====================================================== ============================================== -.. versionchanged:: 1.1 - (previous versions of Django did not provide these two components - of ``CsrfMiddleware`` as described above) +You should update any imports, and also the paths in your +:setting:`MIDDLEWARE_CLASSES`. + +If you have ``CsrfMiddleware`` in your :setting:`MIDDLEWARE_CLASSES`, you will now +have a working installation with CSRF protection. It is recommended at this +point that you replace ``CsrfMiddleware`` with its two components, +``CsrfViewMiddleware`` and ``CsrfResponseMiddleware`` (in that order). + +If you do not have any of the middleware in your :setting:`MIDDLEWARE_CLASSES`, +you will have a working installation but without any CSRF protection for your +views (just as you had before). It is strongly recommended to install +``CsrfViewMiddleware`` and ``CsrfResponseMiddleware``, as described above. + +Note that contrib apps, such as the admin, have been updated to use the +``csrf_protect`` decorator, so that they are secured even if you do not add the +``CsrfViewMiddleware`` to your settings. However, if you have supplied +customised templates to any of the view functions of contrib apps (whether +explicitly via a keyword argument, or by overriding built-in templates), **you +MUST update them** to include the ``csrf_token`` template tag as described +above, or they will stop working. (If you cannot update these templates for +some reason, you will be forced to use ``CsrfResponseMiddleware`` for these +views to continue working). + +Note also, if you are using the comments app, and you are not going to add +``CsrfViewMiddleware`` to your settings (not recommended), you will need to add +the ``csrf_protect`` decorator to any views that include the comment forms and +target the comment views (usually using the :ttag:`comment_form_target` template +tag). + +Assuming you have followed the above, all views in your Django site will now be +protected by the ``CsrfViewMiddleware``. Contrib apps meet the requirements +imposed by the ``CsrfViewMiddleware`` using the template tag, and other +applications in your project will meet its requirements by virtue of the +``CsrfResponseMiddleware``. + +The next step is to update all your applications to use the template tag, as +described in `How to use it`_, steps 2-3. This can be done as soon as is +practical. Any applications that are updated will now require Django 1.1.2 or +later, since they will use the CSRF template tag which was not available in +earlier versions. (The template tag in 1.1.2 is actually a no-op that exists +solely to ease the transition to 1.2 — it allows apps to be created that have +CSRF protection under 1.2 without requiring users of the apps to upgrade to the +Django 1.2.X series). + +The utility script ``extras/csrf_migration_helper.py`` can help to automate the +finding of code and templates that may need to be upgraded. It contains full +help on how to use it. + +Finally, once all applications are upgraded, ``CsrfResponseMiddleware`` can be +removed from your settings. + +While ``CsrfResponseMiddleware`` is still in use, the ``csrf_response_exempt`` +decorator, described in `Exceptions`_, may be useful. The post-processing +middleware imposes a performance hit and a potential vulnerability, and any +views that have been upgraded to use the new template tag method no longer need +it. Exceptions ---------- .. versionadded:: 1.1 -To manually exclude a view function from being handled by the -CsrfMiddleware, you can use the ``csrf_exempt`` decorator, found in -the ``django.contrib.csrf.middleware`` module. For example:: +To manually exclude a view function from being handled by either of the two CSRF +middleware, you can use the ``csrf_exempt`` decorator, found in the +``django.views.decorators.csrf`` module. For example:: - from django.contrib.csrf.middleware import csrf_exempt + from django.views.decorators.csrf import csrf_exempt + @csrf_exempt def my_view(request): return HttpResponse('Hello world') - my_view = csrf_exempt(my_view) -Like the middleware itself, the ``csrf_exempt`` decorator is composed -of two parts: a ``csrf_view_exempt`` decorator and a -``csrf_response_exempt`` decorator, found in the same module. These -disable the view protection mechanism (``CsrfViewMiddleware``) and the -response post-processing (``CsrfResponseMiddleware``) respectively. -They can be used individually if required. +Like the middleware, the ``csrf_exempt`` decorator is composed of two parts: a +``csrf_view_exempt`` decorator and a ``csrf_response_exempt`` decorator, found +in the same module. These disable the view protection mechanism +(``CsrfViewMiddleware``) and the response post-processing +(``CsrfResponseMiddleware``) respectively. They can be used individually if +required. + +You don't have to worry about doing this for most AJAX views. Any request sent +with "X-Requested-With: XMLHttpRequest" is automatically exempt. (See the `How +it works`_ section.) + +Subdomains +---------- + +By default, CSRF cookies are specific to the subdomain they are set for. This +means that a form served from one subdomain (e.g. server1.example.com) will not +be able to have a target on another subdomain (e.g. server2.example.com). This +restriction can be removed by setting :setting:`CSRF_COOKIE_DOMAIN` to be +something like ``".example.com"``. + +Please note that, with or without use of this setting, this CSRF protection +mechanism is not safe against cross-subdomain attacks -- see `Limitations`_. + +Rejected requests +================= + +By default, a '403 Forbidden' response is sent to the user if an incoming +request fails the checks performed by ``CsrfViewMiddleware``. This should +usually only be seen when there is a genuine Cross Site Request Forgery, or +when, due to a programming error, the CSRF token has not been included with a +POST form. + +No logging is done, and the error message is not very friendly, so you may want +to provide your own page for handling this condition. To do this, simply set +the :setting:`CSRF_FAILURE_VIEW` setting to a dotted path to your own view +function, which should have the following signature:: -You don't have to worry about doing this for most AJAX views. Any -request sent with "X-Requested-With: XMLHttpRequest" is automatically -exempt. (See the next section.) + def csrf_failure(request, reason="") + +where ``reason`` is a short message (intended for developers or logging, not for +end users) indicating the reason the request was rejected. How it works ============ -CsrfMiddleware does two things: +The CSRF protection is based on the following things: + +1. A CSRF cookie that is set to a random value (a session independent nonce, as + it is called), which other sites will not have access to. + + This cookie is set by ``CsrfViewMiddleware``. It is meant to be permanent, + but since there is no way to set a cookie that never expires, it is sent with + every response that has called ``django.middleware.csrf.get_token()`` + (the function used internally to retrieve the CSRF token). + +2. A hidden form field with the name 'csrfmiddlewaretoken' present in all + outgoing POST forms. The value of this field is the value of the CSRF + cookie. -1. It modifies outgoing requests by adding a hidden form field to all - 'POST' forms, with the name 'csrfmiddlewaretoken' and a value which is - a hash of the session ID plus a secret. If there is no session ID set, - this modification of the response isn't done, so there is very little - performance penalty for those requests that don't have a session. - (This is done by ``CsrfResponseMiddleware``). + This part is done by the template tag (and with the legacy method, it is done + by ``CsrfResponseMiddleware``). -2. On all incoming POST requests that have the session cookie set, it - checks that the 'csrfmiddlewaretoken' is present and correct. If it - isn't, the user will get a 403 error. (This is done by - ``CsrfViewMiddleware``) +3. For all incoming POST requests, a CSRF cookie must be present, and the + 'csrfmiddlewaretoken' field must be present and correct. If it isn't, the + user will get a 403 error. -This ensures that only forms that have originated from your Web site -can be used to POST data back. + This check is done by ``CsrfViewMiddleware``. + +4. In addition, for HTTPS requests, strict referer checking is done by + ``CsrfViewMiddleware``. This is necessary to address a Man-In-The-Middle + attack that is possible under HTTPS when using a session independent nonce, + due to the fact that HTTP 'Set-Cookie' headers are (unfortunately) accepted + by clients that are talking to a site under HTTPS. (Referer checking is not + done for HTTP requests because the presence of the Referer header is not + reliable enough under HTTP.) + +This ensures that only forms that have originated from your Web site can be used +to POST data back. It deliberately only targets HTTP POST requests (and the corresponding POST -forms). GET requests ought never to have any potentially dangerous side -effects (see `9.1.1 Safe Methods, HTTP 1.1, RFC 2616`_), and so a -CSRF attack with a GET request ought to be harmless. +forms). GET requests ought never to have any potentially dangerous side effects +(see `9.1.1 Safe Methods, HTTP 1.1, RFC 2616`_), and so a CSRF attack with a GET +request ought to be harmless. -POST requests that are not accompanied by a session cookie are not protected, -but they do not need to be protected, since the 'attacking' Web site -could make these kind of requests anyway. +``CsrfResponseMiddleware`` checks the Content-Type before modifying the +response, and only pages that are served as 'text/html' or +'application/xml+xhtml' are modified. -The Content-Type is checked before modifying the response, and only -pages that are served as 'text/html' or 'application/xml+xhtml' -are modified. +AJAX +---- -The middleware tries to be smart about requests that come in via AJAX. Many -JavaScript toolkits send an "X-Requested-With: XMLHttpRequest" HTTP header; -these requests are detected and automatically *not* handled by this middleware. -We can do this safely because, in the context of a browser, the header can only -be added by using ``XMLHttpRequest``, and browsers already implement a -same-domain policy for ``XMLHttpRequest``. (Note that this is not secure if you -don't trust content within the same domain or subdomains.) +The middleware tries to be smart about requests that come in via AJAX. Most +modern JavaScript toolkits send an "X-Requested-With: XMLHttpRequest" HTTP +header; these requests are detected and automatically *not* handled by this +middleware. We can do this safely because, in the context of a browser, the +header can only be added by using ``XMLHttpRequest``, and browsers already +implement a same-domain policy for ``XMLHttpRequest``. +For the more recent browsers that relax this same-domain policy, custom headers +like "X-Requested-With" are only allowed after the browser has done a +'preflight' check to the server to see if the cross-domain request is allowed, +using a strictly 'opt in' mechanism, so the exception for AJAX is still safe—if +the developer has specifically opted in to allowing cross-site AJAX POST +requests on a specific URL, they obviously don't want the middleware to disallow +exactly that. .. _9.1.1 Safe Methods, HTTP 1.1, RFC 2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html +Caching +======= + +If the ``csrf_token`` template tag is used by a template (or the ``get_token`` +function is called some other way), ``CsrfViewMiddleware`` will add a cookie and +a ``Vary: Cookie`` header to the response. Similarly, +``CsrfResponseMiddleware`` will send the ``Vary: Cookie`` header if it inserted +a token. This means that these middleware will play well with the cache +middleware if it is used as instructed (``UpdateCacheMiddleware`` goes before +all other middleware). + +However, if you use cache decorators on individual views, the CSRF middleware +will not yet have been able to set the Vary header. In this case, on any views +that will require a CSRF token to be inserted you should use the +:func:`django.views.decorators.vary.vary_on_cookie` decorator first:: + + from django.views.decorators.cache import cache_page + from django.views.decorators.vary import vary_on_cookie + + @cache_page(60 * 15) + @vary_on_cookie + def my_view(request): + # ... + + +Testing +======= + +The ``CsrfViewMiddleware`` will usually be a big hindrance to testing view +functions, due to the need for the CSRF token which must be sent with every POST +request. For this reason, Django's HTTP client for tests has been modified to +set a flag on requests which relaxes the middleware and the ``csrf_protect`` +decorator so that they no longer rejects requests. In every other respect +(e.g. sending cookies etc.), they behave the same. + Limitations =========== -CsrfMiddleware requires Django's session framework to work. If you have -a custom authentication system that manually sets cookies and the like, -it won't help you. +Subdomains within a site will be able to set cookies on the client for the whole +domain. By setting the cookie and using a corresponding token, subdomains will +be able to circumvent the CSRF protection. The only way to avoid this is to +ensure that subdomains are controlled by trusted users (or, are at least unable +to set cookies). Note that even without CSRF, there are other vulnerabilities, +such as session fixation, that make giving subdomains to untrusted parties a bad +idea, and these vulnerabilities cannot easily be fixed with current browsers. + +If you are using ``CsrfResponseMiddleware`` and your app creates HTML pages and +forms in some unusual way, (e.g. it sends fragments of HTML in JavaScript +document.write statements) you might bypass the filter that adds the hidden +field to the form, in which case form submission will always fail. You should +use the template tag or :meth:`django.middleware.csrf.get_token` to get +the CSRF token and ensure it is included when your form is submitted. + +Contrib and reusable apps +========================= -If your app creates HTML pages and forms in some unusual way, (e.g. -it sends fragments of HTML in JavaScript document.write statements) -you might bypass the filter that adds the hidden field to the form, -in which case form submission will always fail. It may still be possible -to use the middleware, provided you can find some way to get the -CSRF token and ensure that is included when your form is submitted. +Because it is possible for the developer to turn off the ``CsrfViewMiddleware``, +all relevant views in contrib apps use the ``csrf_protect`` decorator to ensure +the security of these applications against CSRF. It is recommended that the +developers of other reusable apps that want the same guarantees also use the +``csrf_protect`` decorator on their views. diff --git a/docs/ref/contrib/formtools/form-wizard.txt b/docs/ref/contrib/formtools/form-wizard.txt index 98f0dbad42..11a2aed091 100644 --- a/docs/ref/contrib/formtools/form-wizard.txt +++ b/docs/ref/contrib/formtools/form-wizard.txt @@ -177,7 +177,7 @@ Here's a full example template: {% block content %} <p>Step {{ step }} of {{ step_count }}</p> - <form action="." method="post"> + <form action="." method="post">{% csrf_token %} <table> {{ form }} </table> diff --git a/docs/ref/middleware.txt b/docs/ref/middleware.txt index ff51df9e8f..b0b26cc227 100644 --- a/docs/ref/middleware.txt +++ b/docs/ref/middleware.txt @@ -165,11 +165,11 @@ every incoming ``HttpRequest`` object. See :ref:`Authentication in Web requests CSRF protection middleware -------------------------- -.. module:: django.contrib.csrf.middleware +.. module:: django.middleware.csrf :synopsis: Middleware adding protection against Cross Site Request Forgeries. -.. class:: django.contrib.csrf.middleware.CsrfMiddleware +.. class:: django.middleware.csrf.CsrfMiddleware .. versionadded:: 1.0 diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt index efd7c549b8..e685472cca 100644 --- a/docs/ref/models/querysets.txt +++ b/docs/ref/models/querysets.txt @@ -1114,6 +1114,17 @@ Aggregation <topics-db-aggregation>`. .. _field-lookups: +``exists()`` +~~~~~~~~~~~~ + +.. versionadded:: 1.2 + +Returns ``True`` if the :class:`QuerySet` contains any results, and ``False`` +if not. This tries to perform the query in the simplest and fastest way +possible, but it *does* execute nearly the same query. This means that calling +:meth:`QuerySet.exists()` is faster that ``bool(some_query_set)``, but not by +a large degree. + Field lookups ------------- diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index e8c673d995..6e78030e8c 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -144,6 +144,51 @@ Default: ``600`` The default number of seconds to cache a page when the caching middleware or ``cache_page()`` decorator is used. +.. setting:: CSRF_COOKIE_NAME + +CSRF_COOKIE_NAME +---------------- + +.. versionadded:: 1.2 + +Default: ``'csrftoken'`` + +The name of the cookie to use for the CSRF authentication token. This can be whatever you +want. See :ref:`ref-contrib-csrf`. + +.. setting:: CSRF_COOKIE_DOMAIN + +CSRF_COOKIE_DOMAIN +------------------ + +.. versionadded:: 1.2 + +Default: ``None`` + +The domain to be used when setting the CSRF cookie. This can be useful for +allowing cross-subdomain requests to be exluded from the normal cross site +request forgery protection. It should be set to a string such as +``".lawrence.com"`` to allow a POST request from a form on one subdomain to be +accepted by accepted by a view served from another subdomain. + +.. setting:: CSRF_FAILURE_VIEW + +CSRF_FAILURE_VIEW +----------------- + +.. versionadded:: 1.2 + +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="") + +where ``reason`` is a short message (intended for developers or logging, not for +end users) indicating the reason the request was rejected. See +:ref:`ref-contrib-csrf`. + .. setting:: DATABASE_ENGINE DATABASE_ENGINE @@ -379,6 +424,29 @@ are not allowed to visit any page, systemwide. Use this for bad robots/crawlers. This is only used if ``CommonMiddleware`` is installed (see :ref:`topics-http-middleware`). +.. setting:: EMAIL_BACKEND + +EMAIL_BACKEND +------------- + +.. versionadded:: 1.2 + +Default: ``'django.core.mail.backends.smtp'`` + +The backend to use for sending emails. For the list of available backends see +:ref:`topics-email`. + +.. setting:: EMAIL_FILE_PATH + +EMAIL_FILE_PATH +--------------- + +.. versionadded:: 1.2 + +Default: Not defined + +The directory used by the ``file`` email backend to store output files. + .. setting:: EMAIL_HOST EMAIL_HOST @@ -751,6 +819,7 @@ Default:: ('django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',) A tuple of middleware classes to use. See :ref:`topics-http-middleware`. diff --git a/docs/ref/templates/api.txt b/docs/ref/templates/api.txt index e3260a96f8..1b6eeb7014 100644 --- a/docs/ref/templates/api.txt +++ b/docs/ref/templates/api.txt @@ -313,6 +313,13 @@ and return a dictionary of items to be merged into the context. By default, "django.core.context_processors.i18n", "django.core.context_processors.media") +.. versionadded:: 1.2 + In addition to these, ``RequestContext`` always uses + ``'django.core.context_processors.csrf'``. This is a security + related context processor required by the admin and other contrib apps, and, + in case of accidental misconfiguration, it is deliberately hardcoded in and + cannot be turned off by the :setting:`TEMPLATE_CONTEXT_PROCESSORS` setting. + Each processor is applied in order. That means, if one processor adds a variable to the context and a second processor adds a variable with the same name, the second will override the first. The default processors are explained @@ -404,6 +411,14 @@ If :setting:`TEMPLATE_CONTEXT_PROCESSORS` contains this processor, every ``RequestContext`` will contain a variable ``MEDIA_URL``, providing the value of the :setting:`MEDIA_URL` setting. +django.core.context_processors.csrf +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 1.2 + +This processor adds a token that is needed by the ``csrf_token`` template tag +for protection against :ref:`Cross Site Request Forgeries <ref-contrib-csrf>`. + django.core.context_processors.request ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt index a2f8b9f8b3..8266224c39 100644 --- a/docs/ref/templates/builtins.txt +++ b/docs/ref/templates/builtins.txt @@ -53,6 +53,16 @@ Ignore everything between ``{% comment %}`` and ``{% endcomment %}`` .. templatetag:: cycle +csrf_token +~~~~~~~~~~ + +.. versionadded:: 1.1.2 + +In the Django 1.1.X series, this is a no-op tag that returns an empty string for +future compatibility purposes. In Django 1.2 and later, it is used for CSRF +protection, as described in the documentation for :ref:`Cross Site Request +Forgeries <ref-contrib-csrf>`. + cycle ~~~~~ diff --git a/docs/releases/1.2-alpha.txt b/docs/releases/1.2-alpha.txt new file mode 100644 index 0000000000..5a0815bf36 --- /dev/null +++ b/docs/releases/1.2-alpha.txt @@ -0,0 +1,52 @@ + +Backwards-incompatible changes +============================== + +CSRF Protection +--------------- + +There have been large changes to the way that CSRF protection works, detailed in +:ref:`the CSRF documentaton <ref-contrib-csrf>`. The following are the major +changes that developers must be aware of: + + * ``CsrfResponseMiddleware`` and ``CsrfMiddleware`` have been deprecated, and + will be removed completely in Django 1.4, in favour of a template tag that + should be inserted into forms. + + * All contrib apps use a ``csrf_protect`` decorator to protect the view. This + requires the use of the csrf_token template tag in the template, so if you + have used custom templates for contrib views, you MUST READ THE UPGRADE + INSTRUCTIONS to fix those templates. + + * ``CsrfViewMiddleware`` is included in :setting:`MIDDLEWARE_CLASSES` by + default. This turns on CSRF protection by default, so that views that accept + POST requests need to be written to work with the middleware. Instructions + on how to do this are found in the CSRF docs. + + * All of the CSRF has moved from contrib to core (with backwards compatible + imports in the old locations, which are deprecated). + +LazyObject +---------- + +``LazyObject`` is an undocumented utility class used for lazily wrapping other +objects of unknown type. In Django 1.1 and earlier, it handled introspection in +a non-standard way, depending on wrapped objects implementing a public method +``get_all_members()``. Since this could easily lead to name clashes, it has been +changed to use the standard method, involving ``__members__`` and ``__dir__()``. +If you used ``LazyObject`` in your own code, and implemented the +``get_all_members()`` method for wrapped objects, you need to make the following +changes: + + * If your class does not have special requirements for introspection (i.e. you + have not implemented ``__getattr__()`` or other methods that allow for + attributes not discoverable by normal mechanisms), you can simply remove the + ``get_all_members()`` method. The default implementation on ``LazyObject`` + will do the right thing. + + * If you have more complex requirements for introspection, first rename the + ``get_all_members()`` method to ``__dir__()``. This is the standard method, + from Python 2.6 onwards, for supporting introspection. If you are require + support for Python < 2.6, add the following code to the class:: + + __members__ = property(lambda self: self.__dir__()) diff --git a/docs/topics/auth.txt b/docs/topics/auth.txt index 6a62be8c32..33461a0858 100644 --- a/docs/topics/auth.txt +++ b/docs/topics/auth.txt @@ -262,8 +262,8 @@ Manager functions Creates, saves and returns a :class:`~django.contrib.auth.models.User`. The :attr:`~django.contrib.auth.models.User.username`, :attr:`~django.contrib.auth.models.User.email` and - :attr:`~django.contrib.auth.models.User.password` are set as given, and - the :class:`~django.contrib.auth.models.User` gets ``is_active=True``. + :attr:`~django.contrib.auth.models.User.password` are set as given, and the + :class:`~django.contrib.auth.models.User` gets ``is_active=True``. If no password is provided, :meth:`~django.contrib.auth.models.User.set_unusable_password()` will @@ -705,7 +705,7 @@ the following line to your URLconf:: (r'^accounts/login/$', 'django.contrib.auth.views.login'), -.. function:: views.login(request, [template_name, redirect_field_name]) +.. function:: views.login(request, [template_name, redirect_field_name, authentication_form]) Here's what ``django.contrib.auth.views.login`` does: @@ -767,7 +767,7 @@ the following line to your URLconf:: <p>Your username and password didn't match. Please try again.</p> {% endif %} - <form method="post" action="{% url django.contrib.auth.views.login %}"> + <form method="post" action="{% url django.contrib.auth.views.login %}">{% csrf_token %} <table> <tr> <td>{{ form.username.label_tag }}</td> @@ -785,6 +785,15 @@ the following line to your URLconf:: {% endblock %} + .. versionadded:: 1.2 + + If you are using alternate authentication (see + :ref:`authentication-backends`) you can pass a custom authentication form + to the login view via the ``authentication_form`` parameter. This form must + accept a ``request`` keyword argument in its ``__init__`` method, and + provide a ``get_user`` argument which returns the authenticated user object + (this method is only ever called after successful form validation). + .. _forms documentation: ../forms/ .. _site framework docs: ../sites/ @@ -824,7 +833,7 @@ includes a few other useful built-in views located in * ``login_url``: The URL of the login page to redirect to. This will default to :setting:`settings.LOGIN_URL <LOGIN_URL>` if not supplied. -.. function:: views.password_change(request[, template_name, post_change_redirect]) +.. function:: views.password_change(request[, template_name, post_change_redirect, password_change_form]) Allows a user to change their password. @@ -837,6 +846,13 @@ includes a few other useful built-in views located in * ``post_change_redirect``: The URL to redirect to after a successful password change. + * .. versionadded:: 1.2 + + ``password_change_form``: A custom "change password" form which must + accept a ``user`` keyword argument. The form is responsible for + actually changing the user's password. + + **Template context:** * ``form``: The password change form. @@ -1030,15 +1046,7 @@ checks to make sure the user is logged in and has the permission optional ``login_url`` argument, which lets you specify the URL for your login page (:setting:`settings.LOGIN_URL <LOGIN_URL>` by default). - Example in Python 2.3 syntax:: - - from django.contrib.auth.decorators import user_passes_test - - def my_view(request): - # ... - my_view = user_passes_test(lambda u: u.has_perm('polls.can_vote'), login_url='/login/')(my_view) - - Example in Python 2.4 syntax:: + For example:: from django.contrib.auth.decorators import user_passes_test diff --git a/docs/topics/cache.txt b/docs/topics/cache.txt index c9fd1b4012..31900cd49f 100644 --- a/docs/topics/cache.txt +++ b/docs/topics/cache.txt @@ -616,12 +616,6 @@ like so:: from django.views.decorators.vary import vary_on_headers - # Python 2.3 syntax. - def my_view(request): - # ... - my_view = vary_on_headers(my_view, 'User-Agent') - - # Python 2.4+ decorator syntax. @vary_on_headers('User-Agent') def my_view(request): # ... diff --git a/docs/topics/conditional-view-processing.txt b/docs/topics/conditional-view-processing.txt index 33f34b2406..53cb3205d8 100644 --- a/docs/topics/conditional-view-processing.txt +++ b/docs/topics/conditional-view-processing.txt @@ -95,13 +95,6 @@ for your front page view:: def front_page(request, blog_id): ... -Of course, if you're using Python 2.3 or prefer not to use the decorator -syntax, you can write the same code as follows, there is no difference:: - - def front_page(request, blog_id): - ... - front_page = condition(last_modified_func=latest_entry)(front_page) - Shortcuts for only computing one value ====================================== diff --git a/docs/topics/email.txt b/docs/topics/email.txt index c80a035b53..92e3c0263d 100644 --- a/docs/topics/email.txt +++ b/docs/topics/email.txt @@ -7,11 +7,13 @@ Sending e-mail .. module:: django.core.mail :synopsis: Helpers to easily send e-mail. -Although Python makes sending e-mail relatively easy via the `smtplib library`_, -Django provides a couple of light wrappers over it, to make sending e-mail -extra quick. +Although Python makes sending e-mail relatively easy via the `smtplib +library`_, Django provides a couple of light wrappers over it. These wrappers +are provided to make sending e-mail extra quick, to make it easy to test +email sending during development, and to provide support for platforms that +can't use SMTP. -The code lives in a single module: ``django.core.mail``. +The code lives in the ``django.core.mail`` module. .. _smtplib library: http://docs.python.org/library/smtplib.html @@ -25,11 +27,11 @@ In two lines:: send_mail('Subject here', 'Here is the message.', 'from@example.com', ['to@example.com'], fail_silently=False) -Mail is sent using the SMTP host and port specified in the :setting:`EMAIL_HOST` -and :setting:`EMAIL_PORT` settings. The :setting:`EMAIL_HOST_USER` and -:setting:`EMAIL_HOST_PASSWORD` settings, if set, are used to authenticate to the -SMTP server, and the :setting:`EMAIL_USE_TLS` setting controls whether a secure -connection is used. +Mail is sent using the SMTP host and port specified in the +:setting:`EMAIL_HOST` and :setting:`EMAIL_PORT` settings. The +:setting:`EMAIL_HOST_USER` and :setting:`EMAIL_HOST_PASSWORD` settings, if +set, are used to authenticate to the SMTP server, and the +:setting:`EMAIL_USE_TLS` setting controls whether a secure connection is used. .. note:: @@ -42,7 +44,7 @@ send_mail() The simplest way to send e-mail is using the function ``django.core.mail.send_mail()``. Here's its definition: - .. function:: send_mail(subject, message, from_email, recipient_list, fail_silently=False, auth_user=None, auth_password=None) + .. function:: send_mail(subject, message, from_email, recipient_list, fail_silently=False, auth_user=None, auth_password=None, connection=None) The ``subject``, ``message``, ``from_email`` and ``recipient_list`` parameters are required. @@ -62,6 +64,10 @@ are required. * ``auth_password``: The optional password to use to authenticate to the SMTP server. If this isn't provided, Django will use the value of the ``EMAIL_HOST_PASSWORD`` setting. + * ``connection``: The optional email backend to use to send the mail. + If unspecified, an instance of the default backend will be used. + See the documentation on :ref:`E-mail backends <topic-email-backends>` + for more details. .. _smtplib docs: http://docs.python.org/library/smtplib.html @@ -71,26 +77,29 @@ send_mass_mail() ``django.core.mail.send_mass_mail()`` is intended to handle mass e-mailing. Here's the definition: - .. function:: send_mass_mail(datatuple, fail_silently=False, auth_user=None, auth_password=None) + .. function:: send_mass_mail(datatuple, fail_silently=False, auth_user=None, auth_password=None, connection=None) ``datatuple`` is a tuple in which each element is in this format:: (subject, message, from_email, recipient_list) ``fail_silently``, ``auth_user`` and ``auth_password`` have the same functions -as in ``send_mail()``. +as in :meth:`~django.core.mail.send_mail()`. Each separate element of ``datatuple`` results in a separate e-mail message. -As in ``send_mail()``, recipients in the same ``recipient_list`` will all see -the other addresses in the e-mail messages' "To:" field. +As in :meth:`~django.core.mail.send_mail()`, recipients in the same +``recipient_list`` will all see the other addresses in the e-mail messages' +"To:" field. send_mass_mail() vs. send_mail() -------------------------------- -The main difference between ``send_mass_mail()`` and ``send_mail()`` is that -``send_mail()`` opens a connection to the mail server each time it's executed, -while ``send_mass_mail()`` uses a single connection for all of its messages. -This makes ``send_mass_mail()`` slightly more efficient. +The main difference between :meth:`~django.core.mail.send_mass_mail()` and +:meth:`~django.core.mail.send_mail()` is that +:meth:`~django.core.mail.send_mail()` opens a connection to the mail server +each time it's executed, while :meth:`~django.core.mail.send_mass_mail()` uses +a single connection for all of its messages. This makes +:meth:`~django.core.mail.send_mass_mail()` slightly more efficient. mail_admins() ============= @@ -98,7 +107,7 @@ mail_admins() ``django.core.mail.mail_admins()`` is a shortcut for sending an e-mail to the site admins, as defined in the :setting:`ADMINS` setting. Here's the definition: - .. function:: mail_admins(subject, message, fail_silently=False) + .. function:: mail_admins(subject, message, fail_silently=False, connection=None) ``mail_admins()`` prefixes the subject with the value of the :setting:`EMAIL_SUBJECT_PREFIX` setting, which is ``"[Django] "`` by default. @@ -115,7 +124,7 @@ mail_managers() function sends an e-mail to the site managers, as defined in the :setting:`MANAGERS` setting. Here's the definition: - .. function:: mail_managers(subject, message, fail_silently=False) + .. function:: mail_managers(subject, message, fail_silently=False, connection=None) Examples ======== @@ -145,7 +154,7 @@ scripts generate. The Django e-mail functions outlined above all protect against header injection by forbidding newlines in header values. If any ``subject``, ``from_email`` or ``recipient_list`` contains a newline (in either Unix, Windows or Mac style), -the e-mail function (e.g. ``send_mail()``) will raise +the e-mail function (e.g. :meth:`~django.core.mail.send_mail()`) will raise ``django.core.mail.BadHeaderError`` (a subclass of ``ValueError``) and, hence, will not send the e-mail. It's your responsibility to validate all data before passing it to the e-mail functions. @@ -178,41 +187,47 @@ from the request's POST data, sends that to admin@example.com and redirects to .. _emailmessage-and-smtpconnection: -The EmailMessage and SMTPConnection classes -=========================================== +The EmailMessage class +====================== .. versionadded:: 1.0 -Django's ``send_mail()`` and ``send_mass_mail()`` functions are actually thin -wrappers that make use of the ``EmailMessage`` and ``SMTPConnection`` classes -in ``django.core.mail``. If you ever need to customize the way Django sends -e-mail, you can subclass these two classes to suit your needs. +Django's :meth:`~django.core.mail.send_mail()` and +:meth:`~django.core.mail.send_mass_mail()` functions are actually thin +wrappers that make use of the :class:`~django.core.mail.EmailMessage` class. + +Not all features of the :class:`~django.core.mail.EmailMessage` class are +available through the :meth:`~django.core.mail.send_mail()` and related +wrapper functions. If you wish to use advanced features, such as BCC'ed +recipients, file attachments, or multi-part e-mail, you'll need to create +:class:`~django.core.mail.EmailMessage` instances directly. .. note:: - Not all features of the ``EmailMessage`` class are available through the - ``send_mail()`` and related wrapper functions. If you wish to use advanced - features, such as BCC'ed recipients, file attachments, or multi-part - e-mail, you'll need to create ``EmailMessage`` instances directly. + This is a design feature. :meth:`~django.core.mail.send_mail()` and + related functions were originally the only interface Django provided. + However, the list of parameters they accepted was slowly growing over + time. It made sense to move to a more object-oriented design for e-mail + messages and retain the original functions only for backwards + compatibility. - This is a design feature. ``send_mail()`` and related functions were - originally the only interface Django provided. However, the list of - parameters they accepted was slowly growing over time. It made sense to - move to a more object-oriented design for e-mail messages and retain the - original functions only for backwards compatibility. +:class:`~django.core.mail.EmailMessage` is responsible for creating the e-mail +message itself. The :ref:`e-mail backend <topic-email-backends>` is then +responsible for sending the e-mail. -In general, ``EmailMessage`` is responsible for creating the e-mail message -itself. ``SMTPConnection`` is responsible for the network connection side of -the operation. This means you can reuse the same connection (an -``SMTPConnection`` instance) for multiple messages. +For convenience, :class:`~django.core.mail.EmailMessage` provides a simple +``send()`` method for sending a single email. If you need to send multiple +messages, the email backend API :ref:`provides an alternative +<topics-sending-multiple-emails>`. EmailMessage Objects -------------------- .. class:: EmailMessage -The ``EmailMessage`` class is initialized with the following parameters (in -the given order, if positional arguments are used). All parameters are -optional and can be set at any time prior to calling the ``send()`` method. +The :class:`~django.core.mail.EmailMessage` class is initialized with the +following parameters (in the given order, if positional arguments are used). +All parameters are optional and can be set at any time prior to calling the +``send()`` method. * ``subject``: The subject line of the e-mail. @@ -227,7 +242,7 @@ optional and can be set at any time prior to calling the ``send()`` method. * ``bcc``: A list or tuple of addresses used in the "Bcc" header when sending the e-mail. - * ``connection``: An ``SMTPConnection`` instance. Use this parameter if + * ``connection``: An e-mail backend instance. Use this parameter if you want to use the same connection for multiple messages. If omitted, a new connection is created when ``send()`` is called. @@ -248,18 +263,18 @@ For example:: The class has the following methods: - * ``send(fail_silently=False)`` sends the message, using either - the connection that is specified in the ``connection`` - attribute, or creating a new connection if none already - exists. If the keyword argument ``fail_silently`` is ``True``, - exceptions raised while sending the message will be quashed. + * ``send(fail_silently=False)`` sends the message. If a connection was + specified when the email was constructed, that connection will be used. + Otherwise, an instance of the default backend will be instantiated and + used. If the keyword argument ``fail_silently`` is ``True``, exceptions + raised while sending the message will be quashed. * ``message()`` constructs a ``django.core.mail.SafeMIMEText`` object (a subclass of Python's ``email.MIMEText.MIMEText`` class) or a - ``django.core.mail.SafeMIMEMultipart`` object holding the - message to be sent. If you ever need to extend the ``EmailMessage`` class, - you'll probably want to override this method to put the content you want - into the MIME object. + ``django.core.mail.SafeMIMEMultipart`` object holding the message to be + sent. If you ever need to extend the + :class:`~django.core.mail.EmailMessage` class, you'll probably want to + override this method to put the content you want into the MIME object. * ``recipients()`` returns a list of all the recipients of the message, whether they're recorded in the ``to`` or ``bcc`` attributes. This is @@ -299,13 +314,13 @@ The class has the following methods: Sending alternative content types ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -It can be useful to include multiple versions of the content in an e-mail; -the classic example is to send both text and HTML versions of a message. With +It can be useful to include multiple versions of the content in an e-mail; the +classic example is to send both text and HTML versions of a message. With Django's e-mail library, you can do this using the ``EmailMultiAlternatives`` -class. This subclass of ``EmailMessage`` has an ``attach_alternative()`` method -for including extra versions of the message body in the e-mail. All the other -methods (including the class initialization) are inherited directly from -``EmailMessage``. +class. This subclass of :class:`~django.core.mail.EmailMessage` has an +``attach_alternative()`` method for including extra versions of the message +body in the e-mail. All the other methods (including the class initialization) +are inherited directly from :class:`~django.core.mail.EmailMessage`. To send a text and HTML combination, you could write:: @@ -318,41 +333,231 @@ To send a text and HTML combination, you could write:: msg.attach_alternative(html_content, "text/html") msg.send() -By default, the MIME type of the ``body`` parameter in an ``EmailMessage`` is -``"text/plain"``. It is good practice to leave this alone, because it -guarantees that any recipient will be able to read the e-mail, regardless of -their mail client. However, if you are confident that your recipients can -handle an alternative content type, you can use the ``content_subtype`` -attribute on the ``EmailMessage`` class to change the main content type. The -major type will always be ``"text"``, but you can change it to the subtype. For -example:: +By default, the MIME type of the ``body`` parameter in an +:class:`~django.core.mail.EmailMessage` is ``"text/plain"``. It is good +practice to leave this alone, because it guarantees that any recipient will be +able to read the e-mail, regardless of their mail client. However, if you are +confident that your recipients can handle an alternative content type, you can +use the ``content_subtype`` attribute on the +:class:`~django.core.mail.EmailMessage` class to change the main content type. +The major type will always be ``"text"``, but you can change it to the +subtype. For example:: msg = EmailMessage(subject, html_content, from_email, [to]) msg.content_subtype = "html" # Main content is now text/html msg.send() -SMTPConnection Objects ----------------------- +.. _topic-email-backends: -.. class:: SMTPConnection +E-Mail Backends +=============== + +.. versionadded:: 1.2 + +The actual sending of an e-mail is handled by the e-mail backend. + +The e-mail backend class has the following methods: + + * ``open()`` instantiates an long-lived email-sending connection. + + * ``close()`` closes the current email-sending connection. + + * ``send_messages(email_messages)`` sends a list of + :class:`~django.core.mail.EmailMessage` objects. If the connection is + not open, this call will implicitly open the connection, and close the + connection afterwards. If the connection is already open, it will be + left open after mail has been sent. + +Obtaining an instance of an e-mail backend +------------------------------------------ + +The :meth:`get_connection` function in ``django.core.mail`` returns an +instance of the e-mail backend that you can use. + +.. currentmodule:: django.core.mail + +.. function:: get_connection(backend=None, fail_silently=False, *args, **kwargs) + +By default, a call to ``get_connection()`` will return an instance of the +email backend specified in :setting:`EMAIL_BACKEND`. If you specify the +``backend`` argument, an instance of that backend will be instantiated. + +The ``fail_silently`` argument controls how the backend should handle errors. +If ``fail_silently`` is True, exceptions during the email sending process +will be silently ignored. + +All other arguments are passed directly to the constructor of the +e-mail backend. + +Django ships with several e-mail sending backends. With the exception of the +SMTP backend (which is the default), these backends are only useful during +testing and development. If you have special email sending requirements, you +can :ref:`write your own email backend <topic-custom-email-backend>`. + +.. _topic-email-smtp-backend: + +SMTP backend +~~~~~~~~~~~~ + +This is the default backend. E-mail will be sent through a SMTP server. +The server address and authentication credentials are set in the +:setting:`EMAIL_HOST`, :setting:`EMAIL_POST`, :setting:`EMAIL_HOST_USER`, +:setting:`EMAIL_HOST_PASSWORD` and :setting:`EMAIL_USE_TLS` settings in your +settings file. + +The SMTP backend is the default configuration inherited by Django. If you +want to specify it explicitly, put the following in your settings:: + + EMAIL_BACKEND = 'django.core.mail.backends.smtp' + +.. admonition:: SMTPConnection objects + + Prior to version 1.2, Django provided a + :class:`~django.core.mail.SMTPConnection` class. This class provided a way + to directly control the use of SMTP to send email. This class has been + deprecated in favor of the generic email backend API. + + For backwards compatibility :class:`~django.core.mail.SMTPConnection` is + still available in ``django.core.mail`` as an alias for the SMTP backend. + New code should use :meth:`~django.core.mail.get_connection` instead. + +Console backend +~~~~~~~~~~~~~~~ + +Instead of sending out real e-mails the console backend just writes the +e-mails that would be send to the standard output. By default, the console +backend writes to ``stdout``. You can use a different stream-like object by +providing the ``stream`` keyword argument when constructing the connection. + +To specify this backend, put the following in your settings:: + + EMAIL_BACKEND = 'django.core.mail.backends.console' + +This backend is not intended for use in production -- it is provided as a +convenience that can be used during development. + +File backend +~~~~~~~~~~~~ + +The file backend writes e-mails to a file. A new file is created for each new +session that is opened on this backend. The directory to which the files are +written is either taken from the :setting:`EMAIL_FILE_PATH` setting or from +the ``file_path`` keyword when creating a connection with +:meth:`~django.core.mail.get_connection`. + +To specify this backend, put the following in your settings:: + + EMAIL_BACKEND = 'django.core.mail.backends.filebased' + EMAIL_FILE_PATH = '/tmp/app-messages' # change this to a proper location + +This backend is not intended for use in production -- it is provided as a +convenience that can be used during development. + +In-memory backend +~~~~~~~~~~~~~~~~~ -The ``SMTPConnection`` class is initialized with the host, port, username and -password for the SMTP server. If you don't specify one or more of those -options, they are read from your settings file. +The ``'locmem'`` backend stores messages in a special attribute of the +``django.core.mail`` module. The ``outbox`` attribute is created when the +first message is send. It's a list with an +:class:`~django.core.mail.EmailMessage` instance for each message that would +be send. -If you're sending lots of messages at once, the ``send_messages()`` method of -the ``SMTPConnection`` class is useful. It takes a list of ``EmailMessage`` -instances (or subclasses) and sends them over a single connection. For example, -if you have a function called ``get_notification_email()`` that returns a -list of ``EmailMessage`` objects representing some periodic e-mail you wish to -send out, you could send this with:: +To specify this backend, put the following in your settings:: - connection = SMTPConnection() # Use default settings for connection + EMAIL_BACKEND = 'django.core.mail.backends.locmem' + +This backend is not intended for use in production -- it is provided as a +convenience that can be used during development and testing. + +Dummy backend +~~~~~~~~~~~~~ + +As the name suggests the dummy backend does nothing with your messages. To +specify this backend, put the following in your settings:: + + EMAIL_BACKEND = 'django.core.mail.backends.dummy' + +This backend is not intended for use in production -- it is provided as a +convenience that can be used during development. + +.. _topic-custom-email-backend: + +Defining a custom e-mail backend +-------------------------------- + +If you need to change how e-mails are send you can write your own e-mail +backend. The ``EMAIL_BACKEND`` setting in your settings file is then the +Python import path for your backend. + +Custom e-mail backends should subclass ``BaseEmailBackend`` that is located in +the ``django.core.mail.backends.base`` module. A custom e-mail backend must +implement the ``send_messages(email_messages)`` method. This method receives a +list of :class:`~django.core.mail.EmailMessage` instances and returns the +number of successfully delivered messages. If your backend has any concept of +a persistent session or connection, you should also implement the ``open()`` +and ``close()`` methods. Refer to ``SMTPEmailBackend`` for a reference +implementation. + +.. _topics-sending-multiple-emails: + +Sending multiple emails +----------------------- + +Establishing and closing an SMTP connection (or any other network connection, +for that matter) is an expensive process. If you have a lot of emails to send, +it makes sense to reuse an SMTP connection, rather than creating and +destroying a connection every time you want to send an email. + +There are two ways you tell an email backend to reuse a connection. + +Firstly, you can use the ``send_messages()`` method. ``send_messages()`` takes +a list of :class:`~django.core.mail.EmailMessage` instances (or subclasses), +and sends them all using a single connection. + +For example, if you have a function called ``get_notification_email()`` that +returns a list of :class:`~django.core.mail.EmailMessage` objects representing +some periodic e-mail you wish to send out, you could send these emails using +a single call to send_messages:: + + from django.core import mail + connection = mail.get_connection() # Use default email connection messages = get_notification_email() connection.send_messages(messages) +In this example, the call to ``send_messages()`` opens a connection on the +backend, sends the list of messages, and then closes the connection again. + +The second approach is to use the ``open()`` and ``close()`` methods on the +email backend to manually control the connection. ``send_messages()`` will not +manually open or close the connection if it is already open, so if you +manually open the connection, you can control when it is closed. For example:: + + from django.core import mail + connection = mail.get_connection() + + # Manually open the connection + connection.open() + + # Construct an email message that uses the connection + email1 = mail.EmailMessage('Hello', 'Body goes here', 'from@example.com', + ['to1@example.com'], connection=connection) + email1.send() # Send the email + + # Construct two more messages + email2 = mail.EmailMessage('Hello', 'Body goes here', 'from@example.com', + ['to2@example.com']) + email3 = mail.EmailMessage('Hello', 'Body goes here', 'from@example.com', + ['to3@example.com']) + + # Send the two emails in a single call - + connection.send_messages([email2, email3]) + # The connection was already open so send_messages() doesn't close it. + # We need to manually close the connection. + connection.close() + + Testing e-mail sending ----------------------- +====================== The are times when you do not want Django to send e-mails at all. For example, while developing a website, you probably don't want to send out thousands of @@ -360,19 +565,41 @@ e-mails -- but you may want to validate that e-mails will be sent to the right people under the right conditions, and that those e-mails will contain the correct content. -The easiest way to test your project's use of e-mail is to use a "dumb" e-mail -server that receives the e-mails locally and displays them to the terminal, -but does not actually send anything. Python has a built-in way to accomplish -this with a single command:: +The easiest way to test your project's use of e-mail is to use the ``console`` +email backend. This backend redirects all email to stdout, allowing you to +inspect the content of mail. + +The ``file`` email backend can also be useful during development -- this backend +dumps the contents of every SMTP connection to a file that can be inspected +at your leisure. + +Another approach is to use a "dumb" SMTP server that receives the e-mails +locally and displays them to the terminal, but does not actually send +anything. Python has a built-in way to accomplish this with a single command:: python -m smtpd -n -c DebuggingServer localhost:1025 This command will start a simple SMTP server listening on port 1025 of -localhost. This server simply prints to standard output all email headers and -the email body. You then only need to set the :setting:`EMAIL_HOST` and +localhost. This server simply prints to standard output all e-mail headers and +the e-mail body. You then only need to set the :setting:`EMAIL_HOST` and :setting:`EMAIL_PORT` accordingly, and you are set. -For more entailed testing and processing of e-mails locally, see the Python -documentation on the `SMTP Server`_. +For a more detailed discussion of testing and processing of e-mails locally, +see the Python documentation on the `SMTP Server`_. .. _SMTP Server: http://docs.python.org/library/smtpd.html + +SMTPConnection +============== + +.. class:: SMTPConnection + +.. deprecated:: 1.2 + +The ``SMTPConnection`` class has been deprecated in favor of the generic email +backend API. + +For backwards compatibility ``SMTPConnection`` is still available in +``django.core.mail`` as an alias for the :ref:`SMTP backend +<topic-email-smtp-backend>`. New code should use +:meth:`~django.core.mail.get_connection` instead. diff --git a/docs/topics/http/middleware.txt b/docs/topics/http/middleware.txt index 19facb8371..c64ec7dc79 100644 --- a/docs/topics/http/middleware.txt +++ b/docs/topics/http/middleware.txt @@ -29,6 +29,7 @@ created by :djadmin:`django-admin.py startproject <startproject>`:: MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', ) diff --git a/docs/topics/install.txt b/docs/topics/install.txt index b66c8aef15..0a64e3258d 100644 --- a/docs/topics/install.txt +++ b/docs/topics/install.txt @@ -11,7 +11,7 @@ Install Python Being a Python Web framework, Django requires Python. -It works with any Python version from 2.3 to 2.6 (due to backwards +It works with any Python version from 2.4 to 2.6 (due to backwards incompatibilities in Python 3.0, Django does not currently work with Python 3.0; see :ref:`the Django FAQ <faq-install>` for more information on supported Python versions and the 3.0 transition). @@ -93,10 +93,10 @@ database bindings are installed. will also want to read the database-specific notes for the :ref:`MySQL backend <ref-databases>`. -* If you're using SQLite and either Python 2.3 or Python 2.4, you'll need - pysqlite_. Use version 2.0.3 or higher. Python 2.5 ships with an SQLite - wrapper in the standard library, so you don't need to install anything extra - in that case. Please read the SQLite backend :ref:`notes<sqlite-notes>`. +* If you're using SQLite and Python 2.4, you'll need pysqlite_. Use version + 2.0.3 or higher. Python 2.5 ships with an SQLite wrapper in the standard + library, so you don't need to install anything extra in that case. Please + read the SQLite backend :ref:`notes<sqlite-notes>`. * If you're using Oracle, you'll need a copy of cx_Oracle_, but please read the database-specific notes for the diff --git a/docs/topics/testing.txt b/docs/topics/testing.txt index 25d2f083fd..6648461014 100644 --- a/docs/topics/testing.txt +++ b/docs/topics/testing.txt @@ -1104,6 +1104,8 @@ applications: ``target_status_code`` will be the url and status code for the final point of the redirect chain. +.. _topics-testing-email: + E-mail services --------------- @@ -1117,7 +1119,7 @@ test every aspect of sending e-mail -- from the number of messages sent to the contents of each message -- without actually sending the messages. The test runner accomplishes this by transparently replacing the normal -:class:`~django.core.mail.SMTPConnection` class with a different version. +email backend with a testing backend. (Don't worry -- this has no effect on any other e-mail senders outside of Django, such as your machine's mail server, if you're running one.) @@ -1128,14 +1130,8 @@ Django, such as your machine's mail server, if you're running one.) During test running, each outgoing e-mail is saved in ``django.core.mail.outbox``. This is a simple list of all :class:`~django.core.mail.EmailMessage` instances that have been sent. -It does not exist under normal execution conditions, i.e., when you're not -running unit tests. The outbox is created during test setup, along with the -dummy :class:`~django.core.mail.SMTPConnection`. When the test framework is -torn down, the standard :class:`~django.core.mail.SMTPConnection` class is -restored, and the test outbox is destroyed. - The ``outbox`` attribute is a special attribute that is created *only* when -the tests are run. It doesn't normally exist as part of the +the ``locmem`` e-mail backend is used. It doesn't normally exist as part of the :mod:`django.core.mail` module and you can't import it directly. The code below shows how to access this attribute correctly. |
