Django tags with parameters

In the beginning

Some time ago, I struggled and wrestled with a problem that should have been easy. How, in my custom tag, can I parse the parameters passed to me, and know if they're string/int literals, or things I should look up in context?

Now, way back in my Zope days, I had this one beat... I had a little pattern I used... and used ... and used... but that was 10 years ago, and a whole different mindset.

I hunted about, and finally found someone else had "done it right!™" in their Django app, so I copied them. Then I put it in my own toolbox gnocchi-tools.

Recently I was trying to help someone who'd run into this exact problem. "Oh", I thought, "I've got this solved!" But it wasn't that easy (because I hadn't written sufficient docs -- mea culpa).

So, here's a reference for everyone.

How It's Done

Everything you need is already in Django. Imagine that :)

Building

First, during parsing, when your tag function is handed everything and expected to yield a Node, you have to split the contents of the {% %}.

def mytag(parser, token):
    parts = token.split_contents()
    parts.pop(0)

Remember that the first 'part' is your tag name. I typically discard this (hence the pop).

Next, you ask the parser to build a 'filter' for each parameter.

    parts = [ parser.compile_filter(part) for part in parts ]

Obviously, not every tag is going to want every parameter resolvable ... but it'll suffice for this example.

That's it for the prep work... pass these values to your tag, and the rest happens in your tag render function.

    return MyTag(parts)

Rendering

In your render function, you now need to resolve these filters "in context".

class MyTag(template.Node):
    def __init__(self, parts):
        super(MyTag, self).__init__()
        self.parts = parts
    def render(self, context):
        parts = [ part.resolve(context) for part in parts ]

... and that's it! All of your 'parts' are now resolved and ready to use!

comments powered by Disqus