diff options
| author | Andrew Godwin <andrew@aeracode.org> | 2013-06-28 17:32:57 +0100 |
|---|---|---|
| committer | Andrew Godwin <andrew@aeracode.org> | 2013-06-28 17:32:57 +0100 |
| commit | 7a47ba6f6aeca36b8b092dbafd36abb342d34d4b (patch) | |
| tree | e959922f7d4d08057225459e31697f410e26df26 /docs | |
| parent | e2d7e83256234251a81ad3388428f6579795a672 (diff) | |
| parent | 48dd1e63bbb93479666208535a56f8c7c4aeab3a (diff) | |
Merge remote-tracking branch 'core/master' into schema-alteration
Conflicts:
django/db/backends/__init__.py
django/db/models/fields/related.py
tests/field_deconstruction/tests.py
Diffstat (limited to 'docs')
30 files changed, 376 insertions, 159 deletions
diff --git a/docs/conf.py b/docs/conf.py index feff99b6f4..0c35e935e6 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -55,7 +55,7 @@ copyright = 'Django Software Foundation and contributors' # built documents. # # The short X.Y version. -version = '1.6' +version = '1.7' # The full version, including alpha/beta/rc tags. try: from django import VERSION, get_version @@ -71,7 +71,7 @@ else: release = django_release() # The "development version" of Django -django_next_version = '1.6' +django_next_version = '1.7' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/faq/install.txt b/docs/faq/install.txt index 5a4cab94cf..d221f93d02 100644 --- a/docs/faq/install.txt +++ b/docs/faq/install.txt @@ -16,9 +16,8 @@ How do I get started? What are Django's prerequisites? -------------------------------- -Django requires Python, specifically Python 2.6.5 - 2.7.x. No other Python -libraries are required for basic Django usage. Django 1.5 also has -experimental support for Python 3.2.3 and above. +Django requires Python, specifically Python 2.6.5 - 2.7.x, or 3.2.3 and above. +No other Python libraries are required for basic Django usage. For a development environment -- if you just want to experiment with Django -- you don't need to have a separate Web server installed; Django comes with its @@ -43,7 +42,7 @@ Do I lose anything by using Python 2.6 versus newer Python versions, such as Pyt ---------------------------------------------------------------------------------------- Not in the core framework. Currently, Django itself officially supports -Python 2.6 (2.6.5 or higher) and 2.7. However, newer versions of +Python 2.6 (2.6.5 or higher), 2.7, 3.2.3 or higher. However, newer versions of Python are often faster, have more features, and are better supported. If you use a newer version of Python you will also have access to some APIs that aren't available under older versions of Python. @@ -51,12 +50,9 @@ aren't available under older versions of Python. Third-party applications for use with Django are, of course, free to set their own version requirements. -All else being equal, we recommend that you use the latest 2.x release -(currently Python 2.7). This will let you take advantage of the numerous -improvements and optimizations to the Python language since version 2.6. - -Generally speaking, we don't recommend running Django on Python 3 yet; see -below for more. +All else being equal, we recommend that you use the latest 2.7 or 3.x release. +This will let you take advantage of the numerous improvements and optimizations +to the Python language since version 2.6. What Python version can I use with Django? ------------------------------------------ @@ -77,15 +73,12 @@ Django version Python versions Can I use Django with Python 3? ------------------------------- -Django 1.5 introduces experimental support for Python 3.2.3 and above. However, -we don't yet suggest that you use Django and Python 3 in production. +Yes, you can! -Python 3 support should be considered a "preview". It's offered to bootstrap -the transition of the Django ecosystem to Python 3, and to help you start -porting your apps for future Python 3 compatibility. But we're not yet -confident enough to promise stability in production. +Django 1.5 introduced experimental support for Python 3.2.3 and above. -Our current plan is to make Django 1.6 suitable for general use with Python 3. +As of Django 1.6, Python 3 support is considered stable and you can safely use +it in production. See also :doc:`/topics/python3`. Will Django run under shared hosting (like TextDrive or Dreamhost)? ------------------------------------------------------------------- diff --git a/docs/howto/custom-model-fields.txt b/docs/howto/custom-model-fields.txt index 8993872cff..54913a887a 100644 --- a/docs/howto/custom-model-fields.txt +++ b/docs/howto/custom-model-fields.txt @@ -31,7 +31,7 @@ Our example object Creating custom fields requires a bit of attention to detail. To make things easier to follow, we'll use a consistent example throughout this document: wrapping a Python object representing the deal of cards in a hand of Bridge_. -Don't worry, you don't have know how to play Bridge to follow this example. +Don't worry, you don't have to know how to play Bridge to follow this example. You only need to know that 52 cards are dealt out equally to four players, who are traditionally called *north*, *east*, *south* and *west*. Our class looks something like this:: diff --git a/docs/howto/deployment/wsgi/modwsgi.txt b/docs/howto/deployment/wsgi/modwsgi.txt index 7749192358..2cbcd8ce7e 100644 --- a/docs/howto/deployment/wsgi/modwsgi.txt +++ b/docs/howto/deployment/wsgi/modwsgi.txt @@ -25,7 +25,8 @@ Basic configuration =================== Once you've got mod_wsgi installed and activated, edit your Apache server's -``httpd.conf`` file and add +``httpd.conf`` file and add the following. If you are using a version of Apache +older than 2.4, replace ``Require all granted`` with ``Allow from all``. .. code-block:: apache @@ -35,7 +36,7 @@ Once you've got mod_wsgi installed and activated, edit your Apache server's <Directory /path/to/mysite.com/mysite> <Files wsgi.py> Order deny,allow - Allow from all + Require all granted </Files> </Directory> diff --git a/docs/howto/static-files/index.txt b/docs/howto/static-files/index.txt index 3668c5dc41..db8bd38e9c 100644 --- a/docs/howto/static-files/index.txt +++ b/docs/howto/static-files/index.txt @@ -68,7 +68,7 @@ details on how ``staticfiles`` finds your files. Now we *might* be able to get away with putting our static files directly in ``my_app/static/`` (rather than creating another ``my_app`` subdirectory), but it would actually be a bad idea. Django will use the - last static file it finds whose name matches, and if you had a static file + first static file it finds whose name matches, and if you had a static file with the same name in a *different* application, Django would be unable to distinguish between them. We need to be able to point Django at the right one, and the easiest way to ensure this is by *namespacing* them. That is, diff --git a/docs/internals/contributing/triaging-tickets.txt b/docs/internals/contributing/triaging-tickets.txt index 43b799ed51..7bb59bc329 100644 --- a/docs/internals/contributing/triaging-tickets.txt +++ b/docs/internals/contributing/triaging-tickets.txt @@ -255,7 +255,11 @@ Keywords ~~~~~~~~ With this field you may label a ticket with multiple keywords. This can be -useful, for example, to group several tickets of a same theme. +useful, for example, to group several tickets of a same theme. Keywords can +either be comma or space separated. Keyword search finds the keyword string +anywhere in the keywords. For example, clicking on a ticket with the keyword +"form" will yield similar tickets tagged with keywords containing strings such +as "formset", "modelformset", and "ManagementForm". .. _closing-tickets: diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index 45f82b49e6..9672746717 100644 --- a/docs/internals/deprecation.txt +++ b/docs/internals/deprecation.txt @@ -326,6 +326,14 @@ these changes. remove calls to this method, and instead ensure that their auth related views are CSRF protected, which ensures that cookies are enabled. +* The version of :func:`django.contrib.auth.views.password_reset_confirm` that + supports base36 encoded user IDs + (``django.contrib.auth.views.password_reset_confirm_uidb36``) will be + removed. If your site has been running Django 1.6 for more than + :setting:`PASSWORD_RESET_TIMEOUT_DAYS`, this change will have no effect. If + not, then any password reset links generated before you upgrade to Django 1.7 + won't work after the upgrade. + 1.8 --- diff --git a/docs/intro/reusable-apps.txt b/docs/intro/reusable-apps.txt index 4247b45238..879cda913a 100644 --- a/docs/intro/reusable-apps.txt +++ b/docs/intro/reusable-apps.txt @@ -67,7 +67,7 @@ After the previous tutorials, our project should look like this:: admin.py models.py static/ - polls + polls/ images/ background.gif style.css diff --git a/docs/intro/tutorial03.txt b/docs/intro/tutorial03.txt index 6193ec45f7..91409848cf 100644 --- a/docs/intro/tutorial03.txt +++ b/docs/intro/tutorial03.txt @@ -339,14 +339,14 @@ Put the following code in that template: Now let's update our ``index`` view in ``polls/views.py`` to use the template:: from django.http import HttpResponse - from django.template import Context, loader + from django.template import RequestContext, loader from polls.models import Poll def index(request): latest_poll_list = Poll.objects.order_by('-pub_date')[:5] template = loader.get_template('polls/index.html') - context = Context({ + context = RequestContext(request, { 'latest_poll_list': latest_poll_list, }) return HttpResponse(template.render(context)) @@ -377,7 +377,7 @@ rewritten:: return render(request, 'polls/index.html', context) Note that once we've done this in all these views, we no longer need to import -:mod:`~django.template.loader`, :class:`~django.template.Context` and +:mod:`~django.template.loader`, :class:`~django.template.RequestContext` and :class:`~django.http.HttpResponse` (you'll want to keep ``HttpResponse`` if you still have the stub methods for ``detail``, ``results``, and ``vote``). diff --git a/docs/ref/class-based-views/flattened-index.txt b/docs/ref/class-based-views/flattened-index.txt index df00f87aa0..272c852181 100644 --- a/docs/ref/class-based-views/flattened-index.txt +++ b/docs/ref/class-based-views/flattened-index.txt @@ -151,6 +151,7 @@ FormView * :attr:`~django.views.generic.edit.FormMixin.form_class` [:meth:`~django.views.generic.edit.FormMixin.get_form_class`] * :attr:`~django.views.generic.base.View.http_method_names` * :attr:`~django.views.generic.edit.FormMixin.initial` [:meth:`~django.views.generic.edit.FormMixin.get_initial`] +* :attr:`~django.views.generic.edit.FormMixin.prefix` [:meth:`~django.views.generic.edit.FormMixin.get_prefix`] * :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`] * :attr:`~django.views.generic.edit.FormMixin.success_url` [:meth:`~django.views.generic.edit.FormMixin.get_success_url`] * :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` [:meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`] @@ -177,11 +178,13 @@ CreateView * :attr:`~django.views.generic.base.TemplateResponseMixin.content_type` * :attr:`~django.views.generic.detail.SingleObjectMixin.context_object_name` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_context_object_name`] +* :attr:`~django.views.generic.edit.ModelFormMixin.fields` * :attr:`~django.views.generic.edit.FormMixin.form_class` [:meth:`~django.views.generic.edit.FormMixin.get_form_class`] * :attr:`~django.views.generic.base.View.http_method_names` * :attr:`~django.views.generic.edit.FormMixin.initial` [:meth:`~django.views.generic.edit.FormMixin.get_initial`] * :attr:`~django.views.generic.detail.SingleObjectMixin.model` * :attr:`~django.views.generic.detail.SingleObjectMixin.pk_url_kwarg` +* :attr:`~django.views.generic.edit.FormMixin.prefix` [:meth:`~django.views.generic.edit.FormMixin.get_prefix`] * :attr:`~django.views.generic.detail.SingleObjectMixin.queryset` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_queryset`] * :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`] * :attr:`~django.views.generic.detail.SingleObjectMixin.slug_field` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_slug_field`] @@ -216,11 +219,13 @@ UpdateView * :attr:`~django.views.generic.base.TemplateResponseMixin.content_type` * :attr:`~django.views.generic.detail.SingleObjectMixin.context_object_name` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_context_object_name`] +* :attr:`~django.views.generic.edit.ModelFormMixin.fields` * :attr:`~django.views.generic.edit.FormMixin.form_class` [:meth:`~django.views.generic.edit.FormMixin.get_form_class`] * :attr:`~django.views.generic.base.View.http_method_names` * :attr:`~django.views.generic.edit.FormMixin.initial` [:meth:`~django.views.generic.edit.FormMixin.get_initial`] * :attr:`~django.views.generic.detail.SingleObjectMixin.model` * :attr:`~django.views.generic.detail.SingleObjectMixin.pk_url_kwarg` +* :attr:`~django.views.generic.edit.FormMixin.prefix` [:meth:`~django.views.generic.edit.FormMixin.get_prefix`] * :attr:`~django.views.generic.detail.SingleObjectMixin.queryset` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_queryset`] * :attr:`~django.views.generic.base.TemplateResponseMixin.response_class` [:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`] * :attr:`~django.views.generic.detail.SingleObjectMixin.slug_field` [:meth:`~django.views.generic.detail.SingleObjectMixin.get_slug_field`] diff --git a/docs/ref/class-based-views/mixins-editing.txt b/docs/ref/class-based-views/mixins-editing.txt index 48d363b3b2..bf1c10df13 100644 --- a/docs/ref/class-based-views/mixins-editing.txt +++ b/docs/ref/class-based-views/mixins-editing.txt @@ -35,6 +35,12 @@ FormMixin The URL to redirect to when the form is successfully processed. + .. attribute:: prefix + + .. versionadded:: 1.6 + + The :attr:`~django.forms.Form.prefix` for the generated form. + .. method:: get_initial() Retrieve initial data for the form. By default, returns a copy of @@ -58,6 +64,13 @@ FormMixin request is a ``POST`` or ``PUT``, the request data (``request.POST`` and ``request.FILES``) will also be provided. + .. method:: get_prefix() + + .. versionadded:: 1.6 + + Determine the :attr:`~django.forms.Form.prefix` for the generated form. + Returns :attr:`~django.views.generic.edit.FormMixin.prefix` by default. + .. method:: get_success_url() Determine the URL to redirect to when the form is successfully diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt index 90ef68837a..318ce297a2 100644 --- a/docs/ref/contrib/admin/index.txt +++ b/docs/ref/contrib/admin/index.txt @@ -2278,9 +2278,14 @@ your URLconf. Specifically, add these four patterns: url(r'^admin/password_reset/$', 'django.contrib.auth.views.password_reset', name='admin_password_reset'), url(r'^admin/password_reset/done/$', 'django.contrib.auth.views.password_reset_done', name='password_reset_done'), - url(r'^reset/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$', 'django.contrib.auth.views.password_reset_confirm', name='password_reset_confirm'), + url(r'^reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>.+)/$', 'django.contrib.auth.views.password_reset_confirm', name='password_reset_confirm'), url(r'^reset/done/$', 'django.contrib.auth.views.password_reset_complete', name='password_reset_complete'), +.. versionchanged:: 1.6 + + The pattern for :func:`~django.contrib.auth.views.password_reset_confirm` + changed as the ``uid`` is now base 64 encoded. + (This assumes you've added the admin at ``admin/`` and requires that you put the URLs starting with ``^admin/`` before the line that includes the admin app itself). diff --git a/docs/ref/contrib/sitemaps.txt b/docs/ref/contrib/sitemaps.txt index 56a15cb9e0..4467ed3a6e 100644 --- a/docs/ref/contrib/sitemaps.txt +++ b/docs/ref/contrib/sitemaps.txt @@ -358,7 +358,7 @@ with a caching decorator -- you must name your sitemap view and pass from django.views.decorators.cache import cache_page urlpatterns = patterns('', - url(r'^sitemap.xml$', + url(r'^sitemap\.xml$', cache_page(86400)(sitemaps_views.index), {'sitemaps': sitemaps, 'sitemap_url_name': 'sitemaps'}), url(r'^sitemap-(?P<section>.+)\.xml$', diff --git a/docs/ref/databases.txt b/docs/ref/databases.txt index a648ac1709..4e5f136e2e 100644 --- a/docs/ref/databases.txt +++ b/docs/ref/databases.txt @@ -623,6 +623,14 @@ If you're getting this error, you can solve it by: SQLite does not support the ``SELECT ... FOR UPDATE`` syntax. Calling it will have no effect. +"pyformat" parameter style in raw queries not supported +------------------------------------------------------- + +For most backends, raw queries (``Manager.raw()`` or ``cursor.execute()``) +can use the "pyformat" parameter style, where placeholders in the query +are given as ``'%(name)s'`` and the parameters are passed as a dictionary +rather than a list. SQLite does not support this. + .. _sqlite-connection-queries: Parameters not quoted in ``connection.queries`` diff --git a/docs/ref/models/fields.txt b/docs/ref/models/fields.txt index 8146dfd341..f5c1058b17 100644 --- a/docs/ref/models/fields.txt +++ b/docs/ref/models/fields.txt @@ -1083,6 +1083,22 @@ define the details of how the relation works. user = models.ForeignKey(User, related_name='+') +.. attribute:: ForeignKey.related_query_name + + .. versionadded:: 1.6 + + The name to use for the reverse filter name from the target model. + Defaults to the value of :attr:`related_name` if it is set, otherwise it + defaults to the name of the model:: + + # Declare the ForeignKey with related_query_name + class Tag(models.Model): + article = models.ForeignKey(Article, related_name="tags", related_query_name="tag") + name = models.CharField(max_length=255) + + # That's now the name of the reverse filter + article_instance.filter(tag__name="important") + .. attribute:: ForeignKey.to_field The field on the related object that the relation is to. By default, Django @@ -1207,6 +1223,12 @@ that control how the relationship functions. users = models.ManyToManyField(User, related_name='u+') referents = models.ManyToManyField(User, related_name='ref+') +.. attribute:: ForeignKey.related_query_name + + .. versionadded:: 1.6 + + Same as :attr:`ForeignKey.related_query_name`. + .. attribute:: ManyToManyField.limit_choices_to Same as :attr:`ForeignKey.limit_choices_to`. diff --git a/docs/ref/models/instances.txt b/docs/ref/models/instances.txt index cfc95db092..17c9aa9fb7 100644 --- a/docs/ref/models/instances.txt +++ b/docs/ref/models/instances.txt @@ -679,8 +679,11 @@ For every :class:`~django.db.models.DateField` and returns the next and previous object with respect to the date field, raising a :exc:`~django.core.exceptions.DoesNotExist` exception when appropriate. -Both methods accept optional keyword arguments, which should be in the format -described in :ref:`Field lookups <field-lookups>`. +Both of these methods will perform their queries using the default +manager for the model. If you need to emulate filtering used by a +custom manager, or want to perform one-off custom filtering, both +methods also accept optional keyword arguments, which should be in the +format described in :ref:`Field lookups <field-lookups>`. Note that in the case of identical date values, these methods will use the primary key as a tie-breaker. This guarantees that no records are skipped or diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index 897af275a0..215931768c 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -1350,8 +1350,14 @@ A data structure containing configuration information. The contents of this data structure will be passed as the argument to the configuration method described in :setting:`LOGGING_CONFIG`. -The default logging configuration passes HTTP 500 server errors to an -email log handler; all other log messages are given to a NullHandler. +Among other things, the default logging configuration passes HTTP 500 server +errors to an email log handler when :setting:`DEBUG` is ``False``. See also +:ref:`configuring-logging`. + +You can see the default logging configuration by looking in +``django/utils/log.py`` (or view the `online source`__). + +__ https://github.com/django/django/blob/master/django/utils/log.py .. setting:: LOGGING_CONFIG @@ -2564,7 +2570,9 @@ various locations. The default will find files stored in the :setting:`STATICFILES_DIRS` setting (using ``django.contrib.staticfiles.finders.FileSystemFinder``) and in a ``static`` subdirectory of each app (using -``django.contrib.staticfiles.finders.AppDirectoriesFinder``) +``django.contrib.staticfiles.finders.AppDirectoriesFinder``). If multiple +files with the same name are present, the first file that is found will be +used. One finder is disabled by default: ``django.contrib.staticfiles.finders.DefaultStorageFinder``. If added to diff --git a/docs/ref/signals.txt b/docs/ref/signals.txt index 06ba2cb3e8..0253832b8d 100644 --- a/docs/ref/signals.txt +++ b/docs/ref/signals.txt @@ -255,7 +255,7 @@ Arguments sent with this signal: ``pk_set`` For the ``pre_add``, ``post_add``, ``pre_remove`` and ``post_remove`` - actions, this is a list of primary key values that have been added to + actions, this is a set of primary key values that have been added to or removed from the relation. For the ``pre_clear`` and ``post_clear`` actions, this is ``None``. @@ -284,7 +284,7 @@ If we connected a handler like this:: and then did something like this:: - >>> p = Pizza.object.create(...) + >>> p = Pizza.objects.create(...) >>> t = Topping.objects.create(...) >>> p.toppings.add(t) @@ -307,7 +307,7 @@ Argument Value ``model`` ``Topping`` (the class of the objects added to the ``Pizza``) -``pk_set`` ``[t.id]`` (since only ``Topping t`` was added to the relation) +``pk_set`` ``set([t.id])`` (since only ``Topping t`` was added to the relation) ``using`` ``"default"`` (since the default router sends writes here) ============== ============================================================ @@ -334,7 +334,7 @@ Argument Value ``model`` ``Pizza`` (the class of the objects removed from the ``Topping``) -``pk_set`` ``[p.id]`` (since only ``Pizza p`` was removed from the +``pk_set`` ``set([p.id])`` (since only ``Pizza p`` was removed from the relation) ``using`` ``"default"`` (since the default router sends writes here) diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt index 45d7781403..8d722829fb 100644 --- a/docs/ref/utils.txt +++ b/docs/ref/utils.txt @@ -649,6 +649,20 @@ escaping HTML. Converts a positive integer to a base 36 string. On Python 2 ``i`` must be smaller than :data:`sys.maxint`. +.. function:: urlsafe_base64_encode(s) + + .. versionadded:: 1.6 + + Encodes a bytestring in base64 for use in URLs, stripping any trailing + equal signs. + +.. function:: urlsafe_base64_decode(s) + + .. versionadded:: 1.6 + + Decodes a base64 encoded string, adding back any trailing equal signs that + might have been stripped. + ``django.utils.module_loading`` =============================== diff --git a/docs/releases/1.6.txt b/docs/releases/1.6.txt index bd6255eae6..1fd98e1271 100644 --- a/docs/releases/1.6.txt +++ b/docs/releases/1.6.txt @@ -121,10 +121,10 @@ GeoDjango now provides :ref:`form fields and widgets <ref-gis-forms-api>` for its geo-specialized fields. They are OpenLayers-based by default, but they can be customized to use any other JS framework. -``checksetup`` management command added for verifying compatibility -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +``check`` management command added for verifying compatibility +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -A ``checksetup`` management command was added, enabling you to verify if your +A ``check`` management command was added, enabling you to verify if your current configuration (currently oriented at settings) is compatible with the current version of Django. @@ -330,6 +330,19 @@ Minor features behavior of clearing filters by setting the :attr:`~django.contrib.admin.ModelAdmin.preserve_filters` attribute to ``False``. +* Added + :meth:`FormMixin.get_prefix<django.views.generic.edit.FormMixin.get_prefix>` + (which returns + :attr:`FormMixin.prefix<django.views.generic.edit.FormMixin.prefix>` by + default) to allow customizing the :attr:`~django.forms.Form.prefix` of the + form. + +* Raw queries (``Manager.raw()`` or ``cursor.execute()``) can now use the + "pyformat" parameter style, where placeholders in the query are given as + ``'%(name)s'`` and the parameters are passed as a dictionary rather than + a list (except on SQLite). This has long been possible (but not officially + supported) on MySQL and PostgreSQL, and is now also available on Oracle. + Backwards incompatible changes in 1.6 ===================================== @@ -649,6 +662,59 @@ rely on the previous URLs. If you want to revert to the original behavior you can set the :attr:`~django.contrib.admin.ModelAdmin.preserve_filters` attribute to ``False``. +``django.contrib.auth`` password reset uses base 64 encoding of ``User`` PK +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Past versions of Django used base 36 encoding of the ``User`` primary key in +the password reset views and URLs +(:func:`django.contrib.auth.views.password_reset_confirm`). Base 36 encoding is +sufficient if the user primary key is an integer, however, with the +introduction of custom user models in Django 1.5, that assumption may no longer +be true. + +:func:`django.contrib.auth.views.password_reset_confirm` has been modified to +take a ``uidb64`` parameter instead of ``uidb36``. If you are reversing this +view, for example in a custom ``password_reset_email.html`` template, be sure +to update your code. + +A temporary shim for :func:`django.contrib.auth.views.password_reset_confirm` +that will allow password reset links generated prior to Django 1.6 to continue +to work has been added to provide backwards compatibility; this will be removed +in Django 1.7. Thus, as long as your site has been running Django 1.6 for more +than :setting:`PASSWORD_RESET_TIMEOUT_DAYS`, this change will have no effect. +If not (for example, if you upgrade directly from Django 1.5 to Django 1.7), +then any password reset links generated before you upgrade to Django 1.7 or +later won't work after the upgrade. + +In addition, if you have any custom password reset URLs, you will need to +update them by replacing ``uidb36`` with ``uidb64`` and the dash that follows +that pattern with a slash. Also add ``_\-`` to the list of characters that may +match the ``uidb64`` pattern. + +For example:: + + url(r'^reset/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$', + 'django.contrib.auth.views.password_reset_confirm', + name='password_reset_confirm'), + +becomes:: + + url(r'^reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>.+)/$', + 'django.contrib.auth.views.password_reset_confirm', + name='password_reset_confirm'), + +You may also want to add the shim to support the old style reset links. Using +the example above, you would modify the existing url by replacing +``django.contrib.auth.views.password_reset_confirm`` with +``django.contrib.auth.views.password_reset_confirm_uidb36`` and also remove +the ``name`` argument so it doesn't conflict with the new url:: + + url(r'^reset/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$', + 'django.contrib.auth.views.password_reset_confirm_uidb36'), + +You can remove this url pattern after your app has been deployed with Django +1.6 for :setting:`PASSWORD_RESET_TIMEOUT_DAYS`. + Miscellaneous ~~~~~~~~~~~~~ @@ -725,6 +791,12 @@ Miscellaneous returned ``False`` for blank passwords. This has been corrected in this release: blank passwords are now valid. +* The admin :attr:`~django.contrib.admin.ModelAdmin.changelist_view` previously + accepted a ``pop`` GET parameter to signify it was to be displayed in a popup. + This parameter has been renamed to ``_popup`` to be consistent with the rest + of the admin views. You should update your custom templates if they use the + previous parameter name. + Features deprecated in 1.6 ========================== @@ -842,6 +914,13 @@ on a widget, you should now define this method on the form field itself. ``Model._meta.module_name`` was renamed to ``model_name``. Despite being a private API, it will go through a regular deprecation path. +``get_(add|change|delete)_permission`` model _meta methods +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``Model._meta.get_(add|change|delete)_permission`` methods were deprecated. +Even if they were not part of the public API they'll also go through +a regular deprecation path. + ``get_query_set`` and similar methods renamed to ``get_queryset`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/topics/auth/default.txt b/docs/topics/auth/default.txt index 8849520b11..e2fa0c287e 100644 --- a/docs/topics/auth/default.txt +++ b/docs/topics/auth/default.txt @@ -817,7 +817,7 @@ patterns. * ``protocol``: http or https - * ``uid``: The user's id encoded in base 36. + * ``uid``: The user's primary key encoded in base 64. * ``token``: Token to check that the reset link is valid. @@ -826,7 +826,12 @@ patterns. .. code-block:: html+django Someone asked for password reset for email {{ email }}. Follow the link below: - {{ protocol}}://{{ domain }}{% url 'password_reset_confirm' uidb36=uid token=token %} + {{ protocol}}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %} + + .. versionchanged:: 1.6 + + Reversing ``password_reset_confirm`` takes a ``uidb64`` argument instead + of ``uidb36``. The same template context is used for subject template. Subject must be single line plain text string. @@ -846,7 +851,7 @@ patterns. Defaults to :file:`registration/password_reset_done.html` if not supplied. -.. function:: password_reset_confirm(request[, uidb36, token, template_name, token_generator, set_password_form, post_reset_redirect]) +.. function:: password_reset_confirm(request[, uidb64, token, template_name, token_generator, set_password_form, post_reset_redirect]) Presents a form for entering a new password. @@ -854,7 +859,12 @@ patterns. **Optional arguments:** - * ``uidb36``: The user's id encoded in base 36. Defaults to ``None``. + * ``uidb64``: The user's id encoded in base 64. Defaults to ``None``. + + .. versionchanged:: 1.6 + + The ``uidb64`` parameter was previously base 36 encoded and named + ``uidb36``. * ``token``: Token to check that the password is valid. Defaults to ``None``. @@ -877,8 +887,8 @@ patterns. * ``form``: The form (see ``set_password_form`` above) for setting the new user's password. - * ``validlink``: Boolean, True if the link (combination of uidb36 and - token) is valid or unused yet. + * ``validlink``: Boolean, True if the link (combination of ``uidb64`` and + ``token``) is valid or unused yet. .. function:: password_reset_complete(request[,template_name]) diff --git a/docs/topics/class-based-views/generic-display.txt b/docs/topics/class-based-views/generic-display.txt index 7ffa471e79..8c2d0db041 100644 --- a/docs/topics/class-based-views/generic-display.txt +++ b/docs/topics/class-based-views/generic-display.txt @@ -92,6 +92,15 @@ We'll be using these models:: def __unicode__(self): return self.name + class Author(models.Model): + salutation = models.CharField(max_length=10) + name = models.CharField(max_length=200) + email = models.EmailField() + headshot = models.ImageField(upload_to='author_headshots') + + def __unicode__(self): + return self.name + class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField('Author') @@ -132,11 +141,11 @@ bit is just the lowercased version of the model's name. enabled in :setting:`TEMPLATE_LOADERS`, a template location could be: /path/to/project/books/templates/books/publisher_list.html -.. highlightlang:: html+django - This template will be rendered against a context containing a variable called ``object_list`` that contains all the publisher objects. A very simple template -might look like the following:: +might look like the following: + +.. code-block:: html+django {% extends "base.html" %} @@ -159,8 +168,6 @@ consider some of the common ways you might customize and extend generic views. Making "friendly" template contexts ----------------------------------- -.. highlightlang:: python - You might have noticed that our sample publisher list template stores all the publishers in a variable named ``object_list``. While this works just fine, it isn't all that "friendly" to template authors: they have to "just know" that @@ -198,15 +205,12 @@ provided by the generic view. For example, think of showing a list of all the books on each publisher detail page. The :class:`~django.views.generic.detail.DetailView` generic view provides the publisher to the context, but how do we get additional information -in that template. +in that template? -However, there is; you can subclass -:class:`~django.views.generic.detail.DetailView` and provide your own -implementation of the ``get_context_data`` method. The default -implementation of this that comes with -:class:`~django.views.generic.detail.DetailView` simply adds in the -object being displayed to the template, but you can override it to send -more:: +The answer is to subclass :class:`~django.views.generic.detail.DetailView` +and provide your own implementation of the ``get_context_data`` method. +The default implementation simply adds the object being displayed to the +template, but you can override it to send more:: from django.views.generic import DetailView from books.models import Publisher, Book @@ -224,10 +228,10 @@ more:: .. note:: - Generally, get_context_data will merge the context data of all parent + Generally, ``get_context_data`` will merge the context data of all parent classes with those of the current class. To preserve this behavior in your own classes where you want to alter the context, you should be sure to call - get_context_data on the super class. When no two classes try to define the + ``get_context_data`` on the super class. When no two classes try to define the same key, this will give the expected results. However if any class attempts to override a key after parent classes have set it (after the call to super), any children of that class will also need to explicitly set it @@ -372,7 +376,7 @@ Performing extra work The last common pattern we'll look at involves doing some extra work before or after calling the generic view. -Imagine we had a ``last_accessed`` field on our ``Author`` object that we were +Imagine we had a ``last_accessed`` field on our ``Author`` model that we were using to keep track of the last time anybody looked at that author:: # models.py @@ -382,7 +386,7 @@ using to keep track of the last time anybody looked at that author:: salutation = models.CharField(max_length=10) name = models.CharField(max_length=200) email = models.EmailField() - headshot = models.ImageField(upload_to='/tmp') + headshot = models.ImageField(upload_to='author_headshots') last_accessed = models.DateTimeField() The generic ``DetailView`` class, of course, wouldn't know anything about this diff --git a/docs/topics/class-based-views/generic-editing.txt b/docs/topics/class-based-views/generic-editing.txt index 7c4e02cc4e..f12672df69 100644 --- a/docs/topics/class-based-views/generic-editing.txt +++ b/docs/topics/class-based-views/generic-editing.txt @@ -190,8 +190,8 @@ the foreign key relation to the model:: # ... -In the view, ensure that you exclude ``created_by`` in the list of fields to -edit, and override +In the view, ensure that you don't include ``created_by`` in the list of fields +to edit, and override :meth:`~django.views.generic.edit.ModelFormMixin.form_valid()` to add the user:: # views.py @@ -256,3 +256,4 @@ works for AJAX requests as well as 'normal' form POSTs:: class AuthorCreate(AjaxableResponseMixin, CreateView): model = Author + fields = ['name'] diff --git a/docs/topics/class-based-views/intro.txt b/docs/topics/class-based-views/intro.txt index dbbbea25f0..a65b887921 100644 --- a/docs/topics/class-based-views/intro.txt +++ b/docs/topics/class-based-views/intro.txt @@ -208,7 +208,7 @@ A similar class-based view might look like:: def get(self, request, *args, **kwargs): form = self.form_class(initial=self.initial) - return render(request, self.template_name, {'form': form}) + return render(request, self.template_name, {'form': form}) def post(self, request, *args, **kwargs): form = self.form_class(request.POST) diff --git a/docs/topics/class-based-views/mixins.txt b/docs/topics/class-based-views/mixins.txt index 3a4811e7bb..84d7417233 100644 --- a/docs/topics/class-based-views/mixins.txt +++ b/docs/topics/class-based-views/mixins.txt @@ -34,7 +34,7 @@ interface to working with templates in class-based views. :class:`~django.views.generic.base.TemplateResponseMixin` Every built in view which returns a :class:`~django.template.response.TemplateResponse` will call the - :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response` + :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response()` method that ``TemplateResponseMixin`` provides. Most of the time this will be called for you (for instance, it is called by the ``get()`` method implemented by both :class:`~django.views.generic.base.TemplateView` and @@ -44,7 +44,7 @@ interface to working with templates in class-based views. it. For an example of this, see the :ref:`JSONResponseMixin example <jsonresponsemixin-example>`. - ``render_to_response`` itself calls + ``render_to_response()`` itself calls :meth:`~django.views.generic.base.TemplateResponseMixin.get_template_names`, which by default will just look up :attr:`~django.views.generic.base.TemplateResponseMixin.template_name` on @@ -60,9 +60,9 @@ interface to working with templates in class-based views. :class:`~django.views.generic.base.ContextMixin` Every built in view which needs context data, such as for rendering a template (including ``TemplateResponseMixin`` above), should call - :meth:`~django.views.generic.base.ContextMixin.get_context_data` passing + :meth:`~django.views.generic.base.ContextMixin.get_context_data()` passing any data they want to ensure is in there as keyword arguments. - ``get_context_data`` returns a dictionary; in ``ContextMixin`` it + ``get_context_data()`` returns a dictionary; in ``ContextMixin`` it simply returns its keyword arguments, but it is common to override this to add more members to the dictionary. @@ -107,7 +107,7 @@ URLConf, and looks the object up either from the on the view, or the :attr:`~django.views.generic.detail.SingleObjectMixin.queryset` attribute if that's provided). ``SingleObjectMixin`` also overrides -:meth:`~django.views.generic.base.ContextMixin.get_context_data`, +:meth:`~django.views.generic.base.ContextMixin.get_context_data()`, which is used across all Django's built in class-based views to supply context data for template renders. @@ -152,7 +152,7 @@ here would be to dynamically vary the objects, such as depending on the current user or to exclude posts in the future for a blog. :class:`~django.views.generic.list.MultipleObjectMixin` also overrides -:meth:`~django.views.generic.base.ContextMixin.get_context_data` to +:meth:`~django.views.generic.base.ContextMixin.get_context_data()` to include appropriate context variables for pagination (providing dummies if pagination is disabled). It relies on ``object_list`` being passed in as a keyword argument, which :class:`ListView` arranges for @@ -286,12 +286,18 @@ One way to do this is to combine :class:`ListView` with for the paginated list of books can hang off the publisher found as the single object. In order to do this, we need to have two different querysets: -**Publisher queryset for use in get_object** - We'll set that up directly when we call ``get_object()``. +``Publisher`` queryset for use in + :meth:`~django.views.generic.detail.SingleObjectMixin.get_object()` + We'll set the ``model`` attribute on the view and rely on the default + implementation of ``get_object()`` to fetch the correct ``Publisher`` + object. -**Book queryset for use by ListView** - We'll figure that out ourselves in ``get_queryset()`` so we - can take into account the ``Publisher`` we're looking at. +``Book`` queryset for use by :class:`~django.views.generic.list.ListView` + The default implementation of ``get_queryset()`` uses the ``model`` attribute + to construct the queryset. This conflicts with our use of this attribute + for ``get_object()`` so we'll override that method and have it return + the queryset of ``Book`` objects linked to the ``Publisher`` we're looking + at. .. note:: @@ -300,7 +306,7 @@ object. In order to do this, we need to have two different querysets: :class:`ListView` will put things in the context data under the value of ``context_object_name`` if it's set, we'll instead explictly - ensure the Publisher is in the context data. :class:`ListView` + ensure the ``Publisher`` is in the context data. :class:`ListView` will add in the suitable ``page_obj`` and ``paginator`` for us providing we remember to call ``super()``. @@ -311,31 +317,36 @@ Now we can write a new ``PublisherDetail``:: from books.models import Publisher class PublisherDetail(SingleObjectMixin, ListView): + model = Publisher # for SingleObjectMixin.get_object paginate_by = 2 template_name = "books/publisher_detail.html" + def get(self, request, *args, **kwargs): + self.object = self.get_object() + return super(PublisherDetail, self).get(request, *args, **kwargs) + def get_context_data(self, **kwargs): - kwargs['publisher'] = self.object - return super(PublisherDetail, self).get_context_data(**kwargs) + context = super(PublisherDetail, self).get_context_data(**kwargs) + context['publisher'] = self.object + return context def get_queryset(self): - self.object = self.get_object(Publisher.objects.all()) return self.object.book_set.all() -Notice how we set ``self.object`` within ``get_queryset()`` so we -can use it again later in ``get_context_data()``. If you don't set -``template_name``, the template will default to the normal +Notice how we set ``self.object`` within ``get()`` so we +can use it again later in ``get_context_data()`` and ``get_queryset()``. +If you don't set ``template_name``, the template will default to the normal :class:`ListView` choice, which in this case would be ``"books/book_list.html"`` because it's a list of books; :class:`ListView` knows nothing about :class:`~django.views.generic.detail.SingleObjectMixin`, so it doesn't have -any clue this view is anything to do with a Publisher. - -.. highlightlang:: html+django +any clue this view is anything to do with a ``Publisher``. The ``paginate_by`` is deliberately small in the example so you don't have to create lots of books to see the pagination working! Here's the -template you'd want to use:: +template you'd want to use: + +.. code-block: html+django {% extends "base.html" %} @@ -427,8 +438,6 @@ code so that on ``POST`` the form gets called appropriately. both of the views implement ``get()``, and things would get much more confusing. -.. highlightlang:: python - Our new ``AuthorDetail`` looks like this:: # CAUTION: you almost certainly do not want to do this. @@ -451,21 +460,18 @@ Our new ``AuthorDetail`` looks like this:: form_class = AuthorInterestForm def get_success_url(self): - return reverse( - 'author-detail', - kwargs = {'pk': self.object.pk}, - ) + return reverse('author-detail', kwargs={'pk': self.object.pk}) def get_context_data(self, **kwargs): + context = super(AuthorDetail, self).get_context_data(**kwargs) form_class = self.get_form_class() - form = self.get_form(form_class) - context = { - 'form': form - } - context.update(kwargs) - return super(AuthorDetail, self).get_context_data(**context) + context['form'] = self.get_form(form_class) + return context def post(self, request, *args, **kwargs): + if not request.user.is_authenticated(): + return HttpResponseForbidden() + self.object = self.get_object() form_class = self.get_form_class() form = self.get_form(form_class) if form.is_valid(): @@ -474,10 +480,8 @@ Our new ``AuthorDetail`` looks like this:: return self.form_invalid(form) def form_valid(self, form): - if not self.request.user.is_authenticated(): - return HttpResponseForbidden() - self.object = self.get_object() - # record the interest using the message in form.cleaned_data + # Here, we would record the user's interest using the message + # passed in form.cleaned_data['message'] return super(AuthorDetail, self).form_valid(form) ``get_success_url()`` is just providing somewhere to redirect to, @@ -530,15 +534,12 @@ write our own ``get_context_data()`` to make the message = forms.CharField() class AuthorDisplay(DetailView): - - queryset = Author.objects.all() + model = Author def get_context_data(self, **kwargs): - context = { - 'form': AuthorInterestForm(), - } - context.update(kwargs) - return super(AuthorDisplay, self).get_context_data(**context) + context = super(AuthorDisplay, self).get_context_data(**kwargs) + context['form'] = AuthorInterestForm() + return context Then the ``AuthorInterest`` is a simple :class:`FormView`, but we have to bring in :class:`~django.views.generic.detail.SingleObjectMixin` so we @@ -558,24 +559,14 @@ template as ``AuthorDisplay`` is using on ``GET``. form_class = AuthorInterestForm model = Author - def get_context_data(self, **kwargs): - context = { - 'object': self.get_object(), - } - return super(AuthorInterest, self).get_context_data(**context) - - def get_success_url(self): - return reverse( - 'author-detail', - kwargs = {'pk': self.object.pk}, - ) - - def form_valid(self, form): - if not self.request.user.is_authenticated(): + def post(self, request, *args, **kwargs): + if not request.user.is_authenticated(): return HttpResponseForbidden() self.object = self.get_object() - # record the interest using the message in form.cleaned_data - return super(AuthorInterest, self).form_valid(form) + return super(AuthorInterest, self).post(request, *args, **kwargs) + + def get_success_url(self): + return reverse('author-detail', kwargs={'pk': self.object.pk}) Finally we bring this together in a new ``AuthorDetail`` view. We already know that calling :meth:`~django.views.generic.base.View.as_view()` on @@ -627,15 +618,13 @@ For example, a simple JSON mixin might look something like this:: """ A mixin that can be used to render a JSON response. """ - response_class = HttpResponse - - def render_to_response(self, context, **response_kwargs): + def render_to_json_response(self, context, **response_kwargs): """ Returns a JSON response, transforming 'context' to make the payload. """ - response_kwargs['content_type'] = 'application/json' - return self.response_class( + return HttpResponse( self.convert_context_to_json(context), + content_type='application/json', **response_kwargs ) @@ -647,12 +636,22 @@ For example, a simple JSON mixin might look something like this:: # -- can be serialized as JSON. return json.dumps(context) -Now we mix this into the base TemplateView:: +.. note:: + + Check out the :doc:`/topics/serialization` documentation for more + information on how to correctly transform Django models and querysets into + JSON. + +This mixin provides a ``render_to_json_response()`` method with the same signature +as :func:`~django.views.generic.base.TemplateResponseMixin.render_to_response()`. +To use it, we simply need to mix it into a ``TemplateView`` for example, +and override ``render_to_response()`` to call ``render_to_json_response()`` instead:: from django.views.generic import TemplateView class JSONView(JSONResponseMixin, TemplateView): - pass + def render_to_response(self, context, **response_kwargs): + return self.render_to_json_response(context, **response_kwargs) Equally we could use our mixin with one of the generic views. We can make our own version of :class:`~django.views.generic.detail.DetailView` by mixing @@ -664,7 +663,8 @@ rendering behavior has been mixed in):: from django.views.generic.detail import BaseDetailView class JSONDetailView(JSONResponseMixin, BaseDetailView): - pass + def render_to_response(self, context, **response_kwargs): + return self.render_to_json_response(context, **response_kwargs) This view can then be deployed in the same way as any other :class:`~django.views.generic.detail.DetailView`, with exactly the @@ -678,20 +678,21 @@ in both the ``JSONResponseMixin`` and a :class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`, and override the implementation of :func:`~django.views.generic.base.TemplateResponseMixin.render_to_response()` -to defer to the appropriate subclass depending on the type of response that the -user requested:: +to defer to the appropriate rendering method depending on the type of response +that the user requested:: from django.views.generic.detail import SingleObjectTemplateResponseMixin class HybridDetailView(JSONResponseMixin, SingleObjectTemplateResponseMixin, BaseDetailView): def render_to_response(self, context): # Look for a 'format=json' GET argument - if self.request.GET.get('format','html') == 'json': - return JSONResponseMixin.render_to_response(self, context) + if self.request.GET.get('format') == 'json': + return self.render_to_json_response(context) else: - return SingleObjectTemplateResponseMixin.render_to_response(self, context) + return super(HybridDetailView, self).render_to_response(context) -Because of the way that Python resolves method overloading, the local -``render_to_response()`` implementation will override the versions provided by -``JSONResponseMixin`` and -:class:`~django.views.generic.detail.SingleObjectTemplateResponseMixin`. +Because of the way that Python resolves method overloading, the call to +``super(HybridDetailView, self).render_to_response(context)`` ends up +calling the +:meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response()` +implementation of :class:`~django.views.generic.base.TemplateResponseMixin`. diff --git a/docs/topics/db/sql.txt b/docs/topics/db/sql.txt index 2ec31a4988..7437d51d28 100644 --- a/docs/topics/db/sql.txt +++ b/docs/topics/db/sql.txt @@ -166,9 +166,17 @@ argument to ``raw()``:: >>> lname = 'Doe' >>> Person.objects.raw('SELECT * FROM myapp_person WHERE last_name = %s', [lname]) -``params`` is a list of parameters. You'll use ``%s`` placeholders in the -query string (regardless of your database engine); they'll be replaced with -parameters from the ``params`` list. +``params`` is a list or dictionary of parameters. You'll use ``%s`` +placeholders in the query string for a list, or ``%(key)s`` +placeholders for a dictionary (where ``key`` is replaced by a +dictionary key, of course), regardless of your database engine. Such +placeholders will be replaced with parameters from the ``params`` +argument. + +.. note:: Dictionary params not supported with SQLite + + Dictionary params are not supported with the SQLite backend; with + this backend, you must pass parameters as a list. .. warning:: @@ -181,14 +189,21 @@ parameters from the ``params`` list. **Don't.** - Using the ``params`` list completely protects you from `SQL injection + Using the ``params`` argument completely protects you from `SQL injection attacks`__, a common exploit where attackers inject arbitrary SQL into your database. If you use string interpolation, sooner or later you'll fall victim to SQL injection. As long as you remember to always use the - ``params`` list you'll be protected. + ``params`` argument you'll be protected. __ http://en.wikipedia.org/wiki/SQL_injection +.. versionchanged:: 1.6 + + In Django 1.5 and earlier, you could pass parameters as dictionaries + when using PostgreSQL or MySQL, although this wasn't documented. Now + you can also do this whem using Oracle, and it is officially supported. + + .. _executing-custom-sql: Executing custom SQL directly diff --git a/docs/topics/db/transactions.txt b/docs/topics/db/transactions.txt index e9a626f56b..903579cc38 100644 --- a/docs/topics/db/transactions.txt +++ b/docs/topics/db/transactions.txt @@ -389,6 +389,27 @@ The following example demonstrates the use of savepoints:: transaction.savepoint_rollback(sid) # open transaction now contains only a.save() +.. versionadded:: 1.6 + +Savepoints may be used to recover from a database error by performing a partial +rollback. If you're doing this inside an :func:`atomic` block, the entire block +will still be rolled back, because it doesn't know you've handled the situation +at a lower level! To prevent this, you can control the rollback behavior with +the following functions. + +.. function:: get_rollback(using=None) + +.. function:: set_rollback(rollback, using=None) + +Setting the rollback flag to ``True`` forces a rollback when exiting the +innermost atomic block. This may be useful to trigger a rollback without +raising an exception. + +Setting it to ``False`` prevents such a rollback. Before doing that, make sure +you've rolled back the transaction to a known-good savepoint within the current +atomic block! Otherwise you're breaking atomicity and data corruption may +occur. + Database-specific notes ======================= diff --git a/docs/topics/http/file-uploads.txt b/docs/topics/http/file-uploads.txt index 54d748d961..f6fa27e27c 100644 --- a/docs/topics/http/file-uploads.txt +++ b/docs/topics/http/file-uploads.txt @@ -371,8 +371,8 @@ Custom file upload handlers **must** define the following methods: ``receive_data_chunk`` methods. In this way, one handler can be a "filter" for other handlers. - Return ``None`` from ``receive_data_chunk`` to sort-circuit remaining - upload handlers from getting this chunk.. This is useful if you're + Return ``None`` from ``receive_data_chunk`` to short-circuit remaining + upload handlers from getting this chunk. This is useful if you're storing the uploaded data yourself and don't want future handlers to store a copy of the data. diff --git a/docs/topics/http/shortcuts.txt b/docs/topics/http/shortcuts.txt index 52a2935977..5c8725172a 100644 --- a/docs/topics/http/shortcuts.txt +++ b/docs/topics/http/shortcuts.txt @@ -277,8 +277,8 @@ will be raised if more than one object is found. .. function:: get_list_or_404(klass, *args, **kwargs) Returns the result of :meth:`~django.db.models.query.QuerySet.filter()` on a - given model manager, raising :class:`~django.http.Http404` if the resulting - list is empty. + given model manager cast to a list, raising :class:`~django.http.Http404` if + the resulting list is empty. Required arguments ------------------ diff --git a/docs/topics/testing/overview.txt b/docs/topics/testing/overview.txt index 8268051a36..d56b1be20f 100644 --- a/docs/topics/testing/overview.txt +++ b/docs/topics/testing/overview.txt @@ -221,10 +221,12 @@ 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. +* All :class:`~django.test.TestCase` subclasses are run first. + +* Then, all other unittests (including :class:`unittest.TestCase`, + :class:`~django.test.SimpleTestCase` 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. |
