Login

Switch/case tags.

Author:
jacobian
Posted:
July 3, 2007
Language:
Python
Version:
.96
Score:
19 (after 19 ratings)

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

  1. Template tag - list punctuation for a list of items by shapiromatron 11 months ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months, 1 week ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
  5. Help text hyperlinks by sa2812 1 year, 7 months ago

Comments

wiz (on July 5, 2007):

Why not default case?

#

jacobian (on July 5, 2007):

@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.