django-planet
Feb. 2, 2023

Standout features in Django 4.2

in blog Django Beats
original entry Standout features in Django 4.2

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 a lookup_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!

Django really flies on Fly.io

You already know Django makes it easier to build better apps. Well now Fly.io makes it easier to _deploy_ those apps and move them closer to your users making it faster for them too!

Deploy a Django app today!