Ticket #10821: if_in_list_tag.py

File if_in_list_tag.py, 2.8 KB (added by tomevans222, 6 years ago)

Implementation of IfInList tag

Line 
1# This tag library adds a useful tag for checking whether an element is in a list of not
2# This can be useful if iterating over a list of all values, and testing to see if it is
3# in our current collection. It behaves like a usual 'if' and can be accompanied by an
4# 'else' block as well.
5#
6# EG:
7#   ctxt = { 'my_colours': ['orange', 'yellow'],
8#            'all_colours': ['red', 'orange', 'yellow', 'green', blue', 'indigo', 'violet'] }
9# <tr>{% for clr in all_colours %}<th>{{ clr }}</th>{% endfor %}</tr>
10# <tr>
11#   {% for clr in all_colours %}
12#   <td>{% IfInList clr in my_colours %}I like it{% else %}I hate it{% endif %}</td>
13#   {% endfor %}
14# </tr>
15#
16# You can also negate the test:
17#
18#  <td>{% IfInList clr not in mine %}</td>
19#
20from django.template import Node, Variable, TemplateSyntaxError, Library, NodeList, VariableDoesNotExist
21
22register = Library()
23
24class IfInListNode(Node):
25  def __init__(self, obj, list, nodelist_true, nodelist_false, negate):
26    self.obj, self.list = Variable(obj), list
27    self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
28    self.negate = negate
29
30  def __repr__(self):
31    return '<IfInList node>'
32
33  def __iter__(self):
34    for node in self.nodelist_true:
35      yield node
36    for node in self.nodelist_false:
37      yield node
38
39  def get_nodes_by_type(self, nodetype):
40    nodes = []
41    if isinstance(self, nodetype):
42      nodes.append(self)
43    nodes.extend(self.nodelist_true.get_nodes_by_type(nodetype))
44    nodes.extend(self.nodelist_false.get_nodes_by_type(nodetype))
45    return nodes
46
47  def render(self, context):
48    try:
49      val = self.obj.resolve(context)
50    except VariableDoesNotExist:
51      val = None
52    try:
53      list = self.list.resolve(context, True)
54    except VariableDoesNotExist:
55      list = []
56    in_list = val in list
57    if in_list and not self.negate or self.negate and not in_list:
58      return self.nodelist_true.render(context)
59    else:
60      return self.nodelist_false.render(context)
61
62
63@register.tag(name='IfInList')
64def do_if_in_list(parser, token):
65  bits = token.contents.split()
66  tag_name, var_not, var_in = None, None, None
67  if len(bits) == 4:
68    tag_name, obj, var_in, list = token.split_contents()
69  elif len(bits) == 5:
70    tag_name, obj, var_not, var_in, list = token.split_contents()
71  if not tag_name  \
72      or (var_not is not None and var_not != 'not') \
73      or (var_in is not None and var_in != 'in'):
74        raise TemplateSyntaxError("Invalid format! 'IfInList obj [not] in list'. You had: %s" % token.contents)
75  negate = var_not is not None
76  list = parser.compile_filter(list)
77  nodelist_true = parser.parse(('else', 'endif'))
78  tok = parser.next_token()
79  if tok.contents == 'else':
80    nodelist_false = parser.parse(('endif',))
81    parser.delete_first_token()
82  else:
83    nodelist_false = NodeList()
84  return IfInListNode(obj, list, nodelist_true, nodelist_false, negate)
Back to Top