========= Tag Howto ========= Tags are much more complex than filters_, because with tags you can do everything. While filters only extend the behavior of tags like ``print`` and ``filter``, the whole Jinja core uses tags to handle loops, conditions... When Jinja compiles a template, it splits the raw template text into ''nodes''. Each node is an instance of ``jinja.nodes.Node`` and has a ``render(context)`` method. A compiled template is, simply, a list of ``Node`` objects. When you call ``render()`` on a compiled template object, the template calls ``render()`` on each ``Node`` in its node list, with the given context. The results are all concatenated together to form the output of the template. When Jinja encounters a ``BlockToken`` in the template it looks at the defined library and let it parse the token content (e.g. ``for item in sequence``). When no library is defined it uses the standard library ``stdlib``. Diving In ========= Each Tag object has to look at least like this:: from jinja.lib import stdlib from jinja.nodes import * class MyTag(Node): rules = {} def __init__(self, parser, matched_tag, handler_args, stack): pass def render(self, context): return '' stdlib.register_tag(MyTag) ``rules`` is a dict of parser instructions:: rules = { 'default': [KeywordNode('mytag')], 'witharg': [KeywordNode('mytag'), ChoiceNode()] } This rule definition would match all ``{% mytag %}`` and ``{% mytag arg %}`` tags. The ``__init__`` method gets called on tag creation. When you're using a cached loader it will save the tag in the state of leaving the ``__init__`` method. The arguments are these: * **parser** - a template parser instance which can be used to parse parts of the template. * **matched_tag** - a string containing the name of the matched rule. (e.g. ``witharg`` or ``default`` in the example above) * **handler_args** - the list of argument nodes. * **stack** - list of piped filters Example ======= To understand this here is the defintion of the ``print`` tag:: class VariableTag(Node): rules = { 'default': [KeywordNode('print'), ChoiceNode()] } def __init__(self, parser, matched_tag, handler_args, stack): self._variable = handler_args[1] self._filters = [(f, args[1:][0]) for f, _, args in stack] def findnodes(self): yield self._variable def render(self, context): if not self._filters: return self._variable.render(context) var = self._variable.resolve(context) for f, args in self._filters: var = f(var, *[arg.resolve(context) for arg in args]) return var stdlib.register_tag(VariableTag) The ``rules`` dict defines a rule matching all ``{% print variable %}``. A ``ChoiceNode`` matches per default all variables and string/integer constants. The ``__init__`` methods saves the variable node and the list of filters in the Tag. The ``findnodes`` method has to return a iterable of all nodes defined in the ``Tag``. In the render method the ``VariableTag`` returns a parsed content of the variable by applying all filters. Nodes ===== Jinja shipps a number of nodes the parser can match. All this nodes are defined in the ``jinja.nodes`` module. KeywordNode ~~~~~~~~~~~ A keyword node matches against a constant keyword value. You can compare Keywords with strings which simplyfies the postprocessing:: if my_keyword_node == "foo": ... else: ... It isn't possible to resolve or render keyworde nodes. VariableNode ~~~~~~~~~~~~ A variable node matches all possible variables by saving the name. Variable nodes provide a ``define`` method for updating their value:: varnode.define(context, 'new value') You can get the value of a ``VariableNode`` using resolve:: value = varnode.resolve(context) variable nodes do also provide a render method which acts like the resolve method but returns a string. ValueNode ~~~~~~~~~ Value nodes behaves like variable nodes but match strings, integers, boolean values and "none". It provides the same functionallity like the ``VariableNode``, but resolve can also get called without the context which allows you to fetch the constant value inside the ``__init__`` method of a tag. ChoiceNode ~~~~~~~~~~ A choice node matches more than one one nodetype:: ChoiceNode(Node1(), Node2()) When not given any arguments it will match eigther one ``VariableNode`` or ``ValueNode``. CollectionNode ~~~~~~~~~~~~~~ A collection node matches an unlimited number of Nodes:: CollectionNode(Node1(), Node2()) When not given any arguments it will match all variable and/or value nodes. One-Way Parsing =============== One way parsing is very basic:: {% mynode %} ... {% endmynode %} You can fetch the ``body`` between those two tags inside the ``__init__`` method of you ``MyTag`` class:: self._body = parser.subparse('endmynode') This will store all the nodes from ``mynode`` to ``endmynode`` which you can render using ``self._body.render(context)``. Two-Way Parsing =============== Two way parsing is a bit more complicated:: {% mynode %} ... {% switchmynode %} ... {% endmynode %} But it would also match:: {% mynode %} ... {% endmynode %} Parsing this would result in two bodies:: self._body_one, self._body_two = parser.forkparse('switchmynode', 'endmynode') When the parser doesn't find the ``switchmynode`` tag it will returns an empty ``NodeList`` for ``self._body_two``. For more informations have a look at the Tags_ module in the jinja source. .. _filters: filter-dev.txt .. _Tags: http://wsgiarea.pocoo.org/trac/browser/jinja/trunk/jinja/tags.py