This post is about the new Django 4.2 release. Itโs got some neat things in it and Mariusz Felisiak shares his favorite highlights. Django on Fly.io is pretty sweet! Check it out: you can be up and running on Fly.io in just minutes.
After 8 months of work by over 200! contributors ๐, the first alpha version of Django 4.2 is out! This is a long-term support release (LTS) with extended support until April 2026, so 3 more years.
The final release should be issued in early April 2023, so now is the best time to take a peek ๐ at a “farrago” of new features shipped to this magnificent release ๐ฆ. I’d like to take a moment and share my personal favorites of Django 4.2 goodies.
psycopg
version 3 support
Django 4.2 supports psycopg
version 3.1.8+ which is the new implementation of the most popular and the richest PostgreSQL adapter for Python. The best part is that there is no need to change the ENGINE
as the built-in django.db.backends.postgresql
backend supports both libraries, psycopg2
and psycopg
. It’s enough to install psycopg
in your environment:
python -m pip install "psycopg>=3.1.8"
psycopg
prefers server-side parameter binding which improves performance and memory usage. It’s disabled by default in Django as it causes hiccups in some cases e.g. parameters passed to expressions in SELECT
and GROUP BY
clauses are not recognized as the same values which can cause grouping errors. Even though support for server-side binding cursors is still experimental it’s worth trying. If you want to use it, set the server_side_binding
option in your DATABASES
configuration:
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
# ...
"OPTIONS": {
"server_side_binding": True,
},
},
}
psycopg
also provides asynchronous connections and cursors which should take Django async support to the next level in the future ๐ฎ.
Comments on columns and tables
Database comments on columns and tables are really helpful for keeping your database schema maintainable. It also helps create a bridge ๐ค between developers and people with direct access to the database like database administrators or data scientists.
11 years after creating the ticket ๐๏ธ, Django 4.2 added support for table and column comments via the new Field.db_comment
and Meta.db_table_comment
options (for all database backends included with Django except SQLite). The migrations framework will propagate comments to your tables metadata. For example:
from django.db import models
class Product(models.Model):
name = models.CharField(max_length=511, db_comment="Product name")
deleted = models.BooleanField(
default=False,
db_comment=(
"Soft delete. When value is `True`, product is not visible "
"for users. This is useful for storing data about products "
"that are no longer for sale."
),
)
class Meta:
db_table_comment = "Available products"
In general, table and column comments are not for application users, however storing them in the model description allows you to create a single place where our database schema is managed. As such, it covers another place where you can use the Django ORM instead of writing raw SQL statements.
Lookups on field instances
Registering lookups on Field
instances can be really helpful, especially if you are familiar with the Lookup API
:
A lookup is a query expression with a left-hand side,
lhs
; a right-hand side,rhs
; and alookup_name
that is used to produce a boolean comparison between
Django < 4.2 only allowed registering lookups on Field
classes, e.g. __abs
lookup for IntegerFied
. Django 4.2 takes the use of specialized lookup to the next and extremely flexible level when you can have a different set of lookup for each field in each model. This allows for creating reusable hooks and can reduce the number of annotations used when filtering a queryset. Let’s assume you have a shop system, with a Product
model and you want to filter out expired products.
from django.db import models
class Product(models.Model):
name = models.CharField(max_length=511)
best_before = models.DateTimeField()
...
You can do this by comparing the best_before
field with the current date:
>>> from django.utils import timezone
>>> Product.objects.filter(best_before__lt=timezone.now())
<QuerySet [<Product: Milk>]>
In Django 4.2, you can now create a lookup IsOverdue
:
from django.db import models
from django.db.models.functions import Now
class IsOverdue(models.Lookup):
lookup_name = "is_overdue"
prepare_rhs = False
def as_sql(self, compiler, connection):
if not isinstance(self.rhs, bool):
raise ValueError(
"The QuerySet value for an is_overdue lookup "
"must be True or False."
)
sql, params = self.process_lhs(compiler, connection)
now_sql, now_params = compiler.compile(Now())
if self.rhs:
return f"{sql} < {now_sql}", (*params, *now_params)
else:
return f"{sql} >= {now_sql}", (*params, *now_params)
register it on the Product.best_before
(which is a field instance) by using register_lookup()
:
Product._meta.get_field("best_before").register_lookup(IsOverdue)
and use it as a handy shortcut:
>>> Product.objects.filter(best_before__is_overdue=True)
<QuerySet [<Product: Milk>]>
Closing Thoughts
Here we dug a little deeper on just 3 of my favorite improvements in the Django 4.2 release. These features all happen to be related to database support and I consider them solid quality of life improvements. Of course, there are a lot of other fixes and improvements in this release and I strongly encourage you to check the release notes. A couple other features worth mentioning are the accessibility improvements in the admin site and forms, and the constantly improving asynchronous support.
All in all, Django 4.2 is another solid release of one of the most popular web frameworks. What enhancements or fixes are you most looking forward to?
Give it a spin and share!