Django 5.0 brings database-level computed columns and streamlined form rendering. Python 3.10+ enables modern syntax and performance improvements. At ZIRA Software, Django 5.0 simplifies complex database operations across our Python projects.
Installation
pip install Django>=5.0
# Verify installation
python -c "import django; print(django.VERSION)"
Database-Generated Columns
# models.py
from django.db import models
from django.db.models import F
from django.db.models.functions import Lower, Concat
class Product(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
price = models.DecimalField(max_digits=10, decimal_places=2)
discount_percent = models.IntegerField(default=0)
# Computed column - stored in database
full_name = models.GeneratedField(
expression=Concat('first_name', models.Value(' '), 'last_name'),
output_field=models.CharField(max_length=201),
db_persist=True, # Stored column (not virtual)
)
# Computed discount price
discounted_price = models.GeneratedField(
expression=F('price') * (1 - F('discount_percent') / 100.0),
output_field=models.DecimalField(max_digits=10, decimal_places=2),
db_persist=True,
)
# Virtual column (computed on read)
search_name = models.GeneratedField(
expression=Lower('full_name'),
output_field=models.CharField(max_length=201),
db_persist=False, # Virtual - computed each query
)
-- Generated SQL (PostgreSQL)
CREATE TABLE product (
id SERIAL PRIMARY KEY,
first_name VARCHAR(100),
last_name VARCHAR(100),
price DECIMAL(10, 2),
discount_percent INTEGER DEFAULT 0,
full_name VARCHAR(201) GENERATED ALWAYS AS (first_name || ' ' || last_name) STORED,
discounted_price DECIMAL(10, 2) GENERATED ALWAYS AS (price * (1 - discount_percent / 100.0)) STORED,
search_name VARCHAR(201) GENERATED ALWAYS AS (LOWER(first_name || ' ' || last_name)) VIRTUAL
);
Field Groups in Forms
# forms.py
from django import forms
class CheckoutForm(forms.Form):
# Shipping address group
shipping_name = forms.CharField(max_length=100)
shipping_address = forms.CharField(max_length=200)
shipping_city = forms.CharField(max_length=100)
shipping_zip = forms.CharField(max_length=20)
# Billing address group
billing_name = forms.CharField(max_length=100)
billing_address = forms.CharField(max_length=200)
billing_city = forms.CharField(max_length=100)
billing_zip = forms.CharField(max_length=20)
class Meta:
field_groups = {
'shipping': ['shipping_name', 'shipping_address', 'shipping_city', 'shipping_zip'],
'billing': ['billing_name', 'billing_address', 'billing_city', 'billing_zip'],
}
<!-- template.html -->
<form method="post">
{% csrf_token %}
<fieldset>
<legend>Shipping Address</legend>
{{ form.shipping_name.as_field_group }}
{{ form.shipping_address.as_field_group }}
{{ form.shipping_city.as_field_group }}
{{ form.shipping_zip.as_field_group }}
</fieldset>
<fieldset>
<legend>Billing Address</legend>
{{ form.billing_name.as_field_group }}
{{ form.billing_address.as_field_group }}
{{ form.billing_city.as_field_group }}
{{ form.billing_zip.as_field_group }}
</fieldset>
<button type="submit">Complete Order</button>
</form>
Simplified Form Rendering
# Django 5.0 - New as_field_group template method
class ContactForm(forms.Form):
name = forms.CharField()
email = forms.EmailField()
message = forms.CharField(widget=forms.Textarea)
# Custom template for field rendering
template_name_div = 'forms/custom_div.html'
<!-- templates/forms/custom_div.html -->
<div class="form-field {% if field.errors %}has-error{% endif %}">
<label for="{{ field.id_for_label }}" class="form-label">
{{ field.label }}
{% if field.field.required %}<span class="required">*</span>{% endif %}
</label>
{{ field }}
{% if field.help_text %}
<small class="form-help">{{ field.help_text }}</small>
{% endif %}
{% for error in field.errors %}
<span class="form-error">{{ error }}</span>
{% endfor %}
</div>
Async ORM Improvements
# views.py
from django.http import JsonResponse
async def dashboard_stats(request):
# Parallel async queries
import asyncio
users_count, orders_total, active_products = await asyncio.gather(
User.objects.acount(),
Order.objects.filter(status='completed').aaggregate(
total=Sum('amount')
),
Product.objects.filter(active=True).acount(),
)
return JsonResponse({
'users': users_count,
'revenue': orders_total['total'],
'products': active_products,
})
# Async iteration
async def get_recent_orders(request):
orders = []
async for order in Order.objects.filter(
created_at__gte=timezone.now() - timedelta(days=7)
).select_related('customer')[:50]:
orders.append({
'id': order.id,
'customer': order.customer.name,
'total': str(order.total),
})
return JsonResponse({'orders': orders})
Facet Filters in Admin
# admin.py
from django.contrib import admin
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
list_display = ['name', 'category', 'price', 'in_stock']
list_filter = ['category', 'in_stock', 'created_at']
# Django 5.0 - Show facet counts
show_facets = admin.ShowFacets.ALWAYS
# Facets show count next to each filter option:
# Category: Electronics (42) | Clothing (28) | Home (15)
Model Default Values
# models.py
from django.db.models.functions import Now
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
# Database-level default
created_at = models.DateTimeField(db_default=Now())
# Complex default expression
slug = models.SlugField(
db_default=Lower(Replace('title', Value(' '), Value('-')))
)
status = models.CharField(
max_length=20,
db_default=Value('draft'), # Database default
)
Choice Enumeration Improvements
from django.db import models
class Order(models.Model):
class Status(models.TextChoices):
PENDING = 'pending', 'Pending'
PROCESSING = 'processing', 'Processing'
SHIPPED = 'shipped', 'Shipped'
DELIVERED = 'delivered', 'Delivered'
# Django 5.0 - Group choices
__empty__ = '(Select status)'
status = models.CharField(
max_length=20,
choices=Status.choices,
default=Status.PENDING,
)
Conclusion
Django 5.0 with database-generated columns and improved forms enhances developer productivity. Database-level computations improve query performance and data consistency.
Building Django applications? Contact ZIRA Software for Python development services.