Back to Blog

Optimizing Django Performance: A Complete Checklist

admin
November 22, 2025 3 min read
222 views
Speed up your Django application with these proven optimization techniques covering database, caching, and frontend.

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

The N+1 query problem is the most common performance killer.

# 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

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:

# Fetch only specific fields
products = Product.objects.only('id', 'name', 'price')

# Exclude heavy fields
products = Product.objects.defer('description', 'long_content')

4. Bulk Operations

# 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

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

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

{% load cache %}

{% cache 500 sidebar request.user.id %}
    <!-- Expensive sidebar content -->
{% endcache %}

4. Redis Configuration

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:

INSTALLED_APPS = [
    ...
    'debug_toolbar',
]

MIDDLEWARE = [
    'debug_toolbar.middleware.DebugToolbarMiddleware',
    ...
]

Query Logging

LOGGING = {
    'version': 1,
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'level': 'DEBUG',
            'handlers': ['console'],
        },
    },
}

Frontend Optimization

1. Static File Compression

# settings.py
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'

2. Image Optimization

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

<img src="placeholder.jpg"
     data-src="actual-image.jpg"
     loading="lazy"
     alt="Product">

Application Settings

1. Persistent Database Connections

DATABASES = {
    'default': {
        ...
        'CONN_MAX_AGE': 600,  # 10 minutes
    }
}

2. Session Backend

# Use cache-based sessions
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'

3. Disable Debug in Production

DEBUG = False

Async Views (Django 4.0+)

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
# 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!

Comments (0)

Please login to leave a comment.

No comments yet. Be the first to comment!