August 1, 2018
Welcome to Django 2.1!
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 2.0 or earlier. We’ve dropped some features that have reached the end of their deprecation cycle, and we’ve begun the deprecation process for some features.
See the Upgrading Django to a newer version guide if you’re updating an existing project.
Django 2.1 supports Python 3.5, 3.6, and 3.7. Django 2.0 is the last version to support Python 3.4. We highly recommend and only officially support the latest release of each series.
A “view” permission is added to the model Meta.default_permissions
. The new permissions will be
created automatically when running migrate
.
This allows giving users read-only access to models in the admin.
ModelAdmin.has_view_permission()
is new. The implementation is backwards
compatible in that there isn’t a need to assign the “view” permission to allow
users who have the “change” permission to edit objects.
There are a couple of backwards incompatible considerations.
django.contrib.admin
¶ModelAdmin.search_fields
now accepts any lookup such as
field__exact
.ModelAdmin.delete_queryset()
method allows customizing the
deletion process of the “delete selected objects” action.ModelAdmin.sortable_by
attribute and
ModelAdmin.get_sortable_by()
method allow limiting the columns that
can be sorted in the change list page.admin_order_field
attribute for elements in
ModelAdmin.list_display
may now be a query expression.ModelAdmin.get_deleted_objects()
method allows customizing
the deletion process of the delete view and the “delete selected” action.actions.html
, change_list_results.html
, date_hierarchy.html
,
pagination.html
, prepopulated_fields_js.html
, search_form.html
,
and submit_line.html
templates can now be overridden per app or
per model (besides overridden
globally).change_list_object_tools.html
and
change_form_object_tools.html
templates.InlineModelAdmin.has_add_permission()
is now passed the parent object
as the second positional argument, obj
.django.contrib.auth
¶createsuperuser
now gives a prompt to allow bypassing the
AUTH_PASSWORD_VALIDATORS
checks.django.contrib.gis
¶GEOSGeometry.buffer_with_style()
method is a version of
buffer()
that allows customizing the style of the
buffer.OpenLayersWidget
is now based on
OpenLayers 4.6.5 (previously 3.20.1).django.contrib.sessions
¶SESSION_COOKIE_SAMESITE
setting to set the SameSite
cookie flag on session cookies.touch()
method of the low-level
cache API updates the timeout of cache keys.CSRF_COOKIE_SAMESITE
setting to set the SameSite
cookie flag on CSRF cookies.ImageField
now renders with the HTML attribute
accept="image/*"
.get_supported_language_variant()
function.pt_BR
strings use pt
translations.inspectdb --include-views
option allows creating models
for database views.BaseCommand
class now uses a custom help
formatter so that the standard options like --verbosity
or --settings
appear last in the help output, giving a more prominent position to subclassed
command’s options.functools.partialmethod
objects..pyc
files.__init_subclass__()
from PEP 487.BinaryField
may now be set to editable=True
if you wish to include
it in model forms.Chr
,
Left
,
LPad
,
LTrim
,
Ord
,
Repeat
,
Replace
,
Right
,
RPad
,
RTrim
, and
Trim
.TruncWeek
function truncates
DateField
and
DateTimeField
to the Monday of a week.QuerySet.order_by()
and distinct(*fields)
now support using field transforms.BooleanField
can now be null=True
. This is
encouraged instead of NullBooleanField
, which will
likely be deprecated in the future.QuerySet.explain()
method displays the database’s execution
plan of a queryset’s query.QuerySet.raw()
now supports prefetch_related()
.HttpRequest.get_full_path_info()
.samesite
argument to HttpResponse.set_cookie()
to allow
setting the SameSite
cookie flag.as_attachment
argument for FileResponse
sets the Content-Disposition
header to make the browser ask if the user
wants to download the file. FileResponse
also tries to set the
Content-Type
and Content-Length
headers where appropriate.json_script
filter safely outputs a Python object as JSON,
wrapped in a <script>
tag, ready for use with JavaScript.Client
support for 307 and 308 redirects.Client
now serializes a request data
dictionary as JSON if content_type='application/json'
. You can customize
the JSON encoder with test client’s json_encoder
parameter.SimpleTestCase.assertWarnsMessage()
method is a simpler
version of assertWarnsRegex()
.This section describes changes that may be needed in third-party database backends.
NotImplementedError
to
django.db.NotSupportedError
.allow_sliced_subqueries
database feature flag to
allow_sliced_subqueries_with_in
.DatabaseOperations.distinct_sql()
now requires an additional params
argument and returns a tuple of SQL and parameters instead of an SQL string.DatabaseFeatures.introspected_boolean_field_type
is changed from a method
to a property.django.contrib.gis
¶The end of upstream support for MySQL 5.5 is December 2018. Django 2.1 supports MySQL 5.6 and higher.
The end of upstream support for PostgreSQL 9.3 is September 2018. Django 2.1 supports PostgreSQL 9.4 and higher.
BCryptPasswordHasher
from the default PASSWORD_HASHERS
setting¶If you used bcrypt with Django 1.4 or 1.5 (before BCryptSHA256PasswordHasher
was added in Django 1.6), you might have some passwords that use the
BCryptPasswordHasher
hasher.
You can check if that’s the case like this:
from django.contrib.auth import get_user_model
User = get_user_model()
User.objects.filter(password__startswith='bcrypt$$')
If you want to continue to allow those passwords to be used, you’ll
have to define the PASSWORD_HASHERS
setting (if you don’t already)
and include 'django.contrib.auth.hashers.BCryptPasswordHasher'
.
wrap_label
widget template context variable¶To fix the lack of <label>
when using RadioSelect
and
CheckboxSelectMultiple
with MultiWidget
, the wrap_label
context
variable now appears as an attribute of each option. For example, in a custom
input_option.html
template, change {% if wrap_label %}
to
{% if widget.wrap_label %}
.
SameSite
cookies¶The cookies used for django.contrib.sessions
, django.contrib.messages
,
and Django’s CSRF protection now set the SameSite
flag to Lax
by
default. Browsers that respect this flag won’t send these cookies on
cross-origin requests. If you rely on the old behavior, set the
SESSION_COOKIE_SAMESITE
and/or CSRF_COOKIE_SAMESITE
setting to None
.
With the new “view” permission, existing custom admin forms may raise errors
when a user doesn’t have the change permission because the form might access
nonexistent fields. Fix this by overriding ModelAdmin.get_form()
and
checking if the user has the “change” permissions and returning the default
form if not:
class MyAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
if not self.has_change_permission(request, obj):
return super().get_form(request, obj, **kwargs)
return CustomForm
If you have a custom permission with a codename of the form
view_<modelname>
, the new view permission handling in the admin will allow
view access to the changelist and detail pages for those models. If this is
unwanted, you must change your custom permission codename.
mysqlclient
is increased from 1.3.3 to
1.3.7.Set-Cookie
’s Expires
directive is changed to
follow RFC 7231#section-7.1.1.1 instead of Netscape’s cookie standard.
Hyphens present in dates like Tue, 25-Dec-2018 22:26:13 GMT
are removed.
This change should be merely cosmetic except perhaps for antiquated browsers
that don’t parse the new format.allowed_hosts
is now a required argument of private API
django.utils.http.is_safe_url()
.multiple
attribute rendered by the
SelectMultiple
widget now uses HTML5 boolean syntax
rather than XHTML’s multiple="multiple"
.<br>
. This is incompatible within XHTML, although some
widgets already used aspects of HTML5 such as boolean attributes.SelectDateWidget
’s empty options is
changed from 0 to an empty string, which mainly may require some adjustments
in tests that compare HTML.User.has_usable_password()
and the
is_password_usable()
function no longer
return False
if the password is None
or an empty string, or if the
password uses a hasher that’s not in the PASSWORD_HASHERS
setting.
This undocumented behavior was a regression in Django 1.6 and prevented users
with such passwords from requesting a password reset. Audit your code to
confirm that your usage of these APIs don’t rely on the old behavior..pyc
files, you might need to delete
them if you’re working in a mixed Python 2 and Python 3 environment.None
as a JSONField
lookup
value now matches objects that have the specified key and a null value rather
than objects that don’t have the key.field-box
is renamed to fieldBox
to prevent
conflicts with the class given to model fields named “box”.actions.html
, change_list_results.html
,
date_hierarchy.html
, pagination.html
, prepopulated_fields_js.html
,
search_form.html
, and submit_line.html
templates can now be
overridden per app or per model, you may need to rename existing templates
with those names that were written for a different purpose.QuerySet.raw()
now caches its results like regular querysets. Use
iterator()
if you don’t want caching.allow_relation()
method is called in more cases.
Improperly written routers may need to be updated accordingly.--settings
and --pythonpath
arguments.django.db.models.sql.constants.QUERY_TERMS
constant is
removed. The get_lookup()
and get_lookups()
methods
of the Lookup Registration API may be
suitable alternatives. Compared to the QUERY_TERMS
constant, they allow
your code to also account for any custom lookups that have been registered.py-bcrypt
is removed as it’s unmaintained. Use bcrypt instead.ForceRHR
GIS function is deprecated in favor of the new
ForcePolygonCW
function.django.utils.http.cookie_date()
is deprecated in favor of
http_date()
, which follows the format of the latest
RFC.{% load staticfiles %}
and {% load admin_static %}
are deprecated
in favor of {% load static %}
, which works the same.django.contrib.staticfiles.templatetags.static()
is deprecated in favor
of django.templatetags.static.static()
.InlineModelAdmin.has_add_permission()
methods that don’t
accept obj
as the second positional argument will be removed in Django
3.0.These features have reached the end of their deprecation cycle and are removed in Django 2.1. See Features deprecated in 1.11 for details, including how to remove usage of these features.
contrib.auth.views.login()
, logout()
, password_change()
,
password_change_done()
, password_reset()
, password_reset_done()
,
password_reset_confirm()
, and password_reset_complete()
are removed.extra_context
parameter of contrib.auth.views.logout_then_login()
is removed.django.test.runner.setup_databases()
is removed.django.utils.translation.string_concat()
is removed.django.core.cache.backends.memcached.PyLibMCCache
no longer supports
passing pylibmc
behavior settings as top-level attributes of OPTIONS
.host
parameter of django.utils.http.is_safe_url()
is removed.{% include %}
template
tag is removed.DatabaseIntrospection.get_indexes()
is removed.authenticate()
method of authentication backends requires request
as the first positional argument.django.db.models.permalink()
decorator is removed.USE_ETAGS
setting is removed. CommonMiddleware
and
django.utils.cache.patch_response_headers()
no longer set ETags.Model._meta.has_auto_field
attribute is removed.url()
’s support for inline flags in regular expression groups ((?i)
,
(?L)
, (?m)
, (?s)
, and (?u)
) is removed.Widget.render()
methods without the renderer
argument
is removed.Jul 27, 2022