Optimizing Django Performance: A Complete Checklist
# Optimizing Django Performance
Slow applications frustrate users and hurt your bottom line. Here's a comprehensive checklist to make your Django app faster.
## Database Optimization
### 1. Use select_related and prefetch_related
The N+1 query problem is the most common performance killer.
```python
# BAD: N+1 queries
for order in Order.objects.all():
print(order.user.email) # Query for each order!
# GOOD: Single query with JOIN
for order in Order.objects.select_related('user'):
print(order.user.email)
# For many-to-many relationships
products = Product.objects.prefetch_related('categories', 'tags')
```
### 2. Add Database Indexes
```python
class Product(models.Model):
name = models.CharField(max_length=200, db_index=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
created_at = models.DateTimeField(db_index=True)
class Meta:
indexes = [
models.Index(fields=['category', 'created_at']),
models.Index(fields=['name', 'is_active']),
]
```
### 3. Use only() and defer()
Fetch only the fields you need:
```python
# Fetch only specific fields
products = Product.objects.only('id', 'name', 'price')
# Exclude heavy fields
products = Product.objects.defer('description', 'long_content')
```
### 4. Bulk Operations
```python
# BAD: Individual inserts
for item in items:
Product.objects.create(**item)
# GOOD: Bulk insert
Product.objects.bulk_create([
Product(**item) for item in items
])
# GOOD: Bulk update
Product.objects.filter(category='old').update(category='new')
```
## Caching Strategies
### 1. Database Query Caching
```python
from django.core.cache import cache
def get_featured_products():
key = 'featured_products'
products = cache.get(key)
if products is None:
products = list(Product.objects.filter(
is_featured=True
).select_related('category')[:10])
cache.set(key, products, 60 * 15) # 15 minutes
return products
```
### 2. View Caching
```python
from django.views.decorators.cache import cache_page
@cache_page(60 * 15) # 15 minutes
def product_list(request):
products = Product.objects.all()
return render(request, 'products.html', {'products': products})
```
### 3. Template Fragment Caching
```django
{% load cache %}
{% cache 500 sidebar request.user.id %}
<!-- Expensive sidebar content -->
{% endcache %}
```
### 4. Redis Configuration
```python
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
}
}
}
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
SESSION_CACHE_ALIAS = 'default'
```
## Query Analysis
### Django Debug Toolbar
Install and use it in development:
```python
INSTALLED_APPS = [
...
'debug_toolbar',
]
MIDDLEWARE = [
'debug_toolbar.middleware.DebugToolbarMiddleware',
...
]
```
### Query Logging
```python
LOGGING = {
'version': 1,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'level': 'DEBUG',
'handlers': ['console'],
},
},
}
```
## Frontend Optimization
### 1. Static File Compression
```python
# settings.py
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
```
### 2. Image Optimization
```python
from PIL import Image
def optimize_image(image_path, max_size=(800, 800), quality=85):
img = Image.open(image_path)
img.thumbnail(max_size, Image.LANCZOS)
img.save(image_path, optimize=True, quality=quality)
```
### 3. Lazy Loading
```html
<img src="placeholder.jpg"
data-src="actual-image.jpg"
loading="lazy"
alt="Product">
```
## Application Settings
### 1. Persistent Database Connections
```python
DATABASES = {
'default': {
...
'CONN_MAX_AGE': 600, # 10 minutes
}
}
```
### 2. Session Backend
```python
# Use cache-based sessions
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'
```
### 3. Disable Debug in Production
```python
DEBUG = False
```
## Async Views (Django 4.0+)
```python
async def async_product_list(request):
products = await sync_to_async(list)(
Product.objects.filter(is_active=True)[:20]
)
return render(request, 'products.html', {'products': products})
```
## Performance Checklist
### Database
- [ ] Add indexes to frequently queried fields
- [ ] Use select_related/prefetch_related
- [ ] Avoid N+1 queries
- [ ] Use bulk operations
- [ ] Enable connection pooling
### Caching
- [ ] Cache expensive queries
- [ ] Use Redis for session storage
- [ ] Implement template fragment caching
- [ ] Set appropriate cache timeouts
### Frontend
- [ ] Minify CSS/JS
- [ ] Optimize images
- [ ] Use CDN for static files
- [ ] Enable browser caching
- [ ] Lazy load images
### Infrastructure
- [ ] Use production-grade server (Gunicorn)
- [ ] Enable gzip compression
- [ ] Use connection pooling (PgBouncer)
- [ ] Monitor with APM tools
## Measuring Performance
Tools to use:
- **Django Debug Toolbar**: Development queries
- **New Relic / Datadog**: Production monitoring
- **Lighthouse**: Frontend performance
- **Apache Bench**: Load testing
```bash
# Load test with Apache Bench
ab -n 1000 -c 100 http://localhost:8000/products/
```
## Conclusion
Performance optimization is iterative. Measure first, optimize the bottlenecks, and measure again. Don't optimize prematurely—focus on real problems.
Need performance-optimized templates? Check our marketplace!