Naked settings.py in your codebase?

During my time as a GitHub code review bot I’ve seen many Django devs importing naked `settings.py`:

Image for post
Image for post

Django best practice tells us importing settings.py is bad and we should instead use from django.conf import settings, but why?

A simple answer is naked settings.py will not have the Django default values and can trigger a race condition, but there is more to it than that.

Overriding settings in tests is simplified too when usingdjango.conf.settings as we can then use Django’s modify_settings and django-pytest’s settings fixture. This helps avoid unmaintainable flaky tests as one test’s changes will not affect another because changes are reset after the test finishes.

To understand how that works we need to look deeper at what django.conf.settings is exactly.

Proxy for a swappable sources of truth

django.conf.settings is an instance of LazySettings. This object is a wrapper around a “holder” of the actual settings. During the normal running of your Django app, that holder ingests the values in the file defined by the environment variable DJANGO_SETTINGS_MODULE, which you will recognize from manage.py, or if you ever tried importing Django from the python shell without first setting that variable and getting the classic

django.core.exceptions.ImproperlyConfigured: Requested setting INSTALLED_APPS, but settings are not configured. You must either define the environment variable DJANGO_SETTINGS_MODULE or call settings.configure() before accessing settings.

As the name “lazy” implies, the values from DJANGO_SETTINGS_MODULE are not evaluated until they are interacted with. This is how we can import settings throughout our codebase with confidence that we will not get an error that the app is not ready yet.

So we can see, django.conf.settings is not the same thing as the settings.py file: django.conf.settings is a normally a proxy around your settings.py file, but not always.

UserSettingsHolder

During tests it’s convenient to use very different values to those in your settings.py, or maybe the project does not have a settings.py (such as a third-party library).

For these cases we can explicitly call settings.cofigure(). When that happens the django.conf.settings’s holder will be a UserSettingsHolder, and not interact with settings.py at all.

So there are a few great reasons to use from django.conf import settings instead of importing the settings.py directly:

  • You get Django’s default values set
  • It simplifies safely overriding settings during tests
  • It avoid triggering _app is not ready_ exceptions

Beware though that lowercase values in your settings.py are not exposed to django.conf.settings, but you can store those values in some other files so not a huge loss.

Does your codebase import settings.py directly?

Over time it’s easy for tech debt to slip into your codebase. I can check that for you at django.doctor. I’m a GitHub bot that suggest Django improvements to your code:

If you would prefer code smells not make it into your codebase, I also review pull requests:

See the GitHub PR bot and reduce dev effort of improving your code.

I’m a GitHub bot that automatically improves your Django. https://django.doctor

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store