DjangoDebugging

Remarks

Pdb

Pdb can also print out all existing variables in global or local scope, by typing globals() or locals() in (Pdb) prompt respectively.

Using Python Debugger (Pdb)

Most basic Django debugging tool is pdb, a part of Python standard library.

Init view script

Let's examine a simple views.py script:

from django.http import HttpResponse


def index(request):
    foo = 1
    bar = 0

    bug = foo/bar

    return HttpResponse("%d goes here." % bug)

Console command to run server:

python manage.py runserver

It's obvious that Django would throw a ZeroDivisionError when you try to load index page, but if we'll pretend that the bug is very deep in the code, it could get really nasty.

Setting a breakpoint

Fortunately, we can set a breakpoint to trace down that bug:

from django.http import HttpResponse

# Pdb import
import pdb


def index(request):
    foo = 1
    bar = 0
    
    # This is our new breakpoint
    pdb.set_trace()
    
    bug = foo/bar
    
    return HttpResponse("%d goes here." % bug)

Console command to run server with pdb:

python -m pdb manage.py runserver

Now on page load breakpoint will trigger (Pdb) prompt in the shell, which will also hang your browser in pending state.

Debugging with pdb shell

It's time to debug that view by interacting with script via shell:

> ../views.py(12)index()
-> bug = foo/bar
# input 'foo/bar' expression to see division results:
(Pdb) foo/bar
*** ZeroDivisionError: division by zero
# input variables names to check their values:
(Pdb) foo
1
(Pdb) bar
0
# 'bar' is a source of the problem, so if we set it's value > 0...
(Pdb) bar = 1
(Pdb) foo/bar
1.0
# exception gone, ask pdb to continue execution by typing 'c':
(Pdb) c
[03/Aug/2016 10:50:45] "GET / HTTP/1.1" 200 111

In the last line we see that our view returned an OK response and executing as it should.

To stop pdb loop, just input q in a shell.

Using Django Debug Toolbar

First, you need to install django-debug-toolbar:

pip install django-debug-toolbar

settings.py:

Next, include it to project's installed apps, but be careful - it's always a good practice to use a different settings.py file for such development-only apps and middlewares as debug toolbar:

# If environment is dev...
DEBUG = True

INSTALLED_APPS += [
    'debug_toolbar',
]

MIDDLEWARE += ['debug_toolbar.middleware.DebugToolbarMiddleware']

Debug toolbar also relies on static files, so appropriate app should be included as well:

INSTALLED_APPS = [
    # ...
    'django.contrib.staticfiles',
    # ...
]

STATIC_URL = '/static/'

# If environment is dev...
DEBUG = True

INSTALLED_APPS += [
    'debug_toolbar',
]

In some cases, it's also required to set INTERNAL_IPS in settings.py:

INTERNAL_IPS = ('127.0.0.1', )

urls.py:

In urls.py, as official documentation suggests, the next snippet should enable debug toolbar routing:

if settings.DEBUG and 'debug_toolbar' in settings.INSTALLED_APPS:
    import debug_toolbar
    urlpatterns += [
        url(r'^__debug__/', include(debug_toolbar.urls)),
    ]

Collect toolbar's static after installation:

python manage.py collectstatic

That's it, debug toolbar will appear on you project's pages, providing various useful information about execution time, SQL, static files, signals, etc.

HTML:

Also, django-debug-toolbar requires a Content-type of text/html, <html> and <body> tags to render properly.


In case if you sure you've configured everything right, but debug toolbar is still not rendered: use this "nuclear" solution to try to figure it out.

Using "assert False"

While developing, inserting the following line to your code:

assert False, value

will cause django to raise an AssertionError with the value supplied as an error message when this line is executed.

If this occurs in a view, or in any code called from a view, and DEBUG=True is set, a full and detailed stacktrace with a lot of debugging information will be displayed in the browser.

Don't forget to remove the line when you are done!

Consider Writing More Documentation, Tests, Logging and Assertions Instead of Using a Debugger

Debugging takes time and effort.

Instead of chasing bugs with a debugger, consider spending more time on making your code better by:

  • Write and run Tests. Python and Django have great builtin testing frameworks - that can be used to test your code much faster than manually with a debugger.
  • Writing proper documentation for your functions, classes and modules. PEP 257 and Google's Python Style Guide supplies good practices for writing good docstrings.
  • Use Logging to produce output from your program - during development and after deploying.
  • Add assertions to your code in important places: Reduce ambiguity, catch problems as they are created.

Bonus: Write doctests for combining documentation and testing!