Search code examples
pythondjangopython-3.xdjango-1.10

Temporarily modifying `builtins` while splitting a django settings file


I'm starting a django 1.10 project and would like to split the settings file. I was unsatisfied with any existing solutions.

  1. I do not want to be able to override string/boolean/dict settings from one file in another. Each string/boolean/dict setting should be set in only one place. This makes it easy to keep track of where things are defined.
  2. I do not want to have to manually extend tuple/list settings, e.g. INSTALLED_APPS += (test_app). This seems to be messy and requires me to keep track of whether a list or tuple was used in the other file.

  3. I do not want to have to import os and define BASE_DIR in multiple files. DRY.

My solution, having looked at many others, is to replace settings.py with a directory containing local_settings.py, common_settings.py and __init__.py.

In __init__.py, I import os and calculate BASE_DIR. I then

import builtins
builtins.BASE_DIR = BASE_DIR
builtins.os = os

from .common_settings import *
from . import local_settings

# At this point both modules have run and we no longer need to be messing
# with the builtins namespace.
del builtins.BASE_DIR
del builtins.os
del builtins

I then loop over dir(local_settings) and mess with globals() to achieve the first two requirements (I can post the whole thing if requested but I'm interested in my use of builtins).

Is this use of builtins too evil? What can break it. Obviously if either identifier clashs with an attribute of a later version of builtins, then this code would break python. If a function that uses either of these identifiers ended up in one of the settings files and it was later called, then that would break.

I don't see either of those happening though. Is there a potential problem that I'm not seeing?


Solution

  • The main problem with modifying builtins in this way is that it adds non-local reasoning to your code for no good reason. The behavior of the common/local settings modules now implicitly depends on what happens in the module that imports them. That's bad.

    Essentially, you need to get rid of your requirement #3.

    • Importing os in each module isn't "repeating yourself" because each module imports os into its own namespace. That's just how Python works.

    • You're right to want to only define BASE_DIR once, but the correct way to do this is to define the variable in one module (say basedir.py) and then explicitly import that variable (from basedir import BASE_DIR) into each module that uses it.