Meant mostly as a demo of how to do complex block tags, here's a switch/case implementation.
It's deliberately simplistic: no default cases, no multiple values for the same case, etc. This is so that you can focus on the technique.
Pay particular attention to how both switch and case pull out their child nodes and save them for later rendering. This is a very useful technique for these types of "structured" template tags.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | """
Simplistic switch/case tag for Django.
Usage::
{% load switchcase %}
{% switch meal %}
{% case "spam" %}...{% endcase %}
{% case "eggs" %}...{% endcase %}
{% endswitch %}
"""
from django import template
register = template.Library()
@register.tag
def switch(parser, token):
"""
Switch tag. Usage::
{% switch meal %}
{% case "spam" %}...{% endcase %}
{% case "eggs" %}...{% endcase %}
{% endswitch %}
Note that ``{% case %}`` arguments can be variables if you like (as can
switch arguments, buts that's a bit silly).
"""
# Parse out the arguments.
args = token.split_contents()
if len(args) != 2:
raise template.TemplateSyntaxError("%s tag tags exactly 2 arguments." % args[0])
# Pull out all the children of the switch tag (until {% endswitch %}).
childnodes = parser.parse(("endswitch",))
# Remove the {% endswitch %} node so it doesn't get parsed twice.
parser.delete_first_token()
# We just care about case children; all other direct children get ignored.
casenodes = childnodes.get_nodes_by_type(CaseNode)
return SwitchNode(args[1], casenodes)
@register.tag
def case(parser, token):
"""
Case tag. Used only inside ``{% switch %}`` tags, so see above for those docs.
"""
args = token.split_contents()
assert len(args) == 2
# Same dance as above, except this time we care about all the child nodes
children = parser.parse(("endcase",))
parser.delete_first_token()
return CaseNode(args[1], children)
class SwitchNode(template.Node):
def __init__(self, value, cases):
self.value = value
self.cases = cases
def render(self, context):
# Resolve the value; if it's a non-existant variable don't even bother
# checking the values of the cases since they'll never match.
try:
value = template.resolve_variable(self.value, context)
except VariableDoesNotExist:
return ""
# Check each case, and if it matches return the rendered content
# of that case (short-circuit).
for case in self.cases:
if case.equals(value, context):
return case.render(context)
# No matches; render nothing.
return ""
class CaseNode(template.Node):
def __init__(self, value, childnodes):
self.value = value
self.childnodes = childnodes
def equals(self, otherval, context):
"""
Check to see if this case's value equals some other value. This is
called from ``SwitchNode.render()``, above.
"""
try:
return template.resolve_variable(self.value, context) == otherval
except VariableDoesNotExist:
# If the variable doesn't exist, it doesn't equal anything.
return False
def render(self, context):
"""Render this particular case, which means rendering its child nodes."""
return self.childnodes.render(context)
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 11 months, 2 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months, 3 weeks ago
- Serializer factory with Django Rest Framework by julio 1 year, 6 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 7 months ago
- Help text hyperlinks by sa2812 1 year, 7 months ago
Comments
Why not default case?
#
@wiz: as I said in the description, this is deliberately simplistic; all I'm trying to do is show off a certain pattern. Added default cases is left as an exercise to the reader :)
#
Please login first before commenting.