Code

Ticket #8087: django-if-in-list.diff

File django-if-in-list.diff, 7.8 KB (added by tomevans222, 5 years ago)
Line 
1Index: django/template/defaulttags.py
2===================================================================
3--- django/template/defaulttags.py      (revision 10604)
4+++ django/template/defaulttags.py      (working copy)
5@@ -232,22 +232,46 @@
6         nodes.extend(self.nodelist_false.get_nodes_by_type(nodetype))
7         return nodes
8 
9+    def __in_list_helper(self, context, list_, val_):
10+        from django.db.models.query import QuerySet
11+        val_ = val_.resolve(context, True)
12+        list_ = list_.resolve(context, True)
13+        if isinstance(list_, QuerySet):
14+            if hasattr(val_, 'pk'):
15+                if list_._result_cache:
16+                    in_list = val_ in list_
17+                else:
18+                    in_list = list_.filter(pk = val_.pk).count() > 0
19+            else:
20+                in_list = False
21+        else:
22+            in_list = val_ in list_
23+        return in_list
24+
25     def render(self, context):
26         if self.link_type == IfNode.LinkTypes.or_:
27             for ifnot, bool_expr in self.bool_exprs:
28-                try:
29-                    value = bool_expr.resolve(context, True)
30-                except VariableDoesNotExist:
31-                    value = None
32+                if isinstance(bool_expr, tuple):
33+                    val_, list_ = bool_expr
34+                    value = self.__in_list_helper(context, list_, val_)
35+                else:
36+                    try:
37+                        value = bool_expr.resolve(context, True)
38+                    except VariableDoesNotExist:
39+                        value = None
40                 if (value and not ifnot) or (ifnot and not value):
41                     return self.nodelist_true.render(context)
42             return self.nodelist_false.render(context)
43         else:
44             for ifnot, bool_expr in self.bool_exprs:
45-                try:
46-                    value = bool_expr.resolve(context, True)
47-                except VariableDoesNotExist:
48-                    value = None
49+                if isinstance(bool_expr, tuple):
50+                    val_, list_ = bool_expr
51+                    value = self.__in_list_helper(context, list_, val_)
52+                else:
53+                    try:
54+                        value = bool_expr.resolve(context, True)
55+                    except VariableDoesNotExist:
56+                        value = None
57                 if not ((value and not ifnot) or (ifnot and not value)):
58                     return self.nodelist_false.render(context)
59             return self.nodelist_true.render(context)
60@@ -778,6 +802,27 @@
61             There are some athletes and absolutely no coaches.
62         {% endif %}
63 
64+    ``if`` tags may also use ``in`` to test whether an object is (or is not) in a
65+    list::
66+       
67+        {% if user in athlete_list %}
68+            User is an athlete
69+        {% endif %}
70+
71+        {% if user not in coach_list %}
72+            User is not a coach
73+        {% endif %}
74+
75+        {% if user in athlete_list and not user in coach_list %}
76+            User is an athlete, but not a coach
77+        {% endif %}
78+
79+    When using the ``in`` operator to test whether an element is in a QuerySet,
80+    the behaviour depends upon whether the queryset has been executed already.
81+    If it has already been executed, the behaviour is to search the result set
82+    manually. If it has not been executed, or no result set is available, it will
83+    filter down the QuerySet to check for just this item.
84+
85     ``if`` tags do not allow ``and`` and ``or`` clauses with the same tag,
86     because the order of logic would be ambigous. For example, this is
87     invalid::
88@@ -810,13 +855,29 @@
89             raise TemplateSyntaxError, "'if' tags can't mix 'and' and 'or'"
90     for boolpair in boolpairs:
91         if ' ' in boolpair:
92-            try:
93-                not_, boolvar = boolpair.split()
94-            except ValueError:
95-                raise TemplateSyntaxError, "'if' statement improperly formatted"
96-            if not_ != 'not':
97-                raise TemplateSyntaxError, "Expected 'not' in if statement"
98-            boolvars.append((True, parser.compile_filter(boolvar)))
99+            elems = boolpair.split()
100+            num_elems = len(elems)
101+            if num_elems == 2: # parse 'not val'
102+                not_, boolvar = elems
103+                if not_ != 'not':
104+                    raise TemplateSyntaxError, "Expected 'not' in if statement"
105+                boolvars.append((True, parser.compile_filter(boolvar)))
106+            elif num_elems == 3: # parse 'val in list'
107+                val_, in_, list_ = elems
108+                if in_ != 'in':
109+                    raise TemplateSyntaxError, "Expected 'in' in if statement"
110+                boolvars.append((False, (parser.compile_filter(val_), parser.compile_filter(list_))))
111+            elif num_elems == 4: # parse 'val not in list' and 'not val in list'
112+                val_, not_, in_, list_ = elems
113+                if val_ == 'not':
114+                    val_, not_ = not_, val_
115+                if not_ != 'not':
116+                    raise TemplateSyntaxError, "Expected 'not' in if statement"
117+                elif in_ != 'in':
118+                    raise TemplateSyntaxError, "Expected 'in' in if statement"
119+                boolvars.append((True, (parser.compile_filter(val_), parser.compile_filter(list_))))
120+            else:
121+                raise TemplateSyntaxError, "Invalid if statement"
122         else:
123             boolvars.append((False, parser.compile_filter(boolpair)))
124     nodelist_true = parser.parse(('else', 'endif'))
125Index: tests/regressiontests/templates/tests.py
126===================================================================
127--- tests/regressiontests/templates/tests.py    (revision 10604)
128+++ tests/regressiontests/templates/tests.py    (working copy)
129@@ -554,6 +554,22 @@
130             'if-tag-or07': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': True}, 'yes'),
131             'if-tag-or08': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'bar': True}, 'yes'),
132 
133+            # IN
134+            'if-tag-in01': ("{% if foo in list %}yes{% else %}no{% endif %}", {'foo': 'a', 'list': ['a', 'b', 'c']}, 'yes'),
135+            'if-tag-in02': ("{% if foo in list %}yes{% else %}no{% endif %}", {'foo': 'x', 'list': ['a', 'b', 'c']}, 'no'),
136+            'if-tag-in03': ("{% if not foo in list %}yes{% else %}no{% endif %}", {'foo': 'a', 'list': ['a', 'b', 'c']}, 'no'),
137+            'if-tag-in04': ("{% if not foo in list %}yes{% else %}no{% endif %}", {'foo': 'x', 'list': ['a', 'b', 'c']}, 'yes'),
138+            'if-tag-in05': ("{% if foo not in list %}yes{% else %}no{% endif %}", {'foo': 'a', 'list': ['a', 'b', 'c']}, 'no'),
139+            'if-tag-in06': ("{% if foo not in list %}yes{% else %}no{% endif %}", {'foo': 'x', 'list': ['a', 'b', 'c']}, 'yes'),
140+            'if-tag-in07': ("{% if foo and bar in list %}yes{% else %}no{% endif %}", {'foo': True, 'bar': 'a', 'list': ['a']}, 'yes'),
141+            'if-tag-in08': ("{% if bar in list and foo %}yes{% else %}no{% endif %}", {'foo': True, 'bar': 'a', 'list': ['a']}, 'yes'),
142+            'if-tag-in09': ("{% if foo and bar in list %}yes{% else %}no{% endif %}", {'foo': True, 'bar': 'b', 'list': ['a']}, 'no'),
143+            'if-tag-in10': ("{% if bar in list and foo %}yes{% else %}no{% endif %}", {'foo': True, 'bar': 'b', 'list': ['a']}, 'no'),
144+            'if-tag-in11': ("{% if foo or bar in list %}yes{% else %}no{% endif %}", {'foo': False, 'bar': 'a', 'list': ['a']}, 'yes'),
145+            'if-tag-in12': ("{% if bar in list or foo %}yes{% else %}no{% endif %}", {'foo': False, 'bar': 'a', 'list': ['a']}, 'yes'),
146+            'if-tag-in13': ("{% if foo or bar in list %}yes{% else %}no{% endif %}", {'foo': False, 'bar': 'b', 'list': ['a']}, 'no'),
147+            'if-tag-in14': ("{% if bar in list or foo %}yes{% else %}no{% endif %}", {'foo': False, 'bar': 'b', 'list': ['a']}, 'no'),
148+
149             # TODO: multiple ORs
150 
151             # NOT