mirror of
https://github.com/django/django.git
synced 2024-11-30 15:10:46 +01:00
1459 lines
60 KiB
Plaintext
1459 lines
60 KiB
Plaintext
============================================
|
||
Django 1.8 release notes - UNDER DEVELOPMENT
|
||
============================================
|
||
|
||
Welcome to Django 1.8!
|
||
|
||
These release notes cover the `new features`_, as well as some `backwards
|
||
incompatible changes`_ you'll want to be aware of when upgrading from Django
|
||
1.6 or older versions. We've also `begun the deprecation process for some
|
||
features`_, and some features have reached the end of their deprecation process
|
||
and `have been removed`_.
|
||
|
||
Django 1.8 has been designated as Django's second :ref:`"Long-Term Support"
|
||
(LTS) <lts-releases>` release. It will receive security updates for at least
|
||
three years after its release. Support for the previous LTS, Django 1.4, will
|
||
end 6 months from the release date of Django 1.8.
|
||
|
||
.. _`new features`: `What's new in Django 1.8`_
|
||
.. _`backwards incompatible changes`: `Backwards incompatible changes in 1.8`_
|
||
.. _`begun the deprecation process for some features`: `Features deprecated in 1.8`_
|
||
.. _`have been removed`: `Features removed in 1.8`_
|
||
|
||
Python compatibility
|
||
====================
|
||
|
||
Like Django 1.7, Django 1.8 requires Python 2.7 or above, though we
|
||
**highly recommend** the latest minor release.
|
||
|
||
What's new in Django 1.8
|
||
========================
|
||
|
||
Security enhancements
|
||
~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Several features of the django-secure_ third-party library have been
|
||
integrated into Django. :class:`django.middleware.security.SecurityMiddleware`
|
||
provides several security enhancements to the request/response cycle. The new
|
||
:djadminopt:`--deploy` option of the :djadmin:`check` command allows you to
|
||
check your production settings file for ways to increase the security of your
|
||
site.
|
||
|
||
.. _django-secure: https://pypi.python.org/pypi/django-secure
|
||
|
||
New PostgreSQL specific functionality
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Django now has a module with extensions for PostgreSQL specific features, such
|
||
as :class:`~django.contrib.postgres.fields.ArrayField`,
|
||
:class:`~django.contrib.postgres.fields.HStoreField`, and :lookup:`unaccent`
|
||
lookup. A full breakdown of the features is available :doc:`in the
|
||
documentation </ref/contrib/postgres/index>`.
|
||
|
||
New data types
|
||
~~~~~~~~~~~~~~
|
||
|
||
* Django now has a :class:`~django.db.models.UUIDField` for storing
|
||
universally unique identifiers. It is stored as the native ``uuid`` data type
|
||
on PostgreSQL and as a fixed length character field on other backends. There
|
||
is a corresponding :class:`form field <django.forms.UUIDField>`.
|
||
|
||
* Django now has a :class:`~django.db.models.DurationField` for storing periods
|
||
of time - modeled in Python by :class:`~python:datetime.timedelta`. It is
|
||
stored in the native ``interval`` data type on PostgreSQL, as a ``INTERVAL
|
||
DAY(9) TO SECOND(6)`` on Oracle, and as a ``bigint`` of microseconds on other
|
||
backends. Date and time related arithmetic has also been improved on all
|
||
backends. There is a corresponding :class:`form field
|
||
<django.forms.DurationField>`.
|
||
|
||
Query Expressions and Database Functions
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
:doc:`Query Expressions </ref/models/expressions>` allow users to create,
|
||
customize, and compose complex SQL expressions. This has enabled annotate
|
||
to accept expressions other than aggregates. Aggregates are now able to
|
||
reference multiple fields, as well as perform arithmetic, similar to ``F()``
|
||
objects.
|
||
|
||
A collection of :doc:`database functions </ref/models/database-functions>` is
|
||
also included with functionality such as
|
||
:class:`~django.db.models.functions.Coalesce`,
|
||
:class:`~django.db.models.functions.Concat`, and
|
||
:class:`~django.db.models.functions.Substr`.
|
||
|
||
``TestCase`` data setup
|
||
~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
:class:`~django.test.TestCase` has been refactored to allow for data
|
||
initialization at the class level using transactions and savepoints. Database
|
||
backends which do not support transactions, like MySQL with the MyISAM storage
|
||
engine, will still be able to run these tests but won't benefit from the
|
||
improvements. Tests are now run within two nested
|
||
:func:`~django.db.transaction.atomic()` blocks: one for the whole class and one
|
||
for each test.
|
||
|
||
* The class method
|
||
:meth:`TestCase.setUpTestData() <django.test.TestCase.setUpTestData>` adds
|
||
the ability to setup test data at the class level. Using this technique can
|
||
speed up the tests as compared to using ``setUp()``.
|
||
|
||
* Fixture loading within ``TestCase`` is now performed once for the whole
|
||
``TestCase``.
|
||
|
||
Minor features
|
||
~~~~~~~~~~~~~~
|
||
|
||
:mod:`django.contrib.admin`
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
* :class:`~django.contrib.admin.ModelAdmin` now has a
|
||
:meth:`~django.contrib.admin.ModelAdmin.has_module_permission`
|
||
method to allow limiting access to the module on the admin index page.
|
||
|
||
* :class:`~django.contrib.admin.InlineModelAdmin` now has an attribute
|
||
:attr:`~django.contrib.admin.InlineModelAdmin.show_change_link` that
|
||
supports showing a link to an inline object's change form.
|
||
|
||
* Use the new ``django.contrib.admin.RelatedOnlyFieldListFilter`` in
|
||
:attr:`ModelAdmin.list_filter <django.contrib.admin.ModelAdmin.list_filter>`
|
||
to limit the ``list_filter`` choices to foreign objects which are attached to
|
||
those from the ``ModelAdmin``.
|
||
|
||
* The :meth:`ModelAdmin.delete_view()
|
||
<django.contrib.admin.ModelAdmin.delete_view>` displays a summary of objects
|
||
to be deleted on the deletion confirmation page.
|
||
|
||
* The jQuery library embedded in the admin has been upgraded to version 1.11.2.
|
||
|
||
* You can now specify :attr:`AdminSite.site_url
|
||
<django.contrib.admin.AdminSite.site_url>` in order to display a link to the
|
||
front-end site.
|
||
|
||
* You can now specify :attr:`ModelAdmin.show_full_result_count
|
||
<django.contrib.admin.ModelAdmin.show_full_result_count>` to control whether
|
||
or not the full count of objects should be displayed on a filtered admin page.
|
||
|
||
* The ``AdminSite.password_change()`` method now has an ``extra_context``
|
||
parameter.
|
||
|
||
* You can now control who may login to the admin site by overriding only
|
||
:meth:`AdminSite.has_permission()
|
||
<django.contrib.admin.AdminSite.has_permission>` and
|
||
:attr:`AdminSite.login_form <django.contrib.admin.AdminSite.login_form>`.
|
||
The ``base.html`` template has a new block ``usertools`` which contains the
|
||
user-specific header. A new context variable ``has_permission``, which gets
|
||
its value from :meth:`~django.contrib.admin.AdminSite.has_permission`,
|
||
indicates whether the user may access the site.
|
||
|
||
:mod:`django.contrib.admindocs`
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
* reStructuredText is now parsed in model docstrings.
|
||
|
||
:mod:`django.contrib.auth`
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
* Authorization backends can now raise
|
||
:class:`~django.core.exceptions.PermissionDenied` in
|
||
:meth:`~django.contrib.auth.models.User.has_perm`
|
||
and :meth:`~django.contrib.auth.models.User.has_module_perms`
|
||
to short-circuit permission checking.
|
||
* :class:`~django.contrib.auth.forms.PasswordResetForm` now
|
||
has a method :meth:`~django.contrib.auth.forms.PasswordResetForm.send_email`
|
||
that can be overridden to customize the mail to be sent.
|
||
|
||
* The ``max_length`` of :attr:`Permission.name
|
||
<django.contrib.auth.models.Permission.name>` has been increased from 50 to
|
||
255 characters. Please run the database migration.
|
||
|
||
* :attr:`~django.contrib.auth.models.CustomUser.USERNAME_FIELD` and
|
||
:attr:`~django.contrib.auth.models.CustomUser.REQUIRED_FIELDS` now supports
|
||
:class:`~django.db.models.ForeignKey`\s.
|
||
|
||
* The default iteration count for the PBKDF2 password hasher has been
|
||
increased by 33%. This backwards compatible change will not affect users who
|
||
have subclassed ``django.contrib.auth.hashers.PBKDF2PasswordHasher`` to
|
||
change the default value.
|
||
|
||
:mod:`django.contrib.gis`
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
* A new :doc:`GeoJSON serializer </ref/contrib/gis/serializers>` is now
|
||
available.
|
||
|
||
* The Spatialite backend now supports ``Collect`` and ``Extent`` aggregates
|
||
when the database version is 3.0 or later.
|
||
|
||
* The PostGIS 2 ``CREATE EXTENSION postgis`` and the Spatialite
|
||
``SELECT InitSpatialMetaData`` initialization commands are now automatically
|
||
run by :djadmin:`migrate`.
|
||
|
||
* The GDAL interface now supports retrieving properties of
|
||
:ref:`raster (image) data file <raster-data-source-objects>`.
|
||
|
||
* Compatibility shims for ``SpatialRefSys`` and ``GeometryColumns`` changed in
|
||
Django 1.2 have been removed.
|
||
|
||
* All GDAL-related exceptions are now raised with ``GDALException``. The former
|
||
``OGRException`` has been kept for backwards compatibility but should not be
|
||
used any longer.
|
||
|
||
:mod:`django.contrib.messages`
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
* ...
|
||
|
||
:mod:`django.contrib.redirects`
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
* ...
|
||
|
||
:mod:`django.contrib.sessions`
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
* Session cookie is now deleted after
|
||
:meth:`~django.contrib.sessions.backends.base.SessionBase.flush()` is called.
|
||
|
||
:mod:`django.contrib.sitemaps`
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
* The new :attr:`Sitemap.i18n <django.contrib.sitemaps.Sitemap.i18n>` attribute
|
||
allows you to generate a sitemap based on the :setting:`LANGUAGES` setting.
|
||
|
||
:mod:`django.contrib.sites`
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
* :func:`~django.contrib.sites.shortcuts.get_current_site` will now lookup
|
||
the current site based on :meth:`request.get_host()
|
||
<django.http.HttpRequest.get_host>` if the :setting:`SITE_ID` setting is not
|
||
defined.
|
||
|
||
* The default :class:`~django.contrib.sites.models.Site` created when running
|
||
``migrate`` now respects the :setting:`SITE_ID` setting (instead of always
|
||
using ``pk=1``).
|
||
|
||
:mod:`django.contrib.staticfiles`
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
* ...
|
||
|
||
:mod:`django.contrib.syndication`
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
* ...
|
||
|
||
Cache
|
||
^^^^^
|
||
|
||
* The ``incr()`` method of the
|
||
``django.core.cache.backends.locmem.LocMemCache`` backend is now thread-safe.
|
||
|
||
Cryptography
|
||
^^^^^^^^^^^^
|
||
|
||
* The ``max_age`` parameter of the
|
||
:meth:`django.core.signing.TimestampSigner.unsign` method now also accept a
|
||
:py:class:`datetime.timedelta` object.
|
||
|
||
Database backends
|
||
^^^^^^^^^^^^^^^^^
|
||
|
||
* The MySQL backend no longer strips microseconds from ``datetime`` values as
|
||
MySQL 5.6.4 and up supports fractional seconds depending on the declaration
|
||
of the datetime field (when ``DATETIME`` includes fractional precision greater
|
||
than 0). New datetime database columns created with Django 1.8 and MySQL 5.6.4
|
||
and up will support microseconds. See the :ref:`MySQL database notes
|
||
<mysql-fractional-seconds>` for more details.
|
||
|
||
* The MySQL backend no longer creates explicit indexes for foreign keys when
|
||
using the InnoDB storage engine, as MySQL already creates them automatically.
|
||
|
||
Email
|
||
^^^^^
|
||
|
||
* :ref:`Email backends <topic-email-backends>` now support the context manager
|
||
protocol for opening and closing connections.
|
||
|
||
* The SMTP email backend now supports ``keyfile`` and ``certfile``
|
||
authentication with the :setting:`EMAIL_SSL_CERTFILE` and
|
||
:setting:`EMAIL_SSL_KEYFILE` settings.
|
||
|
||
* The SMTP :class:`~django.core.mail.backends.smtp.EmailBackend` now supports
|
||
setting the ``timeout`` parameter with the :setting:`EMAIL_TIMEOUT` setting.
|
||
|
||
* :class:`~django.core.mail.EmailMessage` and ``EmailMultiAlternatives`` now
|
||
support the ``reply_to`` parameter.
|
||
|
||
File Storage
|
||
^^^^^^^^^^^^
|
||
|
||
* ...
|
||
|
||
File Uploads
|
||
^^^^^^^^^^^^
|
||
|
||
* ...
|
||
|
||
Forms
|
||
^^^^^
|
||
|
||
* Form widgets now render attributes with a value of ``True`` or ``False``
|
||
as HTML5 boolean attributes.
|
||
|
||
* The new :meth:`~django.forms.Form.has_error()` method allows checking
|
||
if a specific error has happened.
|
||
|
||
* If :attr:`~django.forms.Form.required_css_class` is defined on a form, then
|
||
the ``<label>`` tags for required fields will have this class present in its
|
||
attributes.
|
||
|
||
* The rendering of non-field errors in unordered lists (``<ul>``) now includes
|
||
``nonfield`` in its list of classes to distinguish them from field-specific
|
||
errors.
|
||
|
||
* :class:`~django.forms.Field` now accepts a
|
||
:attr:`~django.forms.Field.label_suffix` argument, which will override the
|
||
form's :attr:`~django.forms.Form.label_suffix`. This enables customizing the
|
||
suffix on a per-field basis — previously it wasn't possible to override
|
||
a form's :attr:`~django.forms.Form.label_suffix` while using shortcuts such
|
||
as ``{{ form.as_p }}`` in templates.
|
||
|
||
* :class:`~django.forms.extras.widgets.SelectDateWidget` now accepts an
|
||
:attr:`~django.forms.extras.widgets.SelectDateWidget.empty_label` argument, which will
|
||
override the top list choice label when :class:`~django.forms.DateField` is not required.
|
||
|
||
* After an :class:`~django.forms.ImageField` has been cleaned and validated, the
|
||
``UploadedFile`` object will have an additional ``image`` attribute containing
|
||
the Pillow ``Image`` instance used to check if the file was a valid image. It
|
||
will also update ``UploadedFile.content_type`` with the image's content type
|
||
as determined by Pillow.
|
||
|
||
* You can now pass a callable that returns an iterable of choices when
|
||
instantiating a :class:`~django.forms.ChoiceField`.
|
||
|
||
Generic Views
|
||
^^^^^^^^^^^^^
|
||
|
||
* Generic views that use :class:`~django.views.generic.list.MultipleObjectMixin`
|
||
may now specify the ordering applied to the
|
||
:attr:`~django.views.generic.list.MultipleObjectMixin.queryset` by setting
|
||
:attr:`~django.views.generic.list.MultipleObjectMixin.ordering` or overriding
|
||
:meth:`~django.views.generic.list.MultipleObjectMixin.get_ordering()`.
|
||
|
||
* The new :attr:`SingleObjectMixin.query_pk_and_slug
|
||
<django.views.generic.detail.SingleObjectMixin.query_pk_and_slug>`
|
||
attribute allows changing the behavior of
|
||
:meth:`~django.views.generic.detail.SingleObjectMixin.get_object()`
|
||
so that it'll perform its lookup using both the primary key and the slug.
|
||
|
||
* The :meth:`~django.views.generic.edit.FormMixin.get_form()` method doesn't
|
||
require a ``form_class`` to be provided anymore. If not provided ``form_class``
|
||
defaults to :meth:`~django.views.generic.edit.FormMixin.get_form_class()`.
|
||
|
||
Internationalization
|
||
^^^^^^^^^^^^^^^^^^^^
|
||
|
||
* :setting:`FORMAT_MODULE_PATH` can now be a list of strings representing
|
||
module paths. This allows importing several format modules from different
|
||
reusable apps. It also allows overriding those custom formats in your main
|
||
Django project.
|
||
|
||
Logging
|
||
^^^^^^^
|
||
|
||
* The :class:`django.utils.log.AdminEmailHandler` class now has a
|
||
:meth:`~django.utils.log.AdminEmailHandler.send_mail` method to make it more
|
||
subclass friendly.
|
||
|
||
Management Commands
|
||
^^^^^^^^^^^^^^^^^^^
|
||
|
||
* Database connections are now always closed after a management command called
|
||
from the command line has finished doing its job.
|
||
|
||
* :djadmin:`dumpdata` now has the option :djadminopt:`--output` which allows
|
||
specifying the file to which the serialized data is written.
|
||
|
||
* :djadmin:`makemessages` and :djadmin:`compilemessages` now have the option
|
||
:djadminopt:`--exclude` which allows exclusion of specific locales from
|
||
processing.
|
||
|
||
* :djadmin:`compilemessages` now has a ``--use-fuzzy`` or ``-f`` option which
|
||
includes fuzzy translations into compiled files.
|
||
|
||
* The :djadminopt:`--ignorenonexistent` option of the :djadmin:`loaddata`
|
||
management command now ignores data for models that no longer exist.
|
||
|
||
* :djadmin:`runserver` now uses daemon threads for faster reloading.
|
||
|
||
* :djadmin:`inspectdb` now outputs ``Meta.unique_together``. It is also able to
|
||
introspect :class:`~django.db.models.AutoField` for MySQL and PostgreSQL
|
||
databases.
|
||
|
||
* When calling management commands from code through :ref:`call_command
|
||
<call-command>` and passing options, the option name can match the command
|
||
line option name (without the initial dashes) or the final option destination
|
||
variable name, but in either case, the resulting option received by the
|
||
command is now always the ``dest`` name specified in the command option
|
||
definition (as long as the command uses the new :py:mod:`argparse` module).
|
||
|
||
* The :djadmin:`dbshell` command now supports MySQL's optional SSL certificate
|
||
authority setting (``--ssl-ca``).
|
||
|
||
* The :djadminopt:`--name` option for :djadmin:`makemigrations` allows you to
|
||
to give the migration(s) a custom name instead of a generated one.
|
||
|
||
* The :djadmin:`loaddata` command now prevents repeated fixture loading. If
|
||
:setting:`FIXTURE_DIRS` contains duplicates or a default fixture directory
|
||
path (``app_name/fixtures``), an exception is raised.
|
||
|
||
* :djadmin:`makemigrations` now supports an :djadminopt:`--exit` option to
|
||
exit with an error code if no migrations are created.
|
||
|
||
* The new :djadmin:`showmigrations` command allows listing all migrations and
|
||
their dependencies in a project.
|
||
|
||
Middleware
|
||
^^^^^^^^^^
|
||
|
||
* The :attr:`CommonMiddleware.response_redirect_class
|
||
<django.middleware.common.CommonMiddleware.response_redirect_class>`
|
||
attribute allows you to customize the redirects issued by the middleware.
|
||
|
||
* A debug message will be logged to the ``django.request`` logger when a
|
||
middleware raises a :exc:`~django.core.exceptions.MiddlewareNotUsed` exception
|
||
in :setting:`DEBUG` mode.
|
||
|
||
Migrations
|
||
^^^^^^^^^^
|
||
|
||
* The :class:`~django.db.migrations.operations.RunSQL` operation can now handle
|
||
parameters passed to the SQL statements.
|
||
|
||
* It is now possible to have migrations (most probably :ref:`data migrations
|
||
<data-migrations>`) for applications without models.
|
||
|
||
* Migrations can now :ref:`serialize model managers
|
||
<using-managers-in-migrations>` as part of the model state.
|
||
|
||
Models
|
||
^^^^^^
|
||
|
||
* Django now logs at most 9000 queries in ``connections.queries``, in order
|
||
to prevent excessive memory usage in long-running processes in debug mode.
|
||
|
||
* There is now a model ``Meta`` option to define a
|
||
:attr:`default related name <django.db.models.Options.default_related_name>`
|
||
for all relational fields of a model.
|
||
|
||
* Pickling models and querysets across different versions of Django isn't
|
||
officially supported (it may work, but there's no guarantee). An extra
|
||
variable that specifies the current Django version is now added to the
|
||
pickled state of models and querysets, and Django raises a ``RuntimeWarning``
|
||
when these objects are unpickled in a different version than the one in
|
||
which they were pickled.
|
||
|
||
* Added :meth:`Model.from_db() <django.db.models.Model.from_db()>` which
|
||
Django uses whenever objects are loaded using the ORM. The method allows
|
||
customizing model loading behavior.
|
||
|
||
* ``extra(select={...})`` now allows you to escape a literal ``%s`` sequence
|
||
using ``%%s``.
|
||
|
||
* :doc:`Custom Lookups</howto/custom-lookups>` can now be registered using
|
||
a decorator pattern.
|
||
|
||
* The new :attr:`Transform.bilateral <django.db.models.Transform.bilateral>`
|
||
attribute allows creating bilateral transformations. These transformations
|
||
are applied to both ``lhs`` and ``rhs`` when used in a lookup expression,
|
||
providing opportunities for more sophisticated lookups.
|
||
|
||
* SQL special characters (\, %, _) are now escaped properly when a pattern
|
||
lookup (e.g. ``contains``, ``startswith``, etc.) is used with an ``F()``
|
||
expression as the right-hand side. In those cases, the escaping is performed
|
||
by the database, which can lead to somewhat complex queries involving nested
|
||
``REPLACE`` function calls.
|
||
|
||
* You can now refresh model instances by using :meth:`Model.refresh_from_db()
|
||
<django.db.models.Model.refresh_from_db>`.
|
||
|
||
* You can now get the set of deferred fields for a model using
|
||
:meth:`Model.get_deferred_fields() <django.db.models.Model.get_deferred_fields>`.
|
||
|
||
Signals
|
||
^^^^^^^
|
||
|
||
* Exceptions from the ``(receiver, exception)`` tuples returned by
|
||
:meth:`Signal.send_robust() <django.dispatch.Signal.send_robust>` now have
|
||
their traceback attached as a ``__traceback__`` attribute.
|
||
|
||
* The ``environ`` argument, which contains the WSGI environment structure from
|
||
the request, was added to the :data:`~django.core.signals.request_started`
|
||
signal.
|
||
|
||
* You can now import the :func:`~django.test.signals.setting_changed` signal
|
||
from ``django.core.signals`` to avoid loading ``django.test`` in non-test
|
||
situations. Django no longer does so itself.
|
||
|
||
System Check Framework
|
||
^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
* :attr:`~django.core.checks.register` can now be used as a function.
|
||
|
||
Templates
|
||
^^^^^^^^^
|
||
|
||
* :tfilter:`urlize` now supports domain-only links that include characters after
|
||
the top-level domain (e.g. ``djangoproject.com/`` and
|
||
``djangoproject.com/download/``).
|
||
|
||
* :tfilter:`urlize` doesn't treat exclamation marks at the end of a domain or
|
||
its query string as part of the URL (the URL in e.g. ``'djangoproject.com!``
|
||
is ``djangoproject.com``)
|
||
|
||
* Added a :class:`locmem.Loader <django.template.loaders.locmem.Loader>`
|
||
class that loads Django templates from a Python dictionary.
|
||
|
||
* The :ttag:`now` tag can now store its output in a context variable with the
|
||
usual syntax: ``{% now 'j n Y' as varname %}``.
|
||
|
||
Requests and Responses
|
||
^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
* ``WSGIRequest`` now respects paths starting with ``//``.
|
||
|
||
* The :meth:`HttpRequest.build_absolute_uri()
|
||
<django.http.HttpRequest.build_absolute_uri>` method now handles paths
|
||
starting with ``//`` correctly.
|
||
|
||
* If :setting:`DEBUG` is ``True`` and a request raises a
|
||
:exc:`~django.core.exceptions.SuspiciousOperation`, the response will be
|
||
rendered with a detailed error page.
|
||
|
||
* The ``query_string`` argument of :class:`~django.http.QueryDict` is now
|
||
optional, defaulting to ``None``, so a blank ``QueryDict`` can now be
|
||
instantiated with ``QueryDict()`` instead of ``QueryDict(None)`` or
|
||
``QueryDict('')``.
|
||
|
||
* The ``GET`` and ``POST`` attributes of an :class:`~django.http.HttpRequest`
|
||
object are now :class:`~django.http.QueryDict`\s rather than dictionaries,
|
||
and the ``FILES`` attribute is now a ``MultiValueDict``.
|
||
This brings this class into line with the documentation and with
|
||
``WSGIRequest``.
|
||
|
||
* The :attr:`HttpResponse.charset <django.http.HttpResponse.charset>` attribute
|
||
was added.
|
||
|
||
* ``WSGIRequestHandler`` now follows RFC in converting URI to IRI, using
|
||
``uri_to_iri()``.
|
||
|
||
* The :meth:`HttpRequest.get_full_path()
|
||
<django.http.HttpRequest.get_full_path>` method now escapes unsafe characters
|
||
from the path portion of a Uniform Resource Identifier (URI) properly.
|
||
|
||
* :class:`~django.http.HttpResponse` now implements a few additional methods
|
||
like :meth:`~django.http.HttpResponse.getvalue` so that instances can be used
|
||
as stream objects.
|
||
|
||
* The new :meth:`HttpResponse.setdefault()
|
||
<django.http.HttpResponse.setdefault>` method allows setting a header unless
|
||
it has already been set.
|
||
|
||
* You can use the new :class:`~django.http.FileResponse` to stream files.
|
||
|
||
* The :func:`~django.views.decorators.http.condition` decorator for
|
||
conditional view processing now supports the ``If-unmodified-since`` header.
|
||
|
||
Tests
|
||
^^^^^
|
||
|
||
* The :class:`RequestFactory.trace() <django.test.RequestFactory>`
|
||
and :class:`Client.trace() <django.test.Client.trace>` methods were
|
||
implemented, allowing you to create ``TRACE`` requests in your tests.
|
||
|
||
* The ``count`` argument was added to
|
||
:meth:`~django.test.SimpleTestCase.assertTemplateUsed`. This allows you to
|
||
assert that a template was rendered a specific number of times.
|
||
|
||
* The new :meth:`~django.test.SimpleTestCase.assertJSONNotEqual` assertion
|
||
allows you to test that two JSON fragments are not equal.
|
||
|
||
* Added options to the :djadmin:`test` command to preserve the test database
|
||
(:djadminopt:`--keepdb`) and to run the test cases in reverse order
|
||
(:djadminopt:`--reverse`).
|
||
|
||
* Added the :attr:`~django.test.Response.resolver_match` attribute to test
|
||
client responses.
|
||
|
||
* Added several settings that allow customization of test tablespace parameters
|
||
for Oracle: :setting:`DATAFILE`, :setting:`DATAFILE_TMP`,
|
||
:setting:`DATAFILE_MAXSIZE` and :setting:`DATAFILE_TMP_MAXSIZE`.
|
||
|
||
* The :func:`~django.test.override_settings` decorator can now affect the
|
||
master router in :setting:`DATABASE_ROUTERS`.
|
||
|
||
* Added test client support for file uploads with file-like objects.
|
||
|
||
* A shared cache is now used when testing with a SQLite in-memory database when
|
||
using Python 3.4+ and SQLite 3.7.13+. This allows sharing the database
|
||
between threads.
|
||
|
||
Validators
|
||
^^^^^^^^^^
|
||
|
||
* ...
|
||
|
||
Backwards incompatible changes in 1.8
|
||
=====================================
|
||
|
||
.. warning::
|
||
|
||
In addition to the changes outlined in this section, be sure to review the
|
||
:ref:`deprecation plan <deprecation-removed-in-1.8>` for any features that
|
||
have been removed. If you haven't updated your code within the
|
||
deprecation timeline for a given feature, its removal may appear as a
|
||
backwards incompatible change.
|
||
|
||
Related object operations are run in a transaction
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Some operations on related objects such as
|
||
:meth:`~django.db.models.fields.related.RelatedManager.add()` or
|
||
:ref:`direct assignment<direct-assignment>` ran multiple data modifying
|
||
queries without wrapping them in transactions. To reduce the risk of data
|
||
corruption, all data modifying methods that affect multiple related objects
|
||
(i.e. ``add()``, ``remove()``, ``clear()``, and :ref:`direct assignment
|
||
<direct-assignment>`) now perform their data modifying queries from within a
|
||
transaction, provided your database supports transactions.
|
||
|
||
This has one backwards incompatible side effect, signal handlers triggered from
|
||
these methods are now executed within the method's transaction and any
|
||
exception in a signal handler will prevent the whole operation.
|
||
|
||
Assigning unsaved objects to relations raises an error
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Assigning unsaved objects to a :class:`~django.db.models.ForeignKey`,
|
||
:class:`~django.contrib.contenttypes.fields.GenericForeignKey`, and
|
||
:class:`~django.db.models.OneToOneField` now raises a :exc:`ValueError`.
|
||
|
||
Previously, the assignment of an unsaved object would be silently ignored.
|
||
For example::
|
||
|
||
>>> book = Book.objects.create(name="Django")
|
||
>>> book.author = Author(name="John")
|
||
>>> book.author.save()
|
||
>>> book.save()
|
||
|
||
>>> Book.objects.get(name="Django")
|
||
>>> book.author
|
||
>>>
|
||
|
||
Now, an error will be raised to prevent data loss::
|
||
|
||
>>> book.author = Author(name="john")
|
||
Traceback (most recent call last):
|
||
...
|
||
ValueError: Cannot assign "<Author: John>": "Author" instance isn't saved in the database.
|
||
|
||
Management commands that only accept positional arguments
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
If you have written a custom management command that only accepts positional
|
||
arguments and you didn't specify the
|
||
:attr:`~django.core.management.BaseCommand.args` command variable, you might
|
||
get an error like ``Error: unrecognized arguments: ...``, as variable parsing
|
||
is now based on :py:mod:`argparse` which doesn't implicitly accept positional
|
||
arguments. You can make your command backwards compatible by simply setting the
|
||
:attr:`~django.core.management.BaseCommand.args` class variable. However, if
|
||
you don't have to keep compatibility with older Django versions, it's better to
|
||
implement the new :meth:`~django.core.management.BaseCommand.add_arguments`
|
||
method as described in :doc:`/howto/custom-management-commands`.
|
||
|
||
Custom test management command arguments through test runner
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
The method to add custom arguments to the `test` management command through the
|
||
test runner has changed. Previously, you could provide an `option_list` class
|
||
variable on the test runner to add more arguments (à la :py:mod:`optparse`).
|
||
Now to implement the same behavior, you have to create an
|
||
``add_arguments(cls, parser)`` class method on the test runner and call
|
||
``parser.add_argument`` to add any custom arguments, as parser is now an
|
||
:py:class:`argparse.ArgumentParser` instance.
|
||
|
||
Model check ensures auto-generated column names are within limits specified by database
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
A field name that's longer than the column name length supported by a database
|
||
can create problems. For example, with MySQL you'll get an exception trying to
|
||
create the column, and with PostgreSQL the column name is truncated by the
|
||
database (you may see a warning in the PostgreSQL logs).
|
||
|
||
A model check has been introduced to better alert users to this scenario before
|
||
the actual creation of database tables.
|
||
|
||
If you have an existing model where this check seems to be a false positive,
|
||
for example on PostgreSQL where the name was already being truncated, simply
|
||
use :attr:`~django.db.models.Field.db_column` to specify the name that's being
|
||
used.
|
||
|
||
The check also applies to the columns generated in an implicit
|
||
``ManyToManyField.through`` model. If you run into an issue there, use
|
||
:attr:`~django.db.models.ManyToManyField.through` to create an explicit model
|
||
and then specify :attr:`~django.db.models.Field.db_column` on its column(s)
|
||
as needed.
|
||
|
||
Query relation lookups now check object types
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Querying for model lookups now checks if the object passed is of correct type
|
||
and raises a :exc:`ValueError` if not. Previously, Django didn't care if the
|
||
object was of correct type; it just used the object's related field attribute
|
||
(e.g. ``id``) for the lookup. Now, an error is raised to prevent incorrect
|
||
lookups::
|
||
|
||
>>> book = Book.objects.create(name="Django")
|
||
>>> book = Book.objects.filter(author=book)
|
||
Traceback (most recent call last):
|
||
...
|
||
ValueError: Cannot query "<Book: Django>": Must be "Author" instance.
|
||
|
||
``select_related()`` now checks given fields
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
``select_related()`` now validates that the given fields actually exist.
|
||
Previously, nonexistent fields were silently ignored. Now, an error is raised::
|
||
|
||
>>> book = Book.objects.select_related('nonexistent_field')
|
||
Traceback (most recent call last):
|
||
...
|
||
FieldError: Invalid field name(s) given in select_related: 'nonexistent_field'
|
||
|
||
The validation also makes sure that the given field is relational::
|
||
|
||
>>> book = Book.objects.select_related('name')
|
||
Traceback (most recent call last):
|
||
...
|
||
FieldError: Non-relational field given in select_related: 'name'
|
||
|
||
Default ``EmailField.max_length`` increased to 254
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
The old default 75 character ``max_length`` was not capable of storing all
|
||
possible RFC3696/5321-compliant email addresses. In order to store all
|
||
possible valid email addresses, the ``max_length`` has been increased to 254
|
||
characters. You will need to generate and apply database migrations for your
|
||
affected models (or add ``max_length=75`` if you wish to keep the length on
|
||
your current fields). A migration for
|
||
:attr:`django.contrib.auth.models.User.email` is included.
|
||
|
||
Support for PostgreSQL versions older than 9.0
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
The end of upstream support periods was reached in July 2014 for PostgreSQL 8.4.
|
||
As a consequence, Django 1.8 sets 9.0 as the minimum PostgreSQL version it
|
||
officially supports.
|
||
|
||
This also includes dropping support for PostGIS 1.3 and 1.4 as these versions
|
||
are not supported on versions of PostgreSQL later than 8.4.
|
||
|
||
Django also now requires the use of Psycopg2 version 2.0.9 or higher.
|
||
|
||
Support for MySQL versions older than 5.5
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
The end of upstream support periods was reached in January 2012 for MySQL 5.0
|
||
and December 2013 for MySQL 5.1. As a consequence, Django 1.8 sets 5.5 as the
|
||
minimum MySQL version it officially supports.
|
||
|
||
Support for Oracle versions older than 11.1
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
The end of upstream support periods was reached in July 2010 for Oracle 9.2,
|
||
January 2012 for Oracle 10.1, and July 2013 for Oracle 10.2. As a consequence,
|
||
Django 1.8 sets 11.1 as the minimum Oracle version it officially supports.
|
||
|
||
Specific privileges used instead of roles for tests on Oracle
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Earlier versions of Django granted the CONNECT and RESOURCE roles to the test
|
||
user on Oracle. These roles have been deprecated, so Django 1.8 uses the
|
||
specific underlying privileges instead. This changes the privileges required
|
||
of the main user for running tests (unless the project is configured to avoid
|
||
creating a test user). The exact privileges required now are detailed in
|
||
:ref:`Oracle notes <oracle-notes>`.
|
||
|
||
``AbstractUser.last_login`` allows null values
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
The :attr:`AbstractUser.last_login <django.contrib.auth.models.User.last_login>`
|
||
field now allows null values. Previously, it defaulted to the time when the user
|
||
was created which was misleading if the user never logged in. Please run the
|
||
database migration. If your custom user inherits from ``AbstractUser`` and you
|
||
wish to set ``last_login`` to ``NULL`` for users who haven't logged in, you can
|
||
run this query::
|
||
|
||
from django.db import models
|
||
from django.contrib.auth import get_user_model
|
||
from django.contrib.auth.models import AbstractBaseUser
|
||
|
||
UserModel = get_user_model()
|
||
if issubclass(UserModel, AbstractBaseUser):
|
||
UserModel._default_manager.filter(
|
||
last_login=models.F('date_joined')
|
||
).update(last_login=None)
|
||
|
||
:mod:`django.contrib.gis`
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
* Support for GEOS 3.1 and GDAL 1.6 has been dropped.
|
||
|
||
* Support for SpatiaLite < 2.4 has been dropped.
|
||
|
||
* GIS-specific lookups have been refactored to use the
|
||
:class:`django.db.models.Lookup` API.
|
||
|
||
* The default ``str`` representation of
|
||
:class:`~django.contrib.gis.geos.GEOSGeometry` objects has been changed from
|
||
WKT to EWKT format (including the SRID). As this representation is used in
|
||
the serialization framework, that means that ``dumpdata`` output will now
|
||
contain the SRID value of geometry objects.
|
||
|
||
Priority of context processors for ``TemplateResponse`` brought in line with ``render``
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
The :class:`~django.template.response.TemplateResponse` constructor is designed to be a
|
||
drop-in replacement for the :func:`~django.shortcuts.render` function. However,
|
||
it had a slight incompatibility, in that for ``TemplateResponse``, context data
|
||
from the passed in context dictionary could be shadowed by context data returned
|
||
from context processors, whereas for ``render`` it was the other way
|
||
around. This was a bug, and the behavior of ``render`` is more appropriate,
|
||
since it allows the globally defined context processors to be overridden locally
|
||
in the view. If you were relying on the fact context data in a
|
||
``TemplateResponse`` could be overridden using a context processor, you will
|
||
need to change your code.
|
||
|
||
Overriding ``setUpClass`` / ``tearDownClass`` in test cases
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
The decorators :func:`~django.test.override_settings` and
|
||
:func:`~django.test.modify_settings` now act at the class level when used as
|
||
class decorators. As a consequence, when overriding ``setUpClass()`` or
|
||
``tearDownClass()``, the ``super`` implementation should always be called.
|
||
|
||
Removal of ``django.contrib.formtools``
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
The formtools contrib app has been moved into a separate package.
|
||
``django.contrib.formtools`` itself has been removed. The docs provide
|
||
:ref:`migration instructions <formtools-how-to-migrate>`.
|
||
|
||
The new package is available `on Github`_ and on PyPI.
|
||
|
||
.. _on GitHub: https://github.com/django/django-formtools/
|
||
|
||
Database connection reloading between tests
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Django previously closed database connections between each test within a
|
||
``TestCase``. This is no longer the case as Django now wraps the whole
|
||
``TestCase`` within a transaction. If some of your tests relied on the old
|
||
behavior, you should have them inherit from ``TransactionTestCase`` instead.
|
||
|
||
Cleanup of the ``django.template`` namespace
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
If you've been relying on private APIs exposed in the ``django.template``
|
||
module, you may have to import them from ``django.template.base`` instead.
|
||
|
||
Also private APIs ``django.template.base.compile_string()``,
|
||
``django.template.loader.find_template()``, and
|
||
``django.template.loader.get_template_from_string()`` were removed.
|
||
|
||
Database backend API
|
||
~~~~~~~~~~~~~~~~~~~~
|
||
|
||
The following changes to the database backend API are documented to assist
|
||
those writing third-party backends in updating their code:
|
||
|
||
* The ``data_types``, ``data_types_suffix``, and
|
||
``data_type_check_constraints`` attributes have moved from the
|
||
``DatabaseCreation`` class to ``DatabaseWrapper``.
|
||
|
||
Miscellaneous
|
||
~~~~~~~~~~~~~
|
||
|
||
* ``connections.queries`` is now a read-only attribute.
|
||
|
||
* Database connections are considered equal only if they're the same object.
|
||
They aren't hashable any more.
|
||
|
||
* :class:`~django.middleware.gzip.GZipMiddleware` used to disable compression
|
||
for some content types when the request is from Internet Explorer, in order
|
||
to work around a bug in IE6 and earlier. This behavior could affect
|
||
performance on IE7 and later. It was removed.
|
||
|
||
* ``URLField.to_python`` no longer adds a trailing slash to pathless URLs.
|
||
|
||
* The :tfilter:`length` template filter now returns ``0`` for an undefined
|
||
variable, rather than an empty string.
|
||
|
||
* ``ForeignKey.default_error_message['invalid']`` has been changed from
|
||
``'%(model)s instance with pk %(pk)r does not exist.'`` to
|
||
``'%(model)s instance with %(field)s %(value)r does not exist.'`` If you are
|
||
using this message in your own code, please update the list of interpolated
|
||
parameters. Internally, Django will continue to provide the
|
||
``pk`` parameter in ``params`` for backwards compatibility.
|
||
|
||
* ``UserCreationForm.errors_messages['duplicate_username']`` is no longer used.
|
||
If you wish to customize that error message, :ref:`override it on the form
|
||
<modelforms-overriding-default-fields>` using the ``'unique'`` key in
|
||
``Meta.errors_messages['username']`` or, if you have a custom form field for
|
||
``'username'``, using the the ``'unique'`` key in its
|
||
:attr:`~django.forms.Field.error_messages` argument.
|
||
|
||
* ``AdminSite`` no longer takes an ``app_name`` argument and its ``app_name``
|
||
attribute has been removed. The application name is always ``admin`` (as
|
||
opposed to the instance name which you can still customize using
|
||
``AdminSite(name="...")``.
|
||
|
||
* The block ``usertools`` in the ``base.html`` template of
|
||
:mod:`django.contrib.admin` now requires the ``has_permission`` context
|
||
variable to be set. If you have any custom admin views that use this
|
||
template, update them to pass :meth:`AdminSite.has_permission()
|
||
<django.contrib.admin.AdminSite.has_permission>` as this new variable's
|
||
value or simply include :meth:`AdminSite.each_context(request)
|
||
<django.contrib.admin.AdminSite.each_context>` in the context.
|
||
|
||
* Internal changes were made to the :class:`~django.forms.ClearableFileInput`
|
||
widget to allow more customization. The undocumented ``url_markup_template``
|
||
attribute was removed in favor of ``template_with_initial``.
|
||
|
||
* For consistency with other major vendors, the ``en_GB`` locale now has Monday
|
||
as the first day of the week.
|
||
|
||
* Seconds have been removed from any locales that had them in ``TIME_FORMAT``,
|
||
``DATETIME_FORMAT``, or ``SHORT_DATETIME_FORMAT``.
|
||
|
||
* The default max size of the Oracle test tablespace has increased from 300M
|
||
(or 200M, before 1.7.2) to 500M.
|
||
|
||
* :func:`~django.core.urlresolvers.reverse` and
|
||
:func:`~django.core.urlresolvers.reverse_lazy` now return Unicode strings
|
||
instead of byte strings.
|
||
|
||
* The ``CacheClass`` shim has been removed from all cache backends.
|
||
These aliases were provided for backwards compatibility with Django 1.3.
|
||
If you are still using them, please update your project to use the real
|
||
class name found in the :setting:`BACKEND <CACHES-BACKEND>` key of the
|
||
:setting:`CACHES` setting.
|
||
|
||
* By default, :ref:`call_command <call-command>` now always skips the check
|
||
framework (unless you pass it ``skip_checks=False``).
|
||
|
||
* When iterating over lines, :class:`~django.core.files.File` now uses
|
||
`universal newlines`_. The following are recognized as ending a line: the
|
||
Unix end-of-line convention ``'\n'``, the Windows convention ``'\r\n'``, and
|
||
the old Macintosh convention ``'\r'``.
|
||
|
||
.. _universal newlines: https://www.python.org/dev/peps/pep-0278
|
||
|
||
* The Memcached cache backends ``MemcachedCache`` and ``PyLibMCCache`` will
|
||
delete a key if ``set()`` fails. This is necessary to ensure the ``cache_db``
|
||
session store always fetches the most current session data.
|
||
|
||
* Private APIs ``override_template_loaders`` and ``override_with_test_loader``
|
||
in ``django.test.utils`` were removed. Override ``TEMPLATES`` with
|
||
``override_settings`` instead.
|
||
|
||
* Warnings from the MySQL database backend are no longer converted to
|
||
exceptions when :setting:`DEBUG` is ``True``.
|
||
|
||
* :class:`~django.http.HttpRequest` now has a simplified ``repr`` (e.g.
|
||
``<WSGIRequest: GET '/somepath/'>``). This won't change the behavior of
|
||
the :class:`~django.views.debug.SafeExceptionReporterFilter` class.
|
||
|
||
* Class-based views that use :class:`~django.views.generic.edit.ModelFormMixin`
|
||
will raise an :exc:`~django.core.exceptions.ImproperlyConfigured` exception
|
||
when both the ``fields`` and ``form_class`` attributes are specified.
|
||
Previously, ``fields`` was silently ignored.
|
||
|
||
* When following redirects, the test client now raises
|
||
:exc:`~django.test.client.RedirectCycleError` if it detects a loop or hits a
|
||
maximum redirect limit (rather than passing silently).
|
||
|
||
* Translatable strings set as the ``default`` parameter of the field are cast
|
||
to concrete strings later, so the return type of ``Field.get_default()`` is
|
||
different in some cases. There is no change to default values which are the
|
||
result of a callable.
|
||
|
||
.. _deprecated-features-1.8:
|
||
|
||
Features deprecated in 1.8
|
||
==========================
|
||
|
||
Loading ``cycle`` and ``firstof`` template tags from ``future`` library
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Django 1.6 introduced ``{% load cycle from future %}`` and
|
||
``{% load firstof from future %}`` syntax for forward compatibility of the
|
||
:ttag:`cycle` and :ttag:`firstof` template tags. This syntax is now deprecated
|
||
and will be removed in Django 2.0. You can simply remove the
|
||
``{% load ... from future %}`` tags.
|
||
|
||
``django.conf.urls.patterns()``
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
In the olden days of Django, it was encouraged to reference views as strings
|
||
in ``urlpatterns``::
|
||
|
||
urlpatterns = patterns('',
|
||
url('^$', 'myapp.views.myview'),
|
||
)
|
||
|
||
and Django would magically import ``myapp.views.myview`` internally and turn
|
||
the string into a real function reference. In order to reduce repetition when
|
||
referencing many views from the same module, the ``patterns()`` function takes
|
||
a required initial ``prefix`` argument which is prepended to all
|
||
views-as-strings in that set of ``urlpatterns``::
|
||
|
||
urlpatterns = patterns('myapp.views',
|
||
url('^$', 'myview'),
|
||
url('^other/$', 'otherview'),
|
||
)
|
||
|
||
In the modern era, we have updated the tutorial to instead recommend importing
|
||
your views module and referencing your view functions (or classes) directly.
|
||
This has a number of advantages, all deriving from the fact that we are using
|
||
normal Python in place of "Django String Magic": the errors when you mistype a
|
||
view name are less obscure, IDEs can help with autocompletion of view names,
|
||
etc.
|
||
|
||
So these days, the above use of the ``prefix`` arg is much more likely to be
|
||
written (and is better written) as::
|
||
|
||
from myapp import views
|
||
|
||
urlpatterns = patterns('',
|
||
url('^$', views.myview),
|
||
url('^other/$', views.otherview),
|
||
)
|
||
|
||
Thus ``patterns()`` serves little purpose and is a burden when teaching new users
|
||
(answering the newbie's question "why do I need this empty string as the first
|
||
argument to ``patterns()``?"). For these reasons, we are deprecating it.
|
||
Updating your code is as simple as ensuring that ``urlpatterns`` is a list of
|
||
:func:`django.conf.urls.url` instances. For example::
|
||
|
||
from django.conf.urls import url
|
||
from myapp import views
|
||
|
||
urlpatterns = [
|
||
url('^$', views.myview),
|
||
url('^other/$', views.otherview),
|
||
]
|
||
|
||
Passing a string as ``view`` to :func:`~django.conf.urls.url`
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Related to the previous item, referencing views as strings in the ``url()``
|
||
function is deprecated. Pass the callable view as described in the previous
|
||
section instead.
|
||
|
||
Template-related settings
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
As a consequence of the multiple template engines refactor, several settings
|
||
are deprecated in favor of :setting:`TEMPLATES`:
|
||
|
||
* ``ALLOWED_INCLUDE_ROOTS``
|
||
* ``TEMPLATE_CONTEXT_PROCESSORS``
|
||
* ``TEMPLATE_DIRS``
|
||
* ``TEMPLATE_LOADERS``
|
||
* ``TEMPLATE_STRING_IF_INVALID``
|
||
|
||
``django.core.context_processors``
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Built-in template context processors have been moved to
|
||
``django.template.context_processors``.
|
||
|
||
``django.test.SimpleTestCase.urls``
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
The attribute :attr:`SimpleTestCase.urls <django.test.SimpleTestCase.urls>`
|
||
for specifying URLconf configuration in tests has been deprecated and will be
|
||
removed in Django 2.0. Use :func:`@override_settings(ROOT_URLCONF=...)
|
||
<django.test.override_settings>` instead.
|
||
|
||
``prefix`` argument to :func:`~django.conf.urls.i18n.i18n_patterns`
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Related to the previous item, the ``prefix`` argument to
|
||
:func:`django.conf.urls.i18n.i18n_patterns` has been deprecated. Simply pass a
|
||
list of :func:`django.conf.urls.url` instances instead.
|
||
|
||
Using an incorrect count of unpacked values in the :ttag:`for` template tag
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Using an incorrect count of unpacked values in :ttag:`for` tag will raise an
|
||
exception rather than fail silently in Django 2.0.
|
||
|
||
Passing a dotted path to :func:`~django.core.urlresolvers.reverse()` and :ttag:`url`
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Reversing URLs by Python path is an expensive operation as it causes the
|
||
path being reversed to be imported. This behavior has also resulted in a
|
||
`security issue`_. Use :ref:`named URL patterns <naming-url-patterns>`
|
||
for reversing instead.
|
||
|
||
If you are using :mod:`django.contrib.sitemaps`, add the ``name`` argument to
|
||
the ``url`` that references :func:`django.contrib.sitemaps.views.sitemap`::
|
||
|
||
from django.contrib.sitemaps.views import sitemap
|
||
|
||
url(r'^sitemap\.xml$', sitemap, {'sitemaps': sitemaps},
|
||
name='django.contrib.sitemaps.views.sitemap')
|
||
|
||
to ensure compatibility when reversing by Python path is removed in Django 2.0.
|
||
|
||
Similarly for GIS sitemaps, add ``name='django.contrib.gis.sitemaps.views.kml'``
|
||
or ``name='django.contrib.gis.sitemaps.views.kmz'``.
|
||
|
||
.. _security issue: https://www.djangoproject.com/weblog/2014/apr/21/security/#s-issue-unexpected-code-execution-using-reverse
|
||
|
||
Aggregate methods and modules
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
The ``django.db.models.sql.aggregates`` and
|
||
``django.contrib.gis.db.models.sql.aggregates`` modules (both private API), have
|
||
been deprecated as ``django.db.models.aggregates`` and
|
||
``django.contrib.gis.db.models.aggregates`` are now also responsible
|
||
for SQL generation. The old modules will be removed in Django 2.0.
|
||
|
||
If you were using the old modules, see :doc:`Query Expressions
|
||
</ref/models/expressions>` for instructions on rewriting custom aggregates
|
||
using the new stable API.
|
||
|
||
The following methods and properties of ``django.db.models.sql.query.Query``
|
||
have also been deprecated and the backwards compatibility shims will be removed
|
||
in Django 2.0:
|
||
|
||
* ``Query.aggregates``, replaced by ``annotations``.
|
||
* ``Query.aggregate_select``, replaced by ``annotation_select``.
|
||
* ``Query.add_aggregate()``, replaced by ``add_annotation()``.
|
||
* ``Query.set_aggregate_mask()``, replaced by ``set_annotation_mask()``.
|
||
* ``Query.append_aggregate_mask()``, replaced by ``append_annotation_mask()``.
|
||
|
||
Extending management command arguments through ``Command.option_list``
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Management commands now use :py:mod:`argparse` instead of :py:mod:`optparse` to
|
||
parse command-line arguments passed to commands. This also means that the way
|
||
to add custom arguments to commands has changed: instead of extending the
|
||
``option_list`` class list, you should now override the
|
||
:meth:`~django.core.management.BaseCommand.add_arguments` method and add
|
||
arguments through ``argparse.add_argument()``. See
|
||
:ref:`this example <custom-commands-options>` for more details.
|
||
|
||
``django.core.management.NoArgsCommand``
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
The class :class:`~django.core.management.NoArgsCommand` is now deprecated and
|
||
will be removed in Django 2.0. Use :class:`~django.core.management.BaseCommand`
|
||
instead, which takes no arguments by default.
|
||
|
||
Listing all migrations in a project
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
The ``--list`` option of the :djadmin:`migrate` management command is
|
||
deprecated and will be removed in Django 2.0. Use :djadmin:`showmigrations`
|
||
instead.
|
||
|
||
``cache_choices`` option of ``ModelChoiceField`` and ``ModelMultipleChoiceField``
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
:class:`~django.forms.ModelChoiceField` and
|
||
:class:`~django.forms.ModelMultipleChoiceField` took an undocumented, untested
|
||
option ``cache_choices``. This cached querysets between multiple renderings of
|
||
the same ``Form`` object. This option is subject to an accelerated deprecation
|
||
and will be removed in Django 1.9.
|
||
|
||
``django.template.resolve_variable()``
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
The function has been informally marked as "Deprecated" for some time. Replace
|
||
``resolve_variable(path, context)`` with
|
||
``django.template.Variable(path).resolve(context)``.
|
||
|
||
``django.contrib.webdesign``
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
It provided the :ttag:`lorem` template tag which is now included in the
|
||
built-in tags. Simply remove ``'django.contrib.webdesign'`` from
|
||
:setting:`INSTALLED_APPS` and ``{% load webdesign %}`` from your templates.
|
||
|
||
``error_message`` argument to ``django.forms.RegexField``
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
It provided backwards compatibility for pre-1.0 code, but its functionality is
|
||
redundant. Use ``Field.error_messages['invalid']`` instead.
|
||
|
||
Old :tfilter:`unordered_list` syntax
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
An older (pre-1.0), more restrictive and verbose input format for the
|
||
:tfilter:`unordered_list` template filter has been deprecated::
|
||
|
||
``['States', [['Kansas', [['Lawrence', []], ['Topeka', []]]], ['Illinois', []]]]``
|
||
|
||
Using the new syntax, this becomes::
|
||
|
||
``['States', ['Kansas', ['Lawrence', 'Topeka'], 'Illinois']]``
|
||
|
||
``django.forms.Field._has_changed()``
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Rename this method to :meth:`~django.forms.Field.has_changed` by removing the
|
||
leading underscore. The old name will still work until Django 2.0.
|
||
|
||
``django.utils.html.remove_tags()`` and ``removetags`` template filter
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
``django.utils.html.remove_tags()`` as well as the template filter
|
||
``removetags`` have been deprecated as they cannot guarantee safe output. Their
|
||
existence is likely to lead to their use in security-sensitive contexts where
|
||
they are not actually safe.
|
||
|
||
The unused and undocumented ``django.utils.html.strip_entities()`` function has
|
||
also been deprecated.
|
||
|
||
``is_admin_site`` argument to ``django.contrib.auth.views.password_reset()``
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
It's a legacy option that should no longer be necessary.
|
||
|
||
``SubfieldBase``
|
||
~~~~~~~~~~~~~~~~
|
||
|
||
``django.db.models.fields.subclassing.SubfieldBase`` has been deprecated and
|
||
will be removed in Django 2.0. Historically, it was used to handle fields where
|
||
type conversion was needed when loading from the database, but it was not used
|
||
in ``.values()`` calls or in aggregates. It has been replaced with
|
||
:meth:`~django.db.models.Field.from_db_value`. Note that the new approach does
|
||
not call the :meth:`~django.db.models.Field.to_python` method on assignment
|
||
as was the case with ``SubfieldBase``.
|
||
|
||
``django.utils.checksums``
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
The ``django.utils.checksums`` module has been deprecated and will be removed
|
||
in Django 2.0. The functionality it provided (validating checksum using the
|
||
Luhn algorithm) was undocumented and not used in Django. The module has been
|
||
moved to the `django-localflavor`_ package (version 1.1+).
|
||
|
||
.. _django-localflavor: https://pypi.python.org/pypi/django-localflavor
|
||
|
||
``django.contrib.admin.helpers.InlineAdminForm.original_content_type_id``
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
The ``original_content_type_id`` attribute on ``InlineAdminForm`` has been
|
||
deprecated and will be removed in Django 2.0. Historically, it was used
|
||
to construct the "view on site" URL. This URL is now accessible using the
|
||
``absolute_url`` attribute of the form.
|
||
|
||
``django.views.generic.edit.FormMixin.get_form()``’s ``form_class`` argument
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
``FormMixin`` subclasses that override the ``get_form()`` method should make
|
||
sure to provide a default value for the ``form_class`` argument since it's
|
||
now optional.
|
||
|
||
``current_app`` argument of template-related APIs
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
The following functions and classes will no longer accept a ``current_app``
|
||
parameter to set an URL namespace in Django 2.0:
|
||
|
||
* ``django.shortcuts.render()``
|
||
* ``django.template.Context()``
|
||
* ``django.template.RequestContext()``
|
||
* ``django.template.response.TemplateResponse()``
|
||
|
||
Set ``request.current_app`` instead, where ``request`` is the first argument
|
||
to these functions or classes. If you're using a plain ``Context``, use a
|
||
``RequestContext`` instead.
|
||
|
||
``dictionary`` and ``context_instance`` arguments of rendering functions
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
The following functions will no longer accept the ``dictionary`` and
|
||
``context_instance`` parameters in Django 2.0:
|
||
|
||
* ``django.shortcuts.render()``
|
||
* ``django.shortcuts.render_to_response()``
|
||
* ``django.template.loader.render_to_string()``
|
||
|
||
Use the ``context`` parameter instead. When ``dictionary`` is passed as a
|
||
positional argument, which is the most common idiom, no changes are needed.
|
||
|
||
There is no replacement for ``context_instance``. All data must be passed to
|
||
templates through the ``context`` dict.
|
||
|
||
``dirs`` argument of template-finding functions
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
The following functions will no longer accept a ``dirs`` parameter to override
|
||
``TEMPLATE_DIRS`` in Django 2.0:
|
||
|
||
* :func:`django.template.loader.get_template()`
|
||
* :func:`django.template.loader.select_template()`
|
||
* :func:`django.shortcuts.render()`
|
||
* :func:`django.shortcuts.render_to_response()`
|
||
|
||
The parameter didn't work consistently across different template loaders and
|
||
didn't work for included templates.
|
||
|
||
``django.template.loader.BaseLoader``
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
``django.template.loader.BaseLoader`` was renamed to
|
||
``django.template.loaders.base.Loader``. If you've written a custom template
|
||
loader that inherits ``BaseLoader``, you must inherit ``Loader`` instead.
|
||
|
||
``django.test.utils.TestTemplateLoader``
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Private API ``django.test.utils.TestTemplateLoader`` is deprecated in favor of
|
||
``django.template.loaders.locmem.Loader``.
|
||
|
||
``qn`` replaced by ``compiler``
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
In previous Django versions, various internal ORM methods (mostly ``as_sql``
|
||
methods) accepted a ``qn`` (for "quote name") argument, which was a reference
|
||
to a function that quoted identifiers for sending to the database. In Django
|
||
1.8, that argument has been renamed to ``compiler`` and is now a full
|
||
``SQLCompiler`` instance. For backwards-compatibility, calling a
|
||
``SQLCompiler`` instance performs the same name-quoting that the ``qn``
|
||
function used to. However, this backwards-compatibility shim is immediately
|
||
deprecated: you should rename your ``qn`` arguments to ``compiler``, and call
|
||
``compiler.quote_name_unless_alias(...)`` where you previously called
|
||
``qn(...)``.
|
||
|
||
Default value of ``RedirectView.permanent``
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
The default value of the
|
||
:attr:`RedirectView.permanent <django.views.generic.base.RedirectView.permanent>`
|
||
attribute will change from ``True`` to ``False`` in Django 1.9.
|
||
|
||
Using ``AuthenticationMiddleware`` without ``SessionAuthenticationMiddleware``
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
:class:`django.contrib.auth.middleware.SessionAuthenticationMiddleware` was
|
||
added in Django 1.7. In Django 1.7.2, its functionality was moved to
|
||
``auth.get_user()`` and, for backwards compatibility, enabled only if
|
||
``'django.contrib.auth.middleware.SessionAuthenticationMiddleware'`` appears in
|
||
:setting:`MIDDLEWARE_CLASSES`.
|
||
|
||
In Django 2.0, session verification will be enabled regardless of whether or not
|
||
``SessionAuthenticationMiddleware`` is enabled (at which point
|
||
``SessionAuthenticationMiddleware`` will have no significance). You can add it
|
||
to your ``MIDDLEWARE_CLASSES`` sometime before then to opt-in. Please read the
|
||
:ref:`upgrade considerations <session-invalidation-on-password-change>` first.
|
||
|
||
``django.contrib.sitemaps.FlatPageSitemap``
|
||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
``django.contrib.sitemaps.FlatPageSitemap`` has moved to
|
||
``django.contrib.flatpages.sitemaps.FlatPageSitemap``. The old import location
|
||
is deprecated and will be removed in Django 1.9.
|
||
|
||
Model ``Field.related``
|
||
~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Private attribute ``django.db.models.Field.related`` is deprecated in favor
|
||
of ``Field.rel``. The latter is an instance of
|
||
``django.db.models.fields.related.ForeignObjectRel`` which replaces
|
||
``django.db.models.related.RelatedObject``. The ``django.db.models.related``
|
||
module has been removed and the ``Field.related`` attribute will be removed in
|
||
Django 2.0.
|
||
|
||
.. removed-features-1.8:
|
||
|
||
Features removed in 1.8
|
||
=======================
|
||
|
||
These features have reached the end of their deprecation cycle and so have been
|
||
removed in Django 1.8 (please see the :ref:`deprecation timeline
|
||
<deprecation-removed-in-1.8>` for more details):
|
||
|
||
* ``django.contrib.comments`` is removed.
|
||
|
||
* The following transaction management APIs are removed:
|
||
|
||
- ``TransactionMiddleware``
|
||
- the decorators and context managers ``autocommit``, ``commit_on_success``,
|
||
and ``commit_manually``, defined in ``django.db.transaction``
|
||
- the functions ``commit_unless_managed`` and ``rollback_unless_managed``,
|
||
also defined in ``django.db.transaction``
|
||
- the ``TRANSACTIONS_MANAGED`` setting
|
||
|
||
* The :ttag:`cycle` and :ttag:`firstof` template tags auto-escape their
|
||
arguments.
|
||
|
||
* The ``SEND_BROKEN_LINK_EMAILS`` setting is removed.
|
||
|
||
* ``django.middleware.doc.XViewMiddleware`` is removed.
|
||
|
||
* The ``Model._meta.module_name`` alias is removed.
|
||
|
||
* The backward compatible shims introduced to rename ``get_query_set``
|
||
and similar queryset methods are removed. This affects the following classes:
|
||
``BaseModelAdmin``, ``ChangeList``, ``BaseCommentNode``,
|
||
``GenericForeignKey``, ``Manager``, ``SingleRelatedObjectDescriptor`` and
|
||
``ReverseSingleRelatedObjectDescriptor``.
|
||
|
||
* The backward compatible shims introduced to rename the attributes
|
||
``ChangeList.root_query_set`` and ``ChangeList.query_set`` are removed.
|
||
|
||
* ``django.views.defaults.shortcut`` and ``django.conf.urls.shortcut`` are
|
||
removed.
|
||
|
||
* Support for the Python Imaging Library (PIL) module is removed.
|
||
|
||
* The following private APIs are removed:
|
||
|
||
- ``django.db.backend``
|
||
- ``django.db.close_connection()``
|
||
- ``django.db.backends.creation.BaseDatabaseCreation.set_autocommit()``
|
||
- ``django.db.transaction.is_managed()``
|
||
- ``django.db.transaction.managed()``
|
||
|
||
* ``django.forms.widgets.RadioInput`` is removed.
|
||
|
||
* The module ``django.test.simple`` and the class
|
||
``django.test.simple.DjangoTestSuiteRunner`` are removed.
|
||
|
||
* The module ``django.test._doctest`` is removed.
|
||
|
||
* The ``CACHE_MIDDLEWARE_ANONYMOUS_ONLY`` setting is removed.
|
||
|
||
* Usage of the hard-coded *Hold down "Control", or "Command" on a Mac, to select
|
||
more than one.* string to override or append to user-provided ``help_text`` in
|
||
forms for ``ManyToMany`` model fields is not be performed by Django anymore
|
||
either at the model or forms layer.
|
||
|
||
* The ``Model._meta.get_(add|change|delete)_permission`` methods are removed.
|
||
|
||
* The session key ``django_language`` is no longer read for backwards
|
||
compatibility.
|
||
|
||
* Geographic Sitemaps are removed
|
||
(``django.contrib.gis.sitemaps.views.index`` and
|
||
``django.contrib.gis.sitemaps.views.sitemap``).
|
||
|
||
* ``django.utils.html.fix_ampersands``, the ``fix_ampersands`` template filter,
|
||
and ``django.utils.html.clean_html`` are removed.
|