If you've ever profiled a Django app, you've probably used django-silk. It's invaluable — every HTTP request recorded, every SQL query captured with timing and a stack trace, and
code profiling with decorators or cProfile.
The problem? The UI hasn't changed meaningfully in years. It's functional, but it's dated.
I spent the last few days forking it into django-silky — same full feature set, completely redesigned interface.
What changed
Light / dark theme
The original has a fixed dark nav bar with a light body — not a real theme, just inconsistent styling. django-silky uses CSS custom properties throughout so the entire UI toggles cleanly between light and dark, persisted in
localStorage.
D3 analytics dashboard
The summary page now has a full analytics section built with self-hosted D3.js v7 (no CDN):
- Request activity — area chart over time (hourly or daily buckets depending on range)
- Status code breakdown — donut chart (2xx / 3xx / 4xx / 5xx)
- HTTP method distribution — horizontal lollipop chart
- Response time histogram — 6 fixed buckets with gradient fill
- Latency percentiles — gradient area chart annotated at p25, p50, p75, p95, p99 for both request time and SQL query time
All charts re-render when you toggle the theme, and they read colours from CSS custom properties so they always match the active theme.
N+1 query detection
This was my favourite thing to build. Silk already captures every SQL query per request, so it has all the data needed to detect N+1 patterns automatically.
The algorithm:
- For each query in a request, compute a fingerprint — strip single-quoted string literals →
?, strip numeric literals →?, normalize whitespace, lowercase - Group queries by fingerprint
- Flag any group with ≥ 3 occurrences
When N+1 is detected you get:
- A warning pill in the request hero bar linking to the SQL list
- A banner on the SQL list page showing the repeated pattern count and the actual query text
- Highlighted rows in the SQL table for every flagged query
The key implementation detail: only strip single-quoted string literals (values). Double-quoted identifiers like "table"."column" are kept intact so the fingerprint remains human-readable.
Requests list
The requests list now uses:
- A table layout instead of a card grid — much easier to scan 25+ requests
- Inline collapsible filter bar replacing the old 300 px slide-out drawer
- Multi-column sort chips — click to add/remove/toggle sort columns, persisted in session
- Real Django Paginator with prev/next/page numbers instead of a raw
LIMIT Nslice - Sort + per-page encoded in the URL so you can share a specific filtered view
Detail pages
Every detail page (request, SQL, profile) now has a hero bar at the top with:
- Method badge (colour-coded: GET green, POST blue, PUT amber, DELETE red)
- Status code badge (2xx green, 3xx blue, 4xx amber, 5xx red)
- Timestamp
- Timing pills: total time, DB time, query count
Icons
Replaced Font Awesome loaded from a CDN with Lucide icons, self-hosted at silk/static/silk/lib/lucide.min.js. Zero external network requests from the UI.
Installation
pip install django-silky
It's a drop-in replacement for django-silk — same silk app label, same database schema (migrations 0001–0008), no manage.py migrate needed.
pip uninstall django-silk
pip install django-silky
settings.py
MIDDLEWARE = [
...
'silk.middleware.SilkyMiddleware',
...
]
INSTALLED_APPS = [
...
'silk',
]
urls.py
urlpatterns += [
path('silk/', include('silk.urls', namespace='silk')),
]
Visit /silk/ and you're in.
SQL inspection
Every SQL query is captured with:
- Execution time (colour-coded: green < 100 ms, amber 100–500 ms, red > 500 ms)
- Tables involved
- Number of joins
- Full Python stack trace so you know exactly where the query was triggered
- EXPLAIN plan (when
SILKY_ANALYZE_QUERIES = True)
Configuration highlights
# Authentication
SILKY_AUTHENTICATION = True
SILKY_AUTHORISATION = True # is_staff required
# Sampling on high-traffic sites
SILKY_INTERCEPT_PERCENT = 50 # record only 50% of requests
# Query analysis
SILKY_ANALYZE_QUERIES = True
# Garbage collection
SILKY_MAX_RECORDED_REQUESTS = 10_000
Code profiling
The decorator and context manager APIs are unchanged from django-silk:
from silk.profiling.profiler import silk_profile
@silk_profile(name='Get farm visits')
def get_visits(request):
return Visit.objects.select_related('farmer').all()
cProfile integration with call-graph rendering is also still there:
SILKY_PYTHON_PROFILER = True
Links
- PyPI: https://pypi.org/project/django-silky/
- GitHub: https://github.com/VaishnavGhenge/django-silky
- Migrating from django-silk: MIGRATING.md
Feedback, issues, and PRs are very welcome. If you hit an N+1 problem in your app that Silk surfaces, I'd love to hear about it!









Top comments (0)