Although a bit hackish, it turned out to be perfectly possible for the tag/filter case. Commands, no: django actually searches the filesystem for the .py
files involved.
The template hack is completely self-contained -- it won't leak dubious stuff into nearby code. A single module can hold all of the metamagic, and be imported by regular modules.
First, we have to create a mock module. We'll create a nice decorator to turn regular classes into modules:
def module(path):
def decorator(cls):
if '.' in path:
parent_path, name = path.rsplit('.', 1)
module(parent_path)(type(name, (object,), { name: cls }))
return sys.modules.setdefault(path, cls)
return decorator
Yes, the recursion is necessary (we need the full import path mocked) and it can be used to create a fake templatetags
module like this:
@module('app.templatetags.extras')
class TemplateExtras(object):
class register:
tags = {}
filters = {}
# Optional: avoid using {% load %}:
django.template.libraries['app.templatetags.extras'] = TemplateExtras
django.template.base.add_to_builtins('app.templatetags.extras')
Add two more pretty decorators into the mix...
def templatetag(f):
return TemplateExtras.register.tags.setdefault(f.__name__, f)
def templatefilter(f):
return TemplateExtras.register.filters.setdefault(f.__name__, f)
... and you get the following clean, straightforward syntax:
# imported or present in app/__init__.py
@templatefilter
def datestr(date):
return datetime.strftime(date, '%Y-%m-%d')