django-silk is a library that lets you profile your Django application’s performance, showing you exactly where time is being spent, down to the database queries and view functions.

Here’s a simple Django app and a urls.py to demonstrate:

# myapp/views.py
from django.shortcuts import render
from django.http import HttpResponse
import time

def slow_view(request):
    time.sleep(0.5)  # Simulate some work
    return HttpResponse("This view was slow!")

def fast_view(request):
    return HttpResponse("This view was fast!")

# myproject/urls.py
from django.contrib import admin
from django.urls import path
from myapp.views import slow_view, fast_view

urlpatterns = [
    path('admin/', admin.site.urls),
    path('slow/', slow_view),
    path('fast/', fast_view),
]

To use django-silk, you’ll need to install it and add it to your INSTALLED_APPS:

pip install django-silk

Then, in your settings.py:

INSTALLED_APPS = [
    # ... other apps
    'silk',
    'myapp', # your app
]

MIDDLEWARE = [
    # ... other middleware
    'silk.middleware.SilkyMiddleware',
]

After running migrations (python manage.py migrate), you can access the Silk dashboard by visiting /silk/ in your browser (e.g., http://127.0.0.1:8000/silk/). When you navigate to /slow/ in your application, you’ll see an entry appear in the Silk dashboard. Clicking on it reveals a detailed breakdown of the request.

The most surprising thing about django-silk is that it profiles everything by default, including static file serving and requests from your browser’s favicon.ico. You can and should filter these out.

The core of what django-silk does is intercepting requests and responses via Django’s middleware. For each request, it starts a timer and records all subsequent actions: view function execution, template rendering, and database queries. It attaches a unique request ID to each request, allowing you to trace all associated database queries and other events within that specific request’s context.

You can control what django-silk profiles using configuration in your settings.py. For instance, to ignore requests for static files and the favicon, you’d add:

SILKY_IGNORE_URLS = [
    r'^/static/.*',
    r'/favicon.ico$',
]
SILKY_ENABLE_PROFILER = True # Ensure profiling is enabled
SILKY_PROFILER_ARGUMENTS = {
    'మెమరీ': False, # Disable memory profiling for less overhead
}

The SILKY_IGNORE_URLS setting is a list of regular expressions. Any URL matching one of these patterns will not be profiled, significantly reducing noise in your dashboard. SILKY_ENABLE_PROFILER must be True to activate detailed profiling. SILKY_PROFILER_ARGUMENTS allows fine-grained control over the underlying cProfile module, here disabling memory profiling to reduce overhead.

The dashboard provides several views:

  • Requests: A list of all profiled requests, sortable by time, URL, and duration.
  • SQL: A consolidated view of all SQL queries executed across all requests, showing their frequency and total execution time.
  • Models: An overview of model usage, indicating which models were accessed and how often.
  • View Functions: Breakdown of time spent within your view functions.

When you click into a specific request, you get a detailed view:

  • Summary: Overall request duration, HTTP method, status code, and the originating IP.
  • View: The specific view function that handled the request and how long it took.
  • SQL Queries: A list of all database queries made during that request, including their individual execution times and the query itself. This is often the most valuable section for identifying performance bottlenecks.
  • Templates: If templates were rendered, this section shows which ones and the time spent rendering them.
  • Other: Any other intercepted events, such as external HTTP requests made by your application.

The most important metric in django-silk is the "Time Spent" column for individual SQL queries within a request. A query that takes 100ms, when repeated 10 times in a loop within a view, will make that view incredibly slow, and django-silk will clearly highlight this. You can then click on a query to see its exact SQL statement and the stack trace leading to its execution.

To optimize a slow query identified by django-silk, you would typically analyze the query itself and the context in which it’s executed. If a query appears frequently or takes too long, you might need to:

  1. Add database indexes: If silk shows a query scanning large tables without an index, adding one can dramatically speed it up. For example, if a query SELECT ... FROM myapp_mymodel WHERE user_id = 5 is slow, adding an index on myapp_mymodel.user_id can help. This is done via Django’s db_index=True on model fields or explicit Meta.indexes.
  2. Optimize ORM usage: Avoid N+1 query problems by using select_related or prefetch_related. If silk shows many similar queries for related objects, these methods are your solution. For example, if myapp_user.posts.all() in a loop triggers many queries, changing it to myapp_user.posts.all().select_related('author') (if 'author' is a ForeignKey on the Post model) or myapp_user.posts.all().prefetch_related('comments') can reduce queries.
  3. Denormalize or cache: For read-heavy operations, consider denormalizing data or implementing caching mechanisms.

If you see a spike in requests that are all extremely slow and consuming high CPU, it’s often due to a poorly optimized database query that django-silk will pinpoint.

The next thing you’ll likely want to tackle is understanding how to manage the amount of data django-silk collects, especially in production, as it can consume significant disk space and I/O.

Want structured learning?

Take the full Django course →