What’s New in Django 3.2?
TL;DR: Major features in 3.2 are automatic AppConfig
discovery, customizing type of auto-created primary keys, functional indexes, and new decorators for the admin site.
It has been around three years since Django 2.2 was first released The developer team promises that update is incredibly important for the future of Django, but it was also mentioned that no major features have been added. You might be wondering why it was released then. Since Django 2.2 was released in 2019, a lot has changed in software development, Python, and of course in Django.
In the past, I’ve worked entirely on LTS versions. Staying on the LTS version is always a trade-off between new features and stability, but there’s no right or wrong answer as to which you should be on. If you’re not in the habit of upgrading often, I would recommend sticking with the LTS releases. If you enjoy taking risks, moving fast and breaking stuff, then the regular releases are for you. That’s not to say they’re worse, just not supported for as long, so when security issues hit, you’re on your own.
In this article, I’ll be listing out the changes made in the latest version which I found most interesting or most useful to my development life. The full list of all changes can be found in the release notes for 3.0, 3.1 and 3.2.
1. Automatic AppConfig
discovery
Most pluggable applications define an AppConfig
subclass in an apps.py
submodule. Many define a default_app_config
variable pointing to this class in their __init__.py
.
When the apps.py
submodule exists and defines a single AppConfig
subclass, Django now uses that configuration automatically, so you can remove default_app_config
.
default_app_config
made it possible to declare only the application’s path in INSTALLED_APPS
(e.g. 'django.contrib.admin'
) rather than the app config’s path (e.g. 'django.contrib.admin.apps.AdminConfig'
). It was introduced for backwards-compatibility with the former style, with the intent to switch the ecosystem to the latter, but the switch didn’t happen.
With automatic AppConfig
discovery, default_app_config
is no longer needed. As a consequence, it’s deprecated.
2. Customizing type of auto-created primary keys
When defining a model, if no field in a model is defined with primary_key=True
an implicit primary key is added. The type of this implicit primary key can now be controlled via the DEFAULT_AUTO_FIELD
setting and AppConfig.default_auto_field
attribute. No more needing to override primary keys in all models.
Maintaining the historical behavior, the default value for DEFAULT_AUTO_FIELD
is AutoField
. Starting with 3.2 new projects are generated with DEFAULT_AUTO_FIELD
set to BigAutoField
. Also, new apps are generated with AppConfig.default_auto_field
set to BigAutoField
. In a future Django release the default value of DEFAULT_AUTO_FIELD
will be changed to BigAutoField
.
To avoid unwanted migrations in the future, either explicitly set DEFAULT_AUTO_FIELD
to AutoField
:
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
or configure it on a per-app basis:
from django.apps import AppConfigclass MyAppConfig(AppConfig):
default_auto_field = 'django.db.models.AutoField'
name = 'my_app'
or on a per-model basis:
from django.db import modelsclass MyModel(models.Model):
id = models.AutoField(primary_key=True)
In anticipation of the changing default, a system check will provide a warning if you do not have an explicit setting for DEFAULT_AUTO_FIELD
.
When changing the value of DEFAULT_AUTO_FIELD
, migrations for the primary key of existing auto-created through tables cannot be generated currently. See the DEFAULT_AUTO_FIELD
docs for details on migrating such tables.
3. Functional indexes
The new *expressions
positional argument of Index()
enables creating functional indexes on expressions and database functions. For example:
from django.db import models
from django.db.models import F, Index, Value
from django.db.models.functions import Lower, Upper
class MyModel(models.Model):
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
height = models.IntegerField()
weight = models.IntegerField() class Meta:
indexes = [
Index(
Lower('first_name'),
Upper('last_name').desc(),
name='first_last_name_idx',
),
Index(
F('height') / (F('weight') + Value(5)),
name='calc_idx',
),
]
Functional indexes are added to models using the Meta.indexes
option.
4. New decorators for the admin site
The new display()
decorator allows for easily adding options to custom display functions that can be used with list_display
or readonly_fields
.
Likewise, the new action()
decorator allows for easily adding options to action functions that can be used with actions
.
Using the @display
decorator has the advantage that it is now possible to use the @property
decorator when needing to specify attributes on the custom method. Prior to this it was necessary to use the property()
function instead after assigning the required attributes to the method.
Using decorators has the advantage that these options are more discoverable as they can be suggested by completion utilities in code editors. They are merely a convenience and still set the same attributes on the functions under the hood.