DEV Community

Cover image for Don't abuse Django's DEBUG setting
Code Review Doctor
Code Review Doctor

Posted on • Updated on

Don't abuse Django's DEBUG setting

It's tempting to treat settings.DEBUG as a feature flag. Many devs see it as analogous to is the app not running in prod. However, using settings.DEBUG to change the flow of code adds complexities to testing and maintaining the codebase.

As a concrete example, let's say we want to activate Django admin only in local development to reduce the surface area for hackers to attack in prod. We could do this:

from django.conf import settings
from django.contrib import admin

urlpatterns = [...]

if settings.DEBUG:
    # not exposing admin to prod so it can't be hacked
    urlpatterns.append(path('admin/', admin.site.urls))
Enter fullscreen mode Exit fullscreen mode

This will work, but the code will be harder to test in isolation because the value of settings.DEBUG changes other Django behaviours including error handling (should we see detailed crash report in browser? Should the error be emailed to admins?). Therefore instead of using settings.DEBUG in this way consider using a feature flag:

from django.conf import settings
from django.contrib import admin

urlpatterns = [...]

if settings.IS_ADMIN_ENABLED:
    # not exposing admin to prod so it can't be hacked
    urlpatterns.append(path('admin/', admin.site.urls))
Enter fullscreen mode Exit fullscreen mode

The reason for this complexity is the fact settings.DEBUG is for switching whether Django should run in debug mode. Indeed it's for switching behaviour of the framework and framework libraries, not application code. Using settings.DEBUG for also controlling the behaviour of the application code built on top of the framework makes the codebase harder to maintain and test. Mixing the layers and responsibilities like this adds complexity.

For example, DEBUG=True shows detailed error pages for use during local development, while DEBUG=False can result in the error instead being emailed to settings.ADMINS. Do you want to cause either of those behavioural changes while testing your code? Probably not, as this is inconvenient and also goes against the principle of least surprise.

Aside from the complexities in testing, Twelve factor App suggests aiming for dev/prod parity. Using settings.DEBUG to control which block of code the application runs prevents dev/prod parity because most developers will not test or run their local environment with DEBUG=False. To solve this instead consider adding a feature flag specifically for this one check, which you can switch easily during unit tests and with none of the other side effects inherent with settings.DEBUG.

If we spot this issue in your GitHub pull request we give this advice:

image

Feature flag implimentation

12 factor app suggests feature flags can be switched on or off using environment variables. You could install a library to handle the parsing of environment variables or do something like:

# settings.py
from ast import iteral_eval
from os import getenv

IS_ADMIN_ENABLED = literal_eval(getenv('IS_ADMIN_ENABLED', 'False'))
Enter fullscreen mode Exit fullscreen mode

ast.iteral_eval is that helpful function you always needed but never knew was provided out of the box by Python. It can convert string "False" to boolean False and string "True" to boolean True.

Does your Django codebase have tech debt like this?

Django Doctor can find and fix over 40 types of common Django tech debt, and can even review your GitHub Pull Requests for you.

Top comments (2)

Collapse
 
bhupesh profile image
Bhupesh Varshney ๐Ÿ‘พ

Damn, nice tip about iteral_eval ๐Ÿ‘Œ

Collapse
 
ldeath profile image
zohaib

can i also write this type of stuff in my account? pls help