systematic-debugging
Systematic Debugging for Django
Core Principle
NO FIXES WITHOUT ROOT CAUSE FIRST
Never apply patches that mask underlying problems. Understand WHY something fails before attempting to fix it.
Four-Phase Framework
Phase 1: Reproduce and Investigate
Before touching any code:
- Write a failing test - Captures the bug behavior
- Read error messages thoroughly - Every word matters
- Examine recent changes -
git diff,git log - Trace data flow - Follow the call chain to find where bad values originate
# Write a failing test first
@pytest.mark.django_db
def test_bug_reproduction():
"""Reproduces issue #123."""
user = UserFactory()
response = Client().post("/profile/", {"bio": "New"})
assert response.status_code == 200 # Currently failing
Phase 2: Isolate
Narrow down the problem:
# Add strategic logging
import logging
logger = logging.getLogger(__name__)
def problematic_view(request):
logger.debug(f"Method: {request.method}")
logger.debug(f"POST: {request.POST}")
logger.debug(f"User: {request.user}")
form = MyForm(request.POST)
logger.debug(f"Valid: {form.is_valid()}")
logger.debug(f"Errors: {form.errors}")
Phase 3: Identify Root Cause
- Read the full stack trace
- Use debugger to inspect state
- Check what assumptions are violated
Phase 4: Fix and Verify
- Implement fix at the root cause
- Run reproduction test (should pass)
- Run full test suite
- Verify manually if needed
Django Debug Tools
Django Debug Toolbar
# settings/dev.py
INSTALLED_APPS += ["debug_toolbar"]
MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware"]
INTERNAL_IPS = ["127.0.0.1"]
Check SQL panel for N+1 queries, slow queries > 10ms.
Python Debugger
def problematic_view(request):
breakpoint() # Execution stops here
# Commands: n(ext), s(tep), c(ontinue), p var, q(uit)
# Drop into debugger on test failure
uv run pytest --pdb -x
Query Debugging
# Log all SQL queries
LOGGING = {
"loggers": {
"django.db.backends": {"level": "DEBUG", "handlers": ["console"]},
},
}
# Count queries in tests
from django.test.utils import CaptureQueriesContext
from django.db import connection
def test_no_n_plus_one():
with CaptureQueriesContext(connection) as ctx:
list(Post.objects.select_related("author"))
assert len(ctx) <= 2
Common Django Issues
N+1 Queries
# Problem
for post in Post.objects.all():
print(post.author.email) # Query per post!
# Fix
for post in Post.objects.select_related("author"):
print(post.author.email) # Single query
Form Not Saving
# Check these:
# 1. form.is_valid() returns True?
# 2. form.save() called?
# 3. If commit=False, did you call .save() on instance?
def debug_form(request):
form = MyForm(request.POST)
print(f"Valid: {form.is_valid()}")
print(f"Errors: {form.errors}")
CSRF 403 Errors
<!-- Check: csrf_token in form -->
<form method="post">
{% csrf_token %}
</form>
Migration Issues
uv run python manage.py showmigrations
uv run python manage.py migrate app_name 0001 --fake
Debugging Celery
# Run synchronously for debugging
my_task(arg) # Direct call, not .delay()
# Or set in settings
CELERY_TASK_ALWAYS_EAGER = True
Debugging HTMX
<script>htmx.logAll();</script>
def view(request):
print(f"HTMX: {request.headers.get('HX-Request')}")
Checklist
Before claiming fixed:
- Root cause identified
- Reproduction test passes
- Full test suite passes (
uv run pytest) - No type errors (
uv run pyright) - No lint errors (
uv run ruff check .)
Red Flags
Stop if you're thinking:
- "Quick fix now, investigate later"
- "One more attempt" (after 3+ failures)
- "This should work" (without understanding why)
Three consecutive failed fixes = architectural problem. Stop and discuss.
Integration with Other Skills
- pytest-django-patterns: Write reproduction tests
- django-models: Debug QuerySet issues
- celery-patterns: Debug async task failures
- htmx-alpine-patterns: Debug HTMX requests
- django-extensions: Use
show_urls,list_model_info, andshell_plusfor project introspection - skill-creator: Create debugging-specific skills for recurring issues
More from kjnez/claude-code-django
htmx-patterns
HTMX patterns for Django including partial templates, hx-* attributes, and dynamic UI without JavaScript. Use when building interactive UI, handling AJAX requests, or creating dynamic components.
31django-templates
Django template patterns including inheritance, partials, tags, and filters. Use when working with templates, creating reusable components, or organizing template structure.
30pytest-django-patterns
pytest-django testing patterns, Factory Boy, fixtures, and TDD workflow. Use when writing tests, creating test factories, or following TDD red-green-refactor cycle.
24django-forms
Django form handling patterns including ModelForm, validation, clean methods, and HTMX form submission. Use when building forms, implementing validation, or handling form submission.
16celery-patterns
Celery task patterns including task definition, retry strategies, periodic tasks, and best practices. Use when implementing background tasks, scheduled jobs, or async processing.
15django-models
Django model design patterns emphasizing fat models/thin views, QuerySet optimization, and domain logic encapsulation. Use when designing models, optimizing queries, implementing business logic, or working with the ORM.
14