Wednesday, January 30, 2013

Django Safer Variable Imports

In Django, global module variables are saved in the file. These variables are commonly brought into local modules like the code below. Have a brief look-
from django.conf import settings

SECRET_KEY = getattr(settings, 'SECRET_KEY', '')
TEMPLATE_DEBUG = getattr(settings, 'TEMPLATE_DEBUG', False)
MAX_CONTENT_LENGTH = getattr(settings, 'MAX_CONTENT_LEN', 5000)
DATETIME_FORMAT = getattr(settings, 'DATETIME_FORMAT', 'Y-m-d H:i T')
FORM_MAX_SUBMIT_TIME = getattr(settings, 'FORM_MIN_SUBMIT_TIME', 60 * 60 * 24 * 7)

Now let me ask, did you notice the typo? Most people won't, so congratulations on you if you did. There are actually 2 typos. The typos are on the variables-

Why did this happen? Why is it difficult to detect?

Firstly why this happens is because we have an excessive amount of repetitive code typing. This is just a fact of number probabilities- The less a person has to type, the less likely they are to make a typo. As a person is required to type more, the odds of a typo goes up.

Secondly why this happens is because it happens silently without ever producing any warning or alert. It would be prudent to have at least a log of when a variable is not supplied from django settings.

To solve this, I use the following simple function to care of both of these problems for me.
def load_attrs(attr_dict):
    """Adds attributes to the previous namespace from settings."""
    previous_namespace = sys._current_frames().values()[-1].f_back.f_globals
    for attr, default in attr_dict.iteritems():
            previous_namespace[attr] = getattr(settings, attr)
        except AttributError:
            previous_namespace[attr] = default
            if DEBUG:
                logging.debug("Failed to load settings."+ attr)

I expect you should generally be able to read this code, except perhaps line 3. Line 3 gets the global namespace of where ever this function was called from. It adds the attributes to that namespace.

Here is an example of importing the function and using it in a module. Compare this to the code at the top of the page-
from django.conf import settings
from apps.lib.lib import load_attrs

        'SECRET_KEY': '',
        'TEMPLATE_DEBUG': False,
        'MAX_CONTENT_LENGTH': 5000,
        'DATETIME_FORMAT': 'Y-m-d H:i T',
        'FORM_MIN_SUBMIT_TIME': 6,
        'FORM_MAX_SUBMIT_TIME': 60 * 60 * 24 * 7,

Much less typing, much less room for error, and IMHO much cleaner looking code. Hopefully somebody will suggest a function like this to be included in a future version of Django.

Comment or leave feedback sepero 111 @ gmx . com

django settings module variables