diff options
| author | Andrew Godwin <andrew@aeracode.org> | 2012-07-26 18:58:10 +0100 |
|---|---|---|
| committer | Andrew Godwin <andrew@aeracode.org> | 2012-07-26 18:58:10 +0100 |
| commit | 4a2e80fff44d0eb1856b593ac5f31ab1492b3e45 (patch) | |
| tree | 9bc87a682dc488e6555792c1d4bb53f5f3cdc880 /docs/topics | |
| parent | 959a3f9791d780062c4efe8765404a8ef95e87f0 (diff) | |
| parent | ab6cd1c839b136cbc94178da433b2e97ab7f6061 (diff) | |
Merge branch 'master' of github.com:django/django into schema-alteration
Conflicts:
django/db/backends/postgresql_psycopg2/base.py
Diffstat (limited to 'docs/topics')
| -rw-r--r-- | docs/topics/class-based-views/generic-display.txt | 10 | ||||
| -rw-r--r-- | docs/topics/class-based-views/index.txt | 4 | ||||
| -rw-r--r-- | docs/topics/db/models.txt | 3 | ||||
| -rw-r--r-- | docs/topics/db/transactions.txt | 7 | ||||
| -rw-r--r-- | docs/topics/email.txt | 2 | ||||
| -rw-r--r-- | docs/topics/files.txt | 23 | ||||
| -rw-r--r-- | docs/topics/forms/index.txt | 3 | ||||
| -rw-r--r-- | docs/topics/forms/modelforms.txt | 3 | ||||
| -rw-r--r-- | docs/topics/http/middleware.txt | 3 | ||||
| -rw-r--r-- | docs/topics/http/sessions.txt | 5 | ||||
| -rw-r--r-- | docs/topics/http/shortcuts.txt | 4 | ||||
| -rw-r--r-- | docs/topics/http/urls.txt | 4 | ||||
| -rw-r--r-- | docs/topics/i18n/timezones.txt | 2 | ||||
| -rw-r--r-- | docs/topics/i18n/translation.txt | 10 | ||||
| -rw-r--r-- | docs/topics/install.txt | 6 | ||||
| -rw-r--r-- | docs/topics/logging.txt | 4 | ||||
| -rw-r--r-- | docs/topics/python3.txt | 283 | ||||
| -rw-r--r-- | docs/topics/signals.txt | 15 | ||||
| -rw-r--r-- | docs/topics/testing.txt | 139 |
19 files changed, 264 insertions, 266 deletions
diff --git a/docs/topics/class-based-views/generic-display.txt b/docs/topics/class-based-views/generic-display.txt index 39fb41df04..0d4cb6244d 100644 --- a/docs/topics/class-based-views/generic-display.txt +++ b/docs/topics/class-based-views/generic-display.txt @@ -157,7 +157,7 @@ might look like the following:: That's really all there is to it. All the cool features of generic views come from changing the attributes set on the generic view. The :doc:`generic views reference</ref/class-based-views/index>` documents all the -generic views and their options in detail; the rest of this document will +generic views and their options in detail; the rest of this document will consider some of the common ways you might customize and extend generic views. @@ -220,7 +220,7 @@ more:: def get_context_data(self, **kwargs): # Call the base implementation first to get a context - context = super(PublisherDetailView, self).get_context_data(**kwargs) + context = super(PublisherDetail, self).get_context_data(**kwargs) # Add in a QuerySet of all the books context['book_list'] = Book.objects.all() return context @@ -284,7 +284,7 @@ technique:: from django.views.generic import ListView from books.models import Book - class AcmeBookListView(ListView): + class AcmeBookList(ListView): context_object_name = 'book_list' queryset = Book.objects.filter(publisher__name='Acme Publishing') @@ -361,7 +361,7 @@ use it in the template:: def get_context_data(self, **kwargs): # Call the base implementation first to get a context - context = super(PublisherBookListView, self).get_context_data(**kwargs) + context = super(PublisherBookList, self).get_context_data(**kwargs) # Add in the publisher context['publisher'] = self.publisher return context @@ -397,7 +397,7 @@ custom view:: urlpatterns = patterns('', #... - url(r'^authors/(?P<pk>\\d+)/$', AuthorDetailView.as_view(), name='author-detail'), + url(r'^authors/(?P<pk>\d+)/$', AuthorDetailView.as_view(), name='author-detail'), ) Then we'd write our new view -- ``get_object`` is the method that retrieves the diff --git a/docs/topics/class-based-views/index.txt b/docs/topics/class-based-views/index.txt index bdf649da48..6c2848944c 100644 --- a/docs/topics/class-based-views/index.txt +++ b/docs/topics/class-based-views/index.txt @@ -93,13 +93,13 @@ conversion to JSON once. For example, a simple JSON mixin might look something like this:: import json - from django import http + from django.http import HttpResponse class JSONResponseMixin(object): """ A mixin that can be used to render a JSON response. """ - reponse_class = HTTPResponse + response_class = HttpResponse def render_to_response(self, context, **response_kwargs): """ diff --git a/docs/topics/db/models.txt b/docs/topics/db/models.txt index 4010ff6f9c..ce15dc9535 100644 --- a/docs/topics/db/models.txt +++ b/docs/topics/db/models.txt @@ -384,7 +384,8 @@ Extra fields on many-to-many relationships ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When you're only dealing with simple many-to-many relationships such as -mixing and matching pizzas and toppings, a standard :class:`~django.db.models.ManyToManyField` is all you need. However, sometimes +mixing and matching pizzas and toppings, a standard +:class:`~django.db.models.ManyToManyField` is all you need. However, sometimes you may need to associate data with the relationship between two models. For example, consider the case of an application tracking the musical groups diff --git a/docs/topics/db/transactions.txt b/docs/topics/db/transactions.txt index 76b65b99e0..9928354664 100644 --- a/docs/topics/db/transactions.txt +++ b/docs/topics/db/transactions.txt @@ -54,6 +54,13 @@ The various cache middlewares are an exception: Even when using database caching, Django's cache backend uses its own database cursor (which is mapped to its own database connection internally). +.. note:: + + The ``TransactionMiddleware`` only affects the database aliased + as "default" within your :setting:`DATABASES` setting. If you are using + multiple databases and want transaction control over databases other than + "default", you will need to write your own transaction middleware. + .. _transaction-management-functions: Controlling transaction management in views diff --git a/docs/topics/email.txt b/docs/topics/email.txt index 3a78a83ce5..0cc476e02c 100644 --- a/docs/topics/email.txt +++ b/docs/topics/email.txt @@ -192,7 +192,7 @@ from the request's POST data, sends that to admin@example.com and redirects to # to get proper validation errors. return HttpResponse('Make sure all fields are entered and valid.') -.. _Header injection: http://www.nyphp.org/phundamentals/email_header_injection.php +.. _Header injection: http://www.nyphp.org/phundamentals/8_Preventing-Email-Header-Injection .. _emailmessage-and-smtpconnection: diff --git a/docs/topics/files.txt b/docs/topics/files.txt index 9ab8d5c496..c9b4327941 100644 --- a/docs/topics/files.txt +++ b/docs/topics/files.txt @@ -75,6 +75,29 @@ using a Python built-in ``file`` object:: Now you can use any of the documented attributes and methods of the :class:`~django.core.files.File` class. +Be aware that files created in this way are not automatically closed. +The following approach may be used to close files automatically:: + + >>> from django.core.files import File + + # Create a Python file object using open() and the with statement + >>> with open('/tmp/hello.world', 'w') as f: + >>> myfile = File(f) + >>> for line in myfile: + >>> print line + >>> myfile.closed + True + >>> f.closed + True + +Closing files is especially important when accessing file fields in a loop +over a large number of objects:: If files are not manually closed after +accessing them, the risk of running out of file descriptors may arise. This +may lead to the following error: + + IOError: [Errno 24] Too many open files + + File storage ============ diff --git a/docs/topics/forms/index.txt b/docs/topics/forms/index.txt index b843107871..4693de6c7e 100644 --- a/docs/topics/forms/index.txt +++ b/docs/topics/forms/index.txt @@ -91,6 +91,9 @@ The standard pattern for processing a form in a view looks like this: .. code-block:: python + from django.shortcuts import render + from django.http import HttpResponseRedirect + def contact(request): if request.method == 'POST': # If the form has been submitted... form = ContactForm(request.POST) # A form bound to the POST data diff --git a/docs/topics/forms/modelforms.txt b/docs/topics/forms/modelforms.txt index eb53b177c5..4cfde400a7 100644 --- a/docs/topics/forms/modelforms.txt +++ b/docs/topics/forms/modelforms.txt @@ -877,7 +877,8 @@ of a model. Here's how you can do that:: formset = BookInlineFormSet(request.POST, request.FILES, instance=author) if formset.is_valid(): formset.save() - # Do something. + # Do something. Should generally end with a redirect. For example: + return HttpResponseRedirect(author.get_absolute_url()) else: formset = BookInlineFormSet(instance=author) return render_to_response("manage_books.html", { diff --git a/docs/topics/http/middleware.txt b/docs/topics/http/middleware.txt index a768a3bbd8..fe92bc59a9 100644 --- a/docs/topics/http/middleware.txt +++ b/docs/topics/http/middleware.txt @@ -199,7 +199,8 @@ of caveats: define ``__init__`` as requiring any arguments. * Unlike the ``process_*`` methods which get called once per request, - ``__init__`` gets called only *once*, when the Web server starts up. + ``__init__`` gets called only *once*, when the Web server responds to the + first request. Marking middleware as unused ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/topics/http/sessions.txt b/docs/topics/http/sessions.txt index 20dc61a222..1f55293413 100644 --- a/docs/topics/http/sessions.txt +++ b/docs/topics/http/sessions.txt @@ -423,6 +423,9 @@ cookie will be sent on every request. Similarly, the ``expires`` part of a session cookie is updated each time the session cookie is sent. +.. versionchanged:: 1.5 + The session is not saved if the response's status code is 500. + Browser-length sessions vs. persistent sessions =============================================== @@ -521,7 +524,7 @@ consistently by all browsers. However, when it is honored, it can be a useful way to mitigate the risk of client side script accessing the protected cookie data. -.. _HTTPOnly: http://www.owasp.org/index.php/HTTPOnly +.. _HTTPOnly: https://www.owasp.org/index.php/HTTPOnly SESSION_COOKIE_NAME ------------------- diff --git a/docs/topics/http/shortcuts.txt b/docs/topics/http/shortcuts.txt index 2185d5497f..10be353e80 100644 --- a/docs/topics/http/shortcuts.txt +++ b/docs/topics/http/shortcuts.txt @@ -15,7 +15,7 @@ introduce controlled coupling for convenience's sake. ``render`` ========== -.. function:: render(request, template[, dictionary][, context_instance][, content_type][, status][, current_app]) +.. function:: render(request, template_name[, dictionary][, context_instance][, content_type][, status][, current_app]) .. versionadded:: 1.3 @@ -32,7 +32,7 @@ Required arguments ``request`` The request object used to generate this response. -``template`` +``template_name`` The full name of a template to use or sequence of template names. Optional arguments diff --git a/docs/topics/http/urls.txt b/docs/topics/http/urls.txt index 2310fac413..4e75dfe55f 100644 --- a/docs/topics/http/urls.txt +++ b/docs/topics/http/urls.txt @@ -704,8 +704,8 @@ target each pattern individually by using its name: .. code-block:: html+django - {% url arch-summary 1945 %} - {% url full-archive 2007 %} + {% url 'arch-summary' 1945 %} + {% url 'full-archive' 2007 %} Even though both URL patterns refer to the ``archive`` view here, using the ``name`` parameter to ``url()`` allows you to tell them apart in templates. diff --git a/docs/topics/i18n/timezones.txt b/docs/topics/i18n/timezones.txt index 1d9dd4b3c6..f3bb13ab03 100644 --- a/docs/topics/i18n/timezones.txt +++ b/docs/topics/i18n/timezones.txt @@ -509,7 +509,7 @@ Setup Finally, our calendar system contains interesting traps for computers:: >>> import datetime - >>> def substract_one_year(value): # DON'T DO THAT! + >>> def one_year_before(value): # DON'T DO THAT! ... return value.replace(year=value.year - 1) >>> one_year_before(datetime.datetime(2012, 3, 1, 10, 0)) datetime.datetime(2011, 3, 1, 10, 0) diff --git a/docs/topics/i18n/translation.txt b/docs/topics/i18n/translation.txt index 896ff87744..c912bf9575 100644 --- a/docs/topics/i18n/translation.txt +++ b/docs/topics/i18n/translation.txt @@ -596,7 +596,7 @@ apply. Reverse URL lookups cannot be carried out within the ``blocktrans`` and should be retrieved (and stored) beforehand:: - {% url path.to.view arg arg2 as the_url %} + {% url 'path.to.view' arg arg2 as the_url %} {% blocktrans %} This is a URL: {{ the_url }} {% endblocktrans %} @@ -790,7 +790,7 @@ To use the catalog, just pull in the dynamically generated script like this: .. code-block:: html+django - <script type="text/javascript" src="{% url django.views.i18n.javascript_catalog %}"></script> + <script type="text/javascript" src="{% url 'django.views.i18n.javascript_catalog' %}"></script> This uses reverse URL lookup to find the URL of the JavaScript catalog view. When the catalog is loaded, your JavaScript code can use the standard @@ -890,7 +890,7 @@ prepend the current active language code to all url patterns defined within urlpatterns += i18n_patterns('', url(r'^about/$', 'about.view', name='about'), - url(r'^news/$', include(news_patterns, namespace='news')), + url(r'^news/', include(news_patterns, namespace='news')), ) @@ -945,7 +945,7 @@ URL patterns can also be marked translatable using the urlpatterns += i18n_patterns('', url(_(r'^about/$'), 'about.view', name='about'), - url(_(r'^news/$'), include(news_patterns, namespace='news')), + url(_(r'^news/'), include(news_patterns, namespace='news')), ) @@ -992,7 +992,7 @@ template tag. It enables the given language in the enclosed template section: {% trans "View this category in:" %} {% for lang_code, lang_name in languages %} {% language lang_code %} - <a href="{% url category slug=category.slug %}">{{ lang_name }}</a> + <a href="{% url 'category' slug=category.slug %}">{{ lang_name }}</a> {% endlanguage %} {% endfor %} diff --git a/docs/topics/install.txt b/docs/topics/install.txt index 0cdb8a49e7..a11a44baa1 100644 --- a/docs/topics/install.txt +++ b/docs/topics/install.txt @@ -9,7 +9,7 @@ Install Python Being a Python Web framework, Django requires Python. -It works with any Python version from 2.6 to 2.7 (due to backwards +It works with any Python version from 2.6.5 to 2.7 (due to backwards incompatibilities in Python 3.0, Django does not currently work with Python 3.0; see :doc:`the Django FAQ </faq/install>` for more information on supported Python versions and the 3.0 transition). @@ -62,7 +62,7 @@ for information on how to configure mod_wsgi once you have it installed. If you can't use mod_wsgi for some reason, fear not: Django supports many other -deployment options. One is :doc:`uWSGI </howto/deployment/fastcgi>`; it works +deployment options. One is :doc:`uWSGI </howto/deployment/wsgi/uwsgi>`; it works very well with `nginx`_. Another is :doc:`FastCGI </howto/deployment/fastcgi>`, perfect for using Django with servers other than Apache. Additionally, Django follows the WSGI spec (:pep:`3333`), which allows it to run on a variety of @@ -70,7 +70,7 @@ server platforms. See the `server-arrangements wiki page`_ for specific installation instructions for each platform. .. _Apache: http://httpd.apache.org/ -.. _nginx: http://nginx.net/ +.. _nginx: http://nginx.org/ .. _mod_wsgi: http://code.google.com/p/modwsgi/ .. _server-arrangements wiki page: https://code.djangoproject.com/wiki/ServerArrangements diff --git a/docs/topics/logging.txt b/docs/topics/logging.txt index aa2afba760..a878d42266 100644 --- a/docs/topics/logging.txt +++ b/docs/topics/logging.txt @@ -209,8 +209,8 @@ By default, Django uses the `dictConfig format`_. ``logging.dictConfig`` is a builtin library in Python 2.7. In order to make this library available for users of earlier Python versions, Django includes a copy as part of ``django.utils.log``. - If you have Python 2.7, the system native library will be used; if - you have Python 2.6 or earlier, Django's copy will be used. + If you have Python 2.7 or later, the system native library will be used; if + you have Python 2.6, Django's copy will be used. In order to configure logging, you use :setting:`LOGGING` to define a dictionary of logging settings. These settings describes the loggers, diff --git a/docs/topics/python3.txt b/docs/topics/python3.txt index 974ddb0e88..3f799edac7 100644 --- a/docs/topics/python3.txt +++ b/docs/topics/python3.txt @@ -2,251 +2,136 @@ Python 3 compatibility ====================== -Django 1.5 introduces a compatibility layer that allows the code to be run both -in Python 2 (2.6/2.7) and Python 3 (>= 3.2) (*work in progress*). +Django 1.5 is the first version of Django to support Python 3. The same code +runs both on Python 2 (≥ 2.6.5) and Python 3 (≥ 3.2), thanks to the six_ +compatibility layer and ``unicode_literals``. -This document is not meant as a complete Python 2 to Python 3 migration guide. -There are many existing resources you can read. But we describe some utilities -and guidelines that we recommend you should use when you want to ensure your -code can be run with both Python 2 and 3. +.. _six: http://packages.python.org/six/ -* http://docs.python.org/py3k/howto/pyporting.html -* http://python3porting.com/ +This document is not meant as a Python 2 to Python 3 migration guide. There +are many existing resources, including `Python's official porting guide`_. +Rather, it describes guidelines that apply to Django's code and are +recommended for pluggable apps that run with both Python 2 and 3. -django.utils.py3 -================ +.. _Python's official porting guide: http://docs.python.org/py3k/howto/pyporting.html -Whenever a symbol or module has different semantics or different locations on -Python 2 and Python 3, you can import it from ``django.utils.py3`` where it -will be automatically converted depending on your current Python version. +Syntax requirements +=================== -PY3 ---- - -If you need to know anywhere in your code if you are running Python 3 or a -previous Python 2 version, you can check the ``PY3`` boolean variable:: - - from django.utils.py3 import PY3 +Unicode +------- - if PY3: - # Do stuff Python 3-wise - else: - # Do stuff Python 2-wise - -This should be considered as a last resort solution when it is not possible -to import a compatible name from django.utils.py3, as described in the sections -below. +In Python 3, all strings are considered Unicode by default. The ``unicode`` +type from Python 2 is called ``str`` in Python 3, and ``str`` becomes +``bytes``. -String handling -=============== +You mustn't use the ``u`` prefix before a unicode string literal because it's +a syntax error in Python 3.2. You must prefix byte strings with ``b``. -In Python 3, all strings are considered Unicode strings by default. Byte strings -have to be prefixed with the letter 'b'. To mimic the same behaviour in Python 2, -we recommend you import ``unicode_literals`` from the ``__future__`` library:: +In order to enable the same behavior in Python 2, every module must import +``unicode_literals`` from ``__future__``:: from __future__ import unicode_literals my_string = "This is an unicode literal" my_bytestring = b"This is a bytestring" -Be cautious if you have to slice bytestrings. -See http://docs.python.org/py3k/howto/pyporting.html#bytes-literals - -Different expected strings --------------------------- - -Some method parameters have changed the expected string type of a parameter. -For example, ``strftime`` format parameter expects a bytestring on Python 2 but -a normal (Unicode) string on Python 3. For these cases, ``django.utils.py3`` -provides a ``n()`` function which encodes the string parameter only with -Python 2. - - >>> from __future__ import unicode_literals - >>> from datetime import datetime - - >>> print(datetime.date(2012, 5, 21).strftime(n("%m → %Y"))) - 05 → 2012 - -Renamed types -============= - -Several types are named differently in Python 2 and Python 3. In order to keep -compatibility while using those types, import their corresponding aliases from -``django.utils.py3``. - -=========== ========= ===================== -Python 2 Python 3 django.utils.py3 -=========== ========= ===================== -basestring, str, string_types (tuple) -unicode str text_type -int, long int, integer_types (tuple) -long int long_type -=========== ========= ===================== - -String aliases --------------- - -Code sample:: +Be cautious if you have to `slice bytestrings`_. - if isinstance(foo, basestring): - print("foo is a string") +.. _slice bytestrings: http://docs.python.org/py3k/howto/pyporting.html#bytes-literals - # I want to convert a number to a Unicode string - bar = 45 - bar_string = unicode(bar) +Exceptions +---------- -Should be replaced by:: +When you capture exceptions, use the ``as`` keyword:: - from django.utils.py3 import string_types, text_type - - if isinstance(foo, string_types): - print("foo is a string") - - # I want to convert a number to a Unicode string - bar = 45 - bar_string = text_type(bar) - -No more long type ------------------ - -``long`` and ``int`` types have been unified in Python 3, meaning that ``long`` -is no longer available. ``django.utils.py3`` provides both ``long_type`` and -``integer_types`` aliases. For example: - -.. code-block:: python + try: + ... + except MyException as exc: + ... - # Old Python 2 code - my_var = long(333463247234623) - if isinstance(my_var, (int, long)): - # ... +This older syntax was removed in Python 3:: -Should be replaced by: + try: + ... + except MyException, exc: + ... -.. code-block:: python +The syntax to reraise an exception with a different traceback also changed. +Use :func:`six.reraise`. - from django.utils.py3 import long_type, integer_types - my_var = long_type(333463247234623) - if isinstance(my_var, integer_types): - # ... +.. module: django.utils.six +Writing compatible code with six +================================ -Changed module locations -======================== +six_ is the canonical compatibility library for supporting Python 2 and 3 in +a single codebase. Read its documentation! -The following modules have changed their location in Python 3. Therefore, it is -recommended to import them from the ``django.utils.py3`` compatibility layer: +:mod:`six` is bundled with Django: you can import it as :mod:`django.utils.six`. -=============================== ====================================== ====================== -Python 2 Python3 django.utils.py3 -=============================== ====================================== ====================== -Cookie http.cookies cookies +Here are the most common changes required to write compatible code. -urlparse.urlparse urllib.parse.urlparse urlparse -urlparse.urlunparse urllib.parse.urlunparse urlunparse -urlparse.urljoin urllib.parse.urljoin urljoin -urlparse.urlsplit urllib.parse.urlsplit urlsplit -urlparse.urlunsplit urllib.parse.urlunsplit urlunsplit -urlparse.urldefrag urllib.parse.urldefrag urldefrag -urlparse.parse_qsl urllib.parse.parse_qsl parse_qsl -urllib.quote urllib.parse.quote quote -urllib.unquote urllib.parse.unquote unquote -urllib.quote_plus urllib.parse.quote_plus quote_plus -urllib.unquote_plus urllib.parse.unquote_plus unquote_plus -urllib.urlencode urllib.parse.urlencode urlencode -urllib.urlopen urllib.request.urlopen urlopen -urllib.url2pathname urllib.request.url2pathname url2pathname -urllib.urlretrieve urllib.request.urlretrieve urlretrieve -urllib2 urllib.request urllib2 -urllib2.Request urllib.request.Request Request -urllib2.OpenerDirector urllib.request.OpenerDirector OpenerDirector -urllib2.UnknownHandler urllib.request.UnknownHandler UnknownHandler -urllib2.HTTPHandler urllib.request.HTTPHandler HTTPHandler -urllib2.HTTPSHandler urllib.request.HTTPSHandler HTTPSHandler -urllib2.HTTPDefaultErrorHandler urllib.request.HTTPDefaultErrorHandler HTTPDefaultErrorHandler -urllib2.FTPHandler urllib.request.FTPHandler FTPHandler -urllib2.HTTPError urllib.request.HTTPError HTTPError -urllib2.HTTPErrorProcessor urllib.request.HTTPErrorProcessor HTTPErrorProcessor +String types +------------ -htmlentitydefs.name2codepoint html.entities.name2codepoint name2codepoint -HTMLParser html.parser HTMLParser -cPickle/pickle pickle pickle -thread/dummy_thread _thread/_dummy_thread thread +The ``basestring`` and ``unicode`` types were removed in Python 3, and the +meaning of ``str`` changed. To test these types, use the following idioms:: -os.getcwdu os.getcwd getcwdu -itertools.izip zip zip -sys.maxint sys.maxsize maxsize -unichr chr unichr -xrange range xrange -=============================== ====================================== ====================== + isinstance(myvalue, six.string_types) # replacement for basestring + isinstance(myvalue, six.text_type) # replacement for unicode + isinstance(myvalue, bytes) # replacement for str +Python ≥ 2.6 provides ``bytes`` as an alias for ``str``, so you don't need +:attr:`six.binary_type`. -Ouptut encoding now Unicode -=========================== +``long`` +-------- -If you want to catch stdout/stderr output, the output content is UTF-8 encoded -in Python 2, while it is Unicode strings in Python 3. You can use the OutputIO -stream to capture this output:: +The ``long`` type no longer exists in Python 3. ``1L`` is a syntax error. Use +:data:`six.integer_types` check if a value is an integer or a long:: - from django.utils.py3 import OutputIO + isinstance(myvalue, six.integer_types) # replacement for (int, long) - try: - old_stdout = sys.stdout - out = OutputIO() - sys.stdout = out - # Do stuff which produces standard output - result = out.getvalue() - finally: - sys.stdout = old_stdout +``xrange`` +---------- -Dict iteritems/itervalues/iterkeys -================================== +Import :func:`six.moves.xrange` wherever you use ``xrange``. -The iteritems(), itervalues() and iterkeys() methods of dictionaries do not -exist any more in Python 3, simply because they represent the default items() -values() and keys() behavior in Python 3. Therefore, to keep compatibility, -use similar functions from ``django.utils.py3``:: +Moved modules +------------- - from django.utils.py3 import iteritems, itervalues, iterkeys +Some modules were renamed in Python 3. The :mod:`django.utils.six.moves +<six.moves>` module provides a compatible location to import them. - my_dict = {'a': 21, 'b': 42} - for key, value in iteritems(my_dict): - # ... - for value in itervalues(my_dict): - # ... - for key in iterkeys(my_dict): - # ... +In addition to six' defaults, Django's version provides ``dummy_thread`` as +``_dummy_thread``. -Note that in Python 3, dict.keys(), dict.items() and dict.values() return -"views" instead of lists. Wrap them into list() if you really need their return -values to be in a list. - -http://docs.python.org/release/3.0.1/whatsnew/3.0.html#views-and-iterators-instead-of-lists +PY3 +--- -Metaclass -========= +If you need different code in Python 2 and Python 3, check :data:`six.PY3`:: -The syntax for declaring metaclasses has changed in Python 3. -``django.utils.py3`` offers a compatible way to declare metaclasses:: + if six.PY3: + # do stuff Python 3-wise + else: + # do stuff Python 2-wise - from django.utils.py3 import with_metaclass +This is a last resort solution when :mod:`six` doesn't provide an appropriate +function. - class MyClass(with_metaclass(SubClass1, SubClass2,...)): - # ... +.. module:: django.utils.six -Re-raising exceptions +Customizations of six ===================== -One of the syntaxes to raise exceptions (raise E, V, T) is gone in Python 3. -This is especially used in very specific cases where you want to re-raise a -different exception that the initial one, while keeping the original traceback. -So, instead of:: - - raise Exception, Exception(msg), traceback - -Use:: - - from django.utils.py3 import reraise +The version of six bundled with Django includes a few additional tools: - reraise(Exception, Exception(msg), traceback) +.. 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. diff --git a/docs/topics/signals.txt b/docs/topics/signals.txt index 3ef68316a9..db1bcb03df 100644 --- a/docs/topics/signals.txt +++ b/docs/topics/signals.txt @@ -52,10 +52,10 @@ called when the signal is sent by using the :meth:`.Signal.connect` method: .. method:: Signal.connect(receiver, [sender=None, weak=True, dispatch_uid=None]) - + :param receiver: The callback function which will be connected to this signal. See :ref:`receiver-functions` for more information. - + :param sender: Specifies a particular sender to receive signals from. See :ref:`connecting-to-specific-signals` for more information. @@ -129,10 +129,17 @@ receiver: Now, our ``my_callback`` function will be called each time a request finishes. +Note that ``receiver`` can also take a list of signals to connect a function +to. + .. versionadded:: 1.3 The ``receiver`` decorator was added in Django 1.3. +.. versionchanged:: 1.5 + +The ability to pass a list of signals was added. + .. admonition:: Where should this code live? You can put signal handling and registration code anywhere you like. @@ -182,7 +189,7 @@ Preventing duplicate signals In some circumstances, the module in which you are connecting signals may be imported multiple times. This can cause your receiver function to be registered more than once, and thus called multiples times for a single signal -event. +event. If this behavior is problematic (such as when using signals to send an email whenever a model is saved), pass a unique identifier as @@ -228,7 +235,7 @@ Remember that you're allowed to change this list of arguments at any time, so ge Sending signals --------------- -There are two ways to send send signals in Django. +There are two ways to send signals in Django. .. method:: Signal.send(sender, **kwargs) .. method:: Signal.send_robust(sender, **kwargs) diff --git a/docs/topics/testing.txt b/docs/topics/testing.txt index f7aadd68f3..1f4c970d3e 100644 --- a/docs/topics/testing.txt +++ b/docs/topics/testing.txt @@ -478,6 +478,32 @@ If there are any circular dependencies in the :setting:`TEST_DEPENDENCIES` definition, an ``ImproperlyConfigured`` exception will be raised. +Order in which tests are executed +--------------------------------- + +In order to guarantee that all ``TestCase`` code starts with a clean database, +the Django test runner reorders tests in the following way: + +* First, all unittests (including :class:`unittest.TestCase`, + :class:`~django.test.SimpleTestCase`, :class:`~django.test.TestCase` and + :class:`~django.test.TransactionTestCase`) are run with no particular ordering + guaranteed nor enforced among them. + +* Then any other tests (e.g. doctests) that may alter the database without + restoring it to its original state are run. + +.. versionchanged:: 1.5 + Before Django 1.5, the only guarantee was that + :class:`~django.test.TestCase` tests were always ran first, before any other + tests. + +.. note:: + + The new ordering of tests may reveal unexpected dependencies on test case + ordering. This is the case with doctests that relied on state left in the + database by a given :class:`~django.test.TransactionTestCase` test, they + must be updated to be able to run independently. + Other test conditions --------------------- @@ -1109,8 +1135,11 @@ The following is a simple unit test using the request factory:: response = my_view(request) self.assertEqual(response.status_code, 200) -TestCase --------- +Test cases +---------- + +Provided test case classes +~~~~~~~~~~~~~~~~~~~~~~~~~~ .. currentmodule:: django.test @@ -1124,16 +1153,19 @@ Normal Python unit test classes extend a base class of Hierarchy of Django unit testing classes +TestCase +^^^^^^^^ + .. class:: TestCase() This class provides some additional capabilities that can be useful for testing Web sites. Converting a normal :class:`unittest.TestCase` to a Django :class:`TestCase` is -easy: just change the base class of your test from :class:`unittest.TestCase` to -:class:`django.test.TestCase`. All of the standard Python unit test -functionality will continue to be available, but it will be augmented with some -useful additions, including: +easy: Just change the base class of your test from `'unittest.TestCase'` to +`'django.test.TestCase'`. All of the standard Python unit test functionality +will continue to be available, but it will be augmented with some useful +additions, including: * Automatic loading of fixtures. @@ -1141,11 +1173,18 @@ useful additions, including: * Creates a TestClient instance. -* Django-specific assertions for testing for things - like redirection and form errors. +* Django-specific assertions for testing for things like redirection and form + errors. + +.. versionchanged:: 1.5 + The order in which tests are run has changed. See `Order in which tests are + executed`_. ``TestCase`` inherits from :class:`~django.test.TransactionTestCase`. +TransactionTestCase +^^^^^^^^^^^^^^^^^^^ + .. class:: TransactionTestCase() Django ``TestCase`` classes make use of database transaction facilities, if @@ -1157,38 +1196,66 @@ behavior, you should use a Django ``TransactionTestCase``. ``TransactionTestCase`` and ``TestCase`` are identical except for the manner in which the database is reset to a known state and the ability for test code -to test the effects of commit and rollback. A ``TransactionTestCase`` resets -the database before the test runs by truncating all tables and reloading -initial data. A ``TransactionTestCase`` may call commit and rollback and -observe the effects of these calls on the database. +to test the effects of commit and rollback: -A ``TestCase``, on the other hand, does not truncate tables and reload initial -data at the beginning of a test. Instead, it encloses the test code in a -database transaction that is rolled back at the end of the test. It also -prevents the code under test from issuing any commit or rollback operations -on the database, to ensure that the rollback at the end of the test restores -the database to its initial state. In order to guarantee that all ``TestCase`` -code starts with a clean database, the Django test runner runs all ``TestCase`` -tests first, before any other tests (e.g. doctests) that may alter the -database without restoring it to its original state. +* A ``TransactionTestCase`` resets the database after the test runs by + truncating all tables. A ``TransactionTestCase`` may call commit and rollback + and observe the effects of these calls on the database. -When running on a database that does not support rollback (e.g. MySQL with the -MyISAM storage engine), ``TestCase`` falls back to initializing the database -by truncating tables and reloading initial data. +* A ``TestCase``, on the other hand, does not truncate tables after a test. + Instead, it encloses the test code in a database transaction that is rolled + back at the end of the test. It also prevents the code under test from + issuing any commit or rollback operations on the database, to ensure that the + rollback at the end of the test restores the database to its initial state. -``TransactionTestCase`` inherits from :class:`~django.test.SimpleTestCase`. + When running on a database that does not support rollback (e.g. MySQL with the + MyISAM storage engine), ``TestCase`` falls back to initializing the database + by truncating tables and reloading initial data. .. note:: - The ``TestCase`` use of rollback to un-do the effects of the test code - may reveal previously-undetected errors in test code. For example, - test code that assumes primary keys values will be assigned starting at - one may find that assumption no longer holds true when rollbacks instead - of table truncation are being used to reset the database. Similarly, - the reordering of tests so that all ``TestCase`` classes run first may - reveal unexpected dependencies on test case ordering. In such cases a - quick fix is to switch the ``TestCase`` to a ``TransactionTestCase``. - A better long-term fix, that allows the test to take advantage of the - speed benefit of ``TestCase``, is to fix the underlying test problem. + + .. versionchanged:: 1.5 + + Prior to 1.5, ``TransactionTestCase`` flushed the database tables *before* + each test. In Django 1.5, this is instead done *after* the test has been run. + + When the flush took place before the test, it was guaranteed that primary + key values started at one in :class:`~django.test.TransactionTestCase` + tests. + + Tests should not depend on this behaviour, but for legacy tests that do, the + :attr:`~TransactionTestCase.reset_sequences` attribute can be used until + the test has been properly updated. + +.. versionchanged:: 1.5 + The order in which tests are run has changed. See `Order in which tests are + executed`_. + +``TransactionTestCase`` inherits from :class:`~django.test.SimpleTestCase`. + +.. attribute:: TransactionTestCase.reset_sequences + + .. versionadded:: 1.5 + + Setting ``reset_sequences = True`` on a ``TransactionTestCase`` will make + sure sequences are always reset before the test run:: + + class TestsThatDependsOnPrimaryKeySequences(TransactionTestCase): + reset_sequences = True + + def test_animal_pk(self): + lion = Animal.objects.create(name="lion", sound="roar") + # lion.pk is guaranteed to always be 1 + self.assertEqual(lion.pk, 1) + + Unless you are explicitly testing primary keys sequence numbers, it is + recommended that you do not hard code primary key values in tests. + + Using ``reset_sequences = True`` will slow down the test, since the primary + key reset is an relatively expensive database operation. + +SimpleTestCase +^^^^^^^^^^^^^^ .. class:: SimpleTestCase() @@ -1940,7 +2007,7 @@ out the `full reference`_ for more details. .. _Selenium: http://seleniumhq.org/ .. _selenium package: http://pypi.python.org/pypi/selenium -.. _full reference: http://readthedocs.org/docs/selenium-python/en/latest/api.html +.. _full reference: http://selenium-python.readthedocs.org/en/latest/api.html .. _Firefox: http://www.mozilla.com/firefox/ .. note:: |
