Django 4.0: What's New
Django 4.0 is here. While not a revolutionary release, it brings practical improvements that make Django development smoother. Let’s explore.
Major Changes
Redis Cache Backend
Native Redis support—no third-party package needed:
# settings.py
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.redis.RedisCache",
"LOCATION": "redis://127.0.0.1:6379",
}
}
Multiple servers:
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.redis.RedisCache",
"LOCATION": [
"redis://127.0.0.1:6379",
"redis://127.0.0.1:6380",
],
}
}
This was one of the most requested features.
Template-Based Form Rendering
Forms now use templates instead of Python strings:
# Old approach (still works)
{{ form.as_p }}
# New approach: Template-based
{{ form }} # Uses default template
# Or specify custom template
{{ form.render }}
Built-in template packs:
django/forms/div.html(new default)django/forms/p.htmldjango/forms/table.html
Customize per-form:
class MyForm(forms.Form):
template_name = "my_forms/custom.html"
# or
template_name_label = "my_forms/label.html"
Timezone Changes
USE_L10N is now True by default, and USE_TZ must be True.
# Django 3.x
USE_TZ = True # Optional
# Django 4.0
USE_TZ = True # Required for timezone support
If you don’t want timezone support, explicitly set it.
Improved startproject Template
New projects include:
# settings.py improvements
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# .gitignore included
# Better directory structure
New Features
scrypt Password Hasher
More secure password hashing:
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.ScryptPasswordHasher',
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
# ...
]
Scrypt is memory-hard, making brute-force attacks more expensive.
Functional Unique Constraints
Unique constraints on expressions:
from django.db import models
from django.db.models.functions import Lower
class Author(models.Model):
name = models.CharField(max_length=100)
class Meta:
constraints = [
models.UniqueConstraint(
Lower('name'),
name='unique_lower_name'
),
]
Case-insensitive unique names!
UniqueConstraint Improvements
Conditional unique constraints:
class Article(models.Model):
title = models.CharField(max_length=200)
is_published = models.BooleanField(default=False)
class Meta:
constraints = [
models.UniqueConstraint(
fields=['title'],
condition=models.Q(is_published=True),
name='unique_published_title'
),
]
Only published articles need unique titles.
asgiref Updates
Better async support:
from asgiref.sync import sync_to_async, async_to_sync
# Wrap sync function for async context
@sync_to_async
def get_user_sync(user_id):
return User.objects.get(id=user_id)
# Use in async view
async def my_view(request):
user = await get_user_sync(1)
return JsonResponse({'name': user.name})
Prefetch Improvements
# Filter prefetched related objects
from django.db.models import Prefetch
authors = Author.objects.prefetch_related(
Prefetch(
'books',
queryset=Book.objects.filter(published=True).order_by('-pub_date')[:5]
)
)
aget, acreate, adelete
Async ORM methods (still limited):
# Async get
user = await User.objects.aget(id=1)
# Async create
article = await Article.objects.acreate(title="Hello", author=user)
# Async delete
await article.adelete()
Note: Full async ORM is still in progress.
Breaking Changes
Python 3.8+ Required
Django 4.0 drops Python 3.6 and 3.7:
# Minimum versions
Python >= 3.8
PostgreSQL >= 10
MySQL >= 5.7
SQLite >= 3.9.0
Removed Features
Removed after deprecation period:
request.is_ajax()(check HTTP_ACCEPT header instead)django.contrib.postgres.fields.JSONField(usemodels.JSONField)default_app_configin__init__.py
# Old (removed)
if request.is_ajax():
# ...
# New
if request.accepts("application/json"):
# ...
URL Patterns
# Old (still works but discouraged)
from django.conf.urls import url
# New (preferred)
from django.urls import path, re_path
Upgrade Guide
Step 1: Python Version
python --version # Must be 3.8+
Step 2: Check Deprecation Warnings
python -Wd manage.py test
Step 3: Update Dependencies
pip install Django==4.0
pip install --upgrade psycopg2 celery djangorestframework
Step 4: Run Tests
python manage.py test
Step 5: Address Breaking Changes
# Update request.is_ajax() usage
# Update JSONField imports
# Ensure USE_TZ is set
Should You Upgrade?
From Django 3.2 LTS
Not urgent. 3.2 is supported until April 2024.
Django 3.2 LTS → Stable, supported
Django 4.0 → Latest features
Django 4.2 LTS → Wait for LTS (April 2023)
For New Projects
Yes, start with 4.0:
- Latest features
- Longer support runway
- Native Redis caching
For Existing Projects
Consider:
- Is Python 3.8+ available?
- Are all dependencies compatible?
- Do you need new features?
Final Thoughts
Django 4.0 is evolutionary, not revolutionary. Native Redis caching and template-based forms are the highlights. The async story continues to improve.
If you’re on 3.2 LTS, no rush. If starting new, use 4.0.
Incremental progress, stable foundations.