| name | debug:django |
| description | Debug Django web applications with systematic diagnostic approaches. This skill covers troubleshooting Django-specific errors including TemplateDoesNotExist, ImproperlyConfigured, IntegrityError, migration conflicts, CSRF failures, N+1 query problems, and circular imports. Includes Django Debug Toolbar setup, ORM query logging, pdb/ipdb usage, shell_plus debugging, and comprehensive logging configuration. Provides four-phase methodology for root cause analysis and regression prevention. |
Django Debugging Guide
Overview
Django debugging requires understanding the framework's layered architecture: settings, URL routing, views, templates, models, and middleware. Effective debugging follows a systematic approach - reproducing the bug, isolating the problem, gathering evidence, and implementing verified fixes.
Key principles:
- Reproduce first: Repeat the exact steps that cause the issue
- Isolate systematically: Identify the exact module, function, or code path
- Gather evidence: Collect error messages, stack traces, logs, and database state
- Test your fix: Verify the solution with automated tests
Common Error Patterns
TemplateDoesNotExist
Cause: Django cannot find the specified template file.
Investigation steps:
- Check template exists in the correct directory
- Verify
TEMPLATES['DIRS'] setting includes your template directories
- Check for typos in template path
- Ensure app is in
INSTALLED_APPS if using app-specific templates
python manage.py shell -c "from django.conf import settings; print(settings.TEMPLATES)"
python manage.py shell -c "import os; from django.conf import settings; print([d for d in settings.TEMPLATES[0]['DIRS'] if os.path.exists(d)])"
ImproperlyConfigured
Cause: Django configuration error in settings.py or environment.
Common causes:
- Missing or incorrect
DJANGO_SETTINGS_MODULE
- Invalid database configuration
- Missing required settings
- Incorrect app configuration
echo $DJANGO_SETTINGS_MODULE
python manage.py check
python manage.py diffsettings
IntegrityError / Database Errors
Cause: Database constraint violations or missing migrations.
OperationalError (missing column/table):
python manage.py showmigrations
python manage.py makemigrations
python manage.py migrate
python manage.py sqlmigrate <app_name> <migration_number>
IntegrityError (constraint violation):
from django.db import IntegrityError
try:
obj.save()
except IntegrityError as e:
print(f"Constraint violation: {e}")
N+1 Query Problems
Cause: Inefficient database queries in loops.
Detection:
LOGGING = {
'version': 1,
'handlers': {'console': {'class': 'logging.StreamHandler'}},
'loggers': {
'django.db.backends': {
'level': 'DEBUG',
'handlers': ['console'],
}
}
}
Solution - use select_related/prefetch_related:
for order in Order.objects.all():
print(order.customer.name)
for order in Order.objects.select_related('customer'):
print(order.customer.name)
orders = Order.objects.prefetch_related('items')
Migration Conflicts
Cause: Multiple developers creating migrations on same app.
python manage.py showmigrations --plan
python manage.py makemigrations --merge
python manage.py migrate <app_name> zero
python manage.py makemigrations <app_name>
python manage.py migrate <app_name>
CSRF Verification Failures
Cause: Missing CSRF token or misconfigured trusted origins.
Checklist:
- Include
{% csrf_token %} in forms
- Check
CSRF_TRUSTED_ORIGINS for HTTPS sites
- Verify middleware includes
CsrfViewMiddleware
- For AJAX, include CSRF token in headers
CSRF_TRUSTED_ORIGINS = [
'https://your-domain.com',
]
Import Errors / Circular Imports
Cause: Circular dependencies between modules.
Detection:
python -c "import your_app"
python -v -c "import your_app" 2>&1 | grep "import"
Solutions:
- Move imports inside functions (lazy import)
- Restructure modules to break cycles
- Use string references for model relationships
def my_function():
from other_module import SomeClass
return SomeClass()
class Order(models.Model):
customer = models.ForeignKey('customers.Customer', on_delete=models.CASCADE)
DoesNotExist
Cause: Querying for an object that does not exist in the database.
user = User.objects.get(id=999)
from django.shortcuts import get_object_or_404
user = get_object_or_404(User, id=999)
user, created = User.objects.get_or_create(
username='john',
defaults={'email': 'john@example.com'}
)
try:
user = User.objects.get(id=999)
except User.DoesNotExist:
user = None
DisallowedHost
Cause: Request host not in ALLOWED_HOSTS setting.
ALLOWED_HOSTS = [
'localhost',
'127.0.0.1',
'your-domain.com',
'.your-domain.com',
]
NoReverseMatch
Cause: URL reverse lookup failed - URL name not found or wrong arguments.
python manage.py show_urls
python manage.py shell -c "from django.urls import get_resolver; print([p.name for p in get_resolver().url_patterns])"
from django.urls import reverse
url = reverse('app_name:view_name', args=[object_id])
url = reverse('app_name:view_name', kwargs={'pk': object_id})
Debugging Tools
Django Debug Toolbar
The most powerful visual debugging tool for Django development.
Installation:
pip install django-debug-toolbar
Configuration:
INSTALLED_APPS = [
'debug_toolbar',
]
MIDDLEWARE = [
'debug_toolbar.middleware.DebugToolbarMiddleware',
]
INTERNAL_IPS = ['127.0.0.1']
DEBUG_TOOLBAR_CONFIG = {
'SHOW_TOOLBAR_CALLBACK': 'debug_toolbar.middleware.show_toolbar_with_docker',
}
from debug_toolbar.toolbar import debug_toolbar_urls
urlpatterns = [
] + debug_toolbar_urls()
Key panels:
- SQL: All database queries with timing and EXPLAIN
- Templates: Rendered templates and context variables
- Cache: Cache hits/misses
- Request: Headers, cookies, session data
- Signals: Django signals fired
Python Debugger (pdb/ipdb)
breakpoint()
import pdb; pdb.set_trace()
import ipdb; ipdb.set_trace()
Common pdb commands:
n (next): Execute next line
s (step): Step into function
c (continue): Continue execution
p variable: Print variable value
pp variable: Pretty print
l (list): Show current code
w (where): Show stack trace
q (quit): Exit debugger
django-extensions shell_plus
pip install django-extensions
INSTALLED_APPS = ['django_extensions', ...]
python manage.py shell_plus
python manage.py shell_plus --ipython
python manage.py shell_plus --print-sql
Logging Configuration
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '{levelname} {asctime} {module} {message}',
'style': '{',
},
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'formatter': 'verbose',
},
'file': {
'class': 'logging.FileHandler',
'filename': 'debug.log',
'formatter': 'verbose',
},
},
'loggers': {
'django': {
'handlers': ['console'],
'level': 'INFO',
},
'django.db.backends': {
'handlers': ['console'],
'level': 'DEBUG',
},
'myapp': {
'handlers': ['console', 'file'],
'level': 'DEBUG',
},
},
}
Usage in code:
import logging
logger = logging.getLogger(__name__)
logger.debug('Detailed information')
logger.info('General information')
logger.warning('Warning message')
logger.error('Error occurred', exc_info=True)
logger.exception('Exception occurred')
SQL Query Logging
from django.db import connection
from django.db import reset_queries
reset_queries()
print(connection.queries)
queryset = User.objects.filter(is_active=True)
print(queryset.explain())
The Four Phases (Django-Specific)
Phase 1: Root Cause Investigation
Gather evidence systematically:
python manage.py check
python manage.py check --deploy
python manage.py showmigrations
python manage.py showmigrations --plan
python manage.py diffsettings
tail -f debug.log
docker compose logs -f api
Examine the error traceback:
- Note the exception type (determines error category)
- Find the last line of YOUR code (not Django internals)
- Check variable values in the frame
Database state inspection:
python manage.py shell
from myapp.models import MyModel
MyModel.objects.count()
MyModel.objects.filter(status='error').values()
Phase 2: Pattern Analysis
Compare with working patterns:
- Check similar working code: Find views/functions that work and compare
- Review Django documentation: Verify correct usage of APIs
- Check third-party package versions: Ensure compatibility
pip freeze | grep -i django
pip show django-rest-framework
Common pattern mismatches:
- Missing
return in view functions
- Wrong queryset method order
- Incorrect URL pattern syntax
- Missing model field attributes
Phase 3: Hypothesis and Testing
Write a failing test first:
import pytest
from django.test import TestCase, Client
@pytest.mark.django_db
class TestBugFix(TestCase):
def test_reproduces_bug(self):
"""This test should fail until bug is fixed."""
client = Client()
response = client.get('/problematic-url/')
self.assertEqual(response.status_code, 200)
def test_edge_case(self):
"""Test the specific input that causes the bug."""
from myapp.services import problematic_function
result = problematic_function(edge_case_input)
self.assertEqual(result, expected_output)
Run tests in isolation:
pytest tests/test_debug.py::TestBugFix::test_reproduces_bug -v
pytest tests/test_debug.py -v --capture=no
pytest tests/test_debug.py --pdb
pytest tests/test_debug.py --cov=myapp --cov-report=term-missing
Phase 4: Implementation
Implement the fix:
- Make minimal changes to fix the issue
- Ensure existing tests still pass
- Add new tests for the specific bug
- Document the fix in commit message
Verify the fix:
pytest -v
pytest tests/test_affected_module.py -v
pytest tests/ -v --tb=short
python manage.py runserver
Post-fix checklist:
Quick Reference Commands
Django Management Commands
python manage.py check
python manage.py check --deploy
python manage.py check --tag security
python manage.py showmigrations
python manage.py showmigrations --plan
python manage.py sqlmigrate app 0001
python manage.py makemigrations --dry-run
python manage.py migrate --plan
python manage.py diffsettings
python manage.py diffsettings --all
python manage.py dbshell
python manage.py inspectdb
python manage.py shell
python manage.py shell_plus
python manage.py shell_plus --print-sql
python manage.py show_urls
python manage.py clearsessions
python manage.py flush
Quick Debugging Snippets
from django.db import connection
def my_view(request):
for query in connection.queries:
print(query['sql'])
request.headers.get('X-Requested-With') == 'XMLHttpRequest'
print(request.GET.dict())
print(request.POST.dict())
print(request.body)
print(dict(request.headers))
{% debug %}
from django.template import engines
engine = engines['django']
template = engine.from_string("{{ debug }}")
Environment Debugging
which python
python --version
pip list
python -c "import django; print(django.VERSION)"
python manage.py dbshell -c "SELECT 1;"
redis-cli ping
celery -A config inspect active
celery -A config inspect stats
Additional Resources