Django 4.1: Async ORM and Form Rendering
Django 4.1 continues the async journey, adding more ORM methods and introducing a new form rendering system. Here’s what’s new.
Async ORM Expansion
New Async Methods
Django 4.1 adds async versions of common QuerySet methods:
# Previously available
await Model.objects.aget(pk=1)
await Model.objects.acreate(name='test')
await Model.objects.adelete()
# New in 4.1
await Model.objects.acontains(obj)
await Model.objects.aexists()
await Model.objects.abulk_create([...])
await Model.objects.abulk_update([...])
# Iteration
async for item in Model.objects.filter(active=True):
process(item)
Async Context Managers
async def my_view(request):
async with transaction.atomic():
obj = await Model.objects.acreate(name='test')
await AnotherModel.objects.acreate(ref=obj)
return JsonResponse({'id': obj.id})
Async Iteration
async def fetch_all_users():
users = []
async for user in User.objects.filter(is_active=True):
users.append({
'id': user.id,
'email': user.email,
})
return users
Form Rendering Improvements
Template-Based Rendering
Forms now render via templates instead of Python code:
# Built-in templates:
# - django/forms/div.html (new default)
# - django/forms/p.html
# - django/forms/table.html
Custom Form Templates
class ContactForm(forms.Form):
template_name = 'forms/contact.html'
name = forms.CharField()
email = forms.EmailField()
message = forms.CharField(widget=forms.Textarea)
<!-- forms/contact.html -->
<form method="post" class="my-form">
{% csrf_token %}
{% for field in form %}
<div class="form-group{% if field.errors %} has-error{% endif %}">
{{ field.label_tag }}
{{ field }}
{% if field.help_text %}
<small class="help-text">{{ field.help_text }}</small>
{% endif %}
{% for error in field.errors %}
<span class="error">{{ error }}</span>
{% endfor %}
</div>
{% endfor %}
<button type="submit">Send</button>
</form>
Field Template Customization
class StyledCharField(forms.CharField):
template_name = 'forms/fields/styled_input.html'
Form Group Templates
FORM_RENDERER = 'django.forms.renderers.TemplatesSetting'
# Add to TEMPLATES
TEMPLATES = [{
...
'DIRS': [BASE_DIR / 'templates'],
...
}]
Validation Improvements
Model Constraint Validation
class Reservation(models.Model):
start_date = models.DateField()
end_date = models.DateField()
class Meta:
constraints = [
models.CheckConstraint(
check=models.Q(end_date__gt=models.F('start_date')),
name='end_after_start',
violation_error_message='End date must be after start date.'
),
]
Now constraint violations show your custom message instead of a generic error.
Async Validation
class AsyncValidator:
async def __call__(self, value):
exists = await User.objects.filter(email=value).aexists()
if exists:
raise ValidationError('Email already in use')
Admin Improvements
Dark Mode Support
Django admin now respects prefers-color-scheme:
/* admin/css/base.css */
@media (prefers-color-scheme: dark) {
:root {
--body-bg: #121212;
--body-fg: #ffffff;
/* ... */
}
}
Works automatically. No configuration needed.
Improved List Filters
class ArticleAdmin(admin.ModelAdmin):
list_filter = [
('created_at', admin.DateFieldListFilter),
('status', admin.ChoicesFieldListFilter),
('author', admin.RelatedOnlyFieldListFilter),
]
InlineModelAdmin Improvements
class CommentInline(admin.TabularInline):
model = Comment
extra = 1
show_change_link = True # Link to full edit form
Security Updates
CSRF Cookie Changes
# More secure defaults
CSRF_COOKIE_MASKED = True # New in 4.1
CSRF tokens are now masked to prevent BREACH attacks.
Password Hasher Updates
# New Argon2id hasher
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.Argon2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
...
]
Database Features
Collation Support
from django.db.models import CharField
from django.db.models.functions import Collate
# Case-insensitive comparison
Author.objects.annotate(
name_ci=Collate('name', 'nocase')
).filter(name_ci='john')
More Expression Support
from django.db.models import F, Value
from django.db.models.functions import Concat
# Concat with F expressions improved
Article.objects.annotate(
full_title=Concat(
F('category__name'),
Value(': '),
F('title')
)
)
Migration Notes
Breaking Changes
# CSRF_TRUSTED_ORIGINS now requires scheme
CSRF_TRUSTED_ORIGINS = [
'https://example.com', # Include https://
]
# Form rendering changed
# Old Python-based rendering may need updates if customized
Deprecations
# Using positional arguments in some functions deprecated
# These will be removed in Django 5.0
Upgrade Checklist
- Check CSRF_TRUSTED_ORIGINS: Add schemes (https://)
- Test form rendering: Custom form templates may need updates
- Update dependencies: Ensure compatibility
- Test async code: New async methods may change behavior
Should You Upgrade?
From 4.0
Yes. Straightforward upgrade with good improvements.
From 3.2 LTS
Consider waiting. 3.2 is supported until April 2024. 4.2 LTS comes in April 2023.
Django 3.2 LTS → Stable until 2024
Django 4.0 → Supported until April 2023
Django 4.1 → Current release
Django 4.2 LTS → Coming April 2023 (wait for this if patient)
Final Thoughts
Django 4.1 is iterative progress. The async ORM expansion is welcome, form template rendering is long overdue, and dark mode admin is a nice touch.
Not revolutionary, but solid.
Incremental improvements that make the framework better.