Django template and the locals trick

I don’t like repetition — I think “DRY”, “Don’t Repeat Yourself”, is a key programming principle. As a consequence, I have indeed used locals() in similar situations. Django template rendering is far from the only situation of this kind: the general case is “a function or operator which accepts a dict, but doesn’t mind if the dict contains extra entries”. (For example, ordinary string formatting in Python is another such case).

However, there’s a countervailing principle: programs should be understandable in as localized a way as feasible — that helps maintenance and refactoring (as it obviates the need to study other files to check what refactorings are acceptable). This suggests, for the locals() case, that it’s OK if the template (or string format, etc) is a local literal (a rare case where only few variables are probably being used and thus locals() is not a huge win!-), but problematic in the normal case where the template lives in a different file.

So, using locals(), in most cases, seriously hampers refactoring. In almost every situation in Python, local variables and their names can freely be changed as part of a local refactoring, since they have no “externally visible” effect… but using locals() breaks that — suddenly you can’t safely rename a variable to a different name offering better clarity, refactor code flow in a manner that removes the need for a variable, etc, etc, without each and every time studying a separate template file to check if the old name might not be needed (and possibly editing the template file, which can be non-trivial, for example if it’s maintained in several different natural languages for i18n/L10n purposes).

As a consequence, in addition to the secondary issue of performance, there is strong pressure against using locals() in “serious”, “production” code — code that does need long term maintenance and therefore easy refactoring and locality. So, when I’m “programming as best I know how”, rather than “cutting corners”, I’m aware I had better avoid locals().

The values that you want to have in the context in which the template is rendered are not necessarily “naturally” available as local bare-names, after all; maybe some or many of them are results of computations, items from lists or dictionaries, and the like. In this case, the temptation to “cut corners” with locals() is easier to avoid if you just accumulate those values into a suitable dictionary rather than assigning them local bare-names.

It’s not the easiest tradeoff, because two good principles (avoiding repetition, and having good locality) are inevitably clashing — therefore, good question! And not one entirely susceptible to sharp black or white answers, which is why I’ve tried to expand on both sides. In the end, I think it’s one of those “style” aspects where a programming team might be well advised to adopt a team-uniform style guideline and stick to it — at least it removes the need to make a decision over and over every time the issue arises, and produces a more homogeneous (and thereby maintainable) code base. [[I have to confess that this specific point has never been explicitly addressed in the style guidelines of teams I’ve been in, though, although many others have!-)]]

Leave a Comment