Top 10 things you need to know about Django 4.0

Ali Aref
4 min readDec 18, 2021

--

Django 4.0 Released On 7th December 2021

Python compatibility

Django 4.0 supports Python 3.8, 3.9, and 3.10. It’s highly recommend and only officially support the latest release of each series.

Attention:
The Django 3.2.x series was the last to support Python 3.6 and 3.7

What you need to know about Django 4.0

1- You can use Scrypt password hasher

The new scrypt password hasher is more secure and recommended over PBKDF2. However, it’s not the default as it requires OpenSSL 1.1+ and more memory.

scrypt is similar to PBKDF2 and bcrypt in utilizing a set number of iterations to slow down brute-force attacks. However, because PBKDF2 and bcrypt do not require a lot of memory, attackers with sufficient resources can launch large-scale parallel attacks in order to speed up the attacking process. scrypt is specifically designed to use more memory compared to other password-based key derivation functions in order to limit the amount of parallelism an attacker can use.

To use scrypt as your default storage algorithm,
modify PASSWORD_HASHERS to list ScryptPasswordHasher first. That is, in your settings file:

PASSWORD_HASHERS = [
'django.contrib.auth.hashers.ScryptPasswordHasher',
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
'django.contrib.auth.hashers.Argon2PasswordHasher',
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
]

2- Requests and Responses

The SecurityMiddleware now adds the Cross-Origin Opener Policy header with a value of 'same-origin' to prevent cross-origin popups from sharing the same browsing context. You can prevent this header from being added by setting the SECURE_CROSS_ORIGIN_OPENER_POLICY setting to None.

3- SecurityMiddleware no longer sets the X-XSS-Production header

The SecurityMiddleware no longer sets the X-XSS-Protection header if the SECURE_BROWSER_XSS_FILTER setting is True. The setting is removed.

Most modern browsers don’t honor the X-XSS-Protection HTTP header. You can use Content-Security-Policy without allowing 'unsafe-inline' scripts instead.

If you want to support legacy browsers and set the header, use this line in a custom middleware:

response.headers.setdefault('X-XSS-Protection', '1; mode=block')

4- CSRF-TOKEN

CSRF protection now consults the Origin header, if present. To facilitate this, some changes to the CSRF_TRUSTED_ORIGINS setting are required.

5- Generic Views

DeleteView now uses FormMixin to handle POST requests. As a consequence, any custom deletion logic in delete() handlers should be moved to form_valid(), or a shared helper method, if required.

6- Forms

ModelChoiceField now includes the provided value in the params argument of a raised ValidationError for the invalid_choice error message. This allows custom error messages to use the %(value)s placeholder.

BaseFormSet now renders non-form errors with an additional class of nonform to help distinguish them from form-specific errors.

BaseFormSet now allows customizing the widget used when deleting forms via can_delete by setting the deletion_widget attribute or overriding get_deletion_widget() method.

7- Models

New QuerySet.contains(obj) method returns whether the queryset contains the given object. This tries to perform the query in the simplest and fastest way possible.

The new precision argument of the Round() database function allows specifying the number of decimal places after rounding.

QuerySet.bulk_create() now sets the primary key on objects when using SQLite 3.35+.

DurationField now supports multiplying and dividing by scalar values on SQLite.

QuerySet.bulk_update() now returns the number of objects updated.

The new Expression.empty_result_set_value attribute allows specifying a value to return when the function is used over an empty result set.

The skip_locked argument of QuerySet.select_for_update() is now allowed on MariaDB 10.6+.

Lookup expressions may now be used in QuerySet annotations, aggregations, and directly in filters.

The new default argument for built-in aggregates allows specifying a value to be returned when the queryset (or grouping) contains no entries, rather than None.

8- Functional unique constraints

The new *expressions positional argument of UniqueConstraint() enables creating functional unique constraints on expressions and database functions. For example:

from django.db import models
from django.db.models import UniqueConstraint
from django.db.models.functions import Lower
class MyModel(models.Model):
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
class Meta:
constraints = [
UniqueConstraint(
Lower('first_name'),
Lower('last_name').desc(),
name='first_last_name_unique',
),
]

Functional unique constraints are added to models using the Meta.constraints option.

9- Templates

The Template based form rendering
Now Forms, Formsets, and ErrorList rendered using the template engine to enhance customization. See the new render(), get_context(), and template_name for Form and formset rendering for Formset.

floatformat template filter now allows using the u suffix to force disabling localization.

10- Management Commands

The runserver management command now supports the --skip-checks option.

On PostgreSQL, dbshell now supports specifying a password file.

The shell command now respects sys.__interactivehook__ at startup. This allows loading shell history between interactive sessions. As a consequence, readline is no longer loaded if running in isolated mode.

The new BaseCommand.suppressed_base_arguments attribute allows suppressing unsupported default command options in the help output.

The new startapp --exclude and startproject --exclude options allow excluding directories from the template.

--

--