DEV Community

Cover image for django-silky: A Modern Fork of django-silk with Dark Mode, D3 Charts, and N+1 Detection
Vaishnav Ghenge
Vaishnav Ghenge

Posted on

django-silky: A Modern Fork of django-silk with Dark Mode, D3 Charts, and N+1 Detection

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.

Summary dashboard in dark mode

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.

Analytics charts

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:

  1. For each query in a request, compute a fingerprint — strip single-quoted string literals → ?, strip numeric literals → ?, normalize whitespace, lowercase
  2. Group queries by fingerprint
  3. 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

N+1 badge in hero bar

N+1 banner with real SQL

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 N slice
  • Sort + per-page encoded in the URL so you can share a specific filtered view

Requests list

Requests list with filter bar

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

Request detail

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

settings.py

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

INSTALLED_APPS = [
  ...
  'silk',
]
Enter fullscreen mode Exit fullscreen mode

urls.py

urlpatterns += [
  path('silk/', include('silk.urls', namespace='silk')),
]
Enter fullscreen mode Exit fullscreen mode

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)

SQL list

SQL detail with EXPLAIN plan


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
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

cProfile integration with call-graph rendering is also still there:

SILKY_PYTHON_PROFILER = True
Enter fullscreen mode Exit fullscreen mode

Links

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)