Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #13058 - "smart if" template tag doesn't support "if not in ...…

…" condition

Thanks to ramusus for the report.



git-svn-id: http://code.djangoproject.com/svn/django/trunk@12732 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 960af90279081e7a4121736f3f2bc67077f11b31 1 parent 021ba30
Luke Plant authored March 08, 2010
4  django/template/defaulttags.py
@@ -806,8 +806,8 @@ def do_if(parser, token):
806 806
     Arguments and operators _must_ have a space between them, so
807 807
     ``{% if 1>2 %}`` is not a valid if tag.
808 808
 
809  
-    All supported operators are: ``or``, ``and``, ``in``, ``==`` (or ``=``),
810  
-    ``!=``, ``>``, ``>=``, ``<`` and ``<=``.
  809
+    All supported operators are: ``or``, ``and``, ``in``, ``not in``
  810
+    ``==`` (or ``=``), ``!=``, ``>``, ``>=``, ``<`` and ``<=``.
811 811
 
812 812
     Operator precedence follows Python.
813 813
     """
17  django/template/smartif.py
@@ -97,6 +97,7 @@ def eval(self, context):
97 97
     'and': infix(7, lambda x, y: x and y),
98 98
     'not': prefix(8, operator.not_),
99 99
     'in': infix(9, lambda x, y: x in y),
  100
+    'not in': infix(9, lambda x, y: x not in y),
100 101
     '=': infix(10, operator.eq),
101 102
     '==': infix(10, operator.eq),
102 103
     '!=': infix(10, operator.ne),
@@ -150,11 +151,23 @@ class IfParser(object):
150 151
     error_class = ValueError
151 152
 
152 153
     def __init__(self, tokens):
153  
-        self.tokens = map(self.translate_tokens, tokens)
  154
+        # pre-pass necessary to turn  'not','in' into single token 
  155
+        l = len(tokens)
  156
+        mapped_tokens = []
  157
+        i = 0
  158
+        while i < l:
  159
+            token = tokens[i]
  160
+            if token == "not" and i + 1 < l and tokens[i+1] == "in":
  161
+                token = "not in"
  162
+                i += 1 # skip 'in'
  163
+            mapped_tokens.append(self.translate_token(token))
  164
+            i += 1
  165
+
  166
+        self.tokens = mapped_tokens
154 167
         self.pos = 0
155 168
         self.current_token = self.next()
156 169
 
157  
-    def translate_tokens(self, token):
  170
+    def translate_token(self, token):
158 171
         try:
159 172
             op = OPERATORS[token]
160 173
         except (KeyError, TypeError):
5  docs/ref/templates/builtins.txt
@@ -440,6 +440,11 @@ how ``x in y`` will be interpreted::
440 440
       instance that belongs to the QuerySet.
441 441
     {% endif %}
442 442
 
  443
+``not in`` operator
  444
+~~~~~~~~~~~~~~~~~~~~
  445
+
  446
+Not contained within.  This is the negation of the ``in`` operator.
  447
+
443 448
 
444 449
 The comparison operators cannot be 'chained' like in Python or in mathematical
445 450
 notation. For example, instead of using::
6  docs/releases/1.2.txt
@@ -636,9 +636,9 @@ You can now do this:
636 636
 There's really no reason to use ``{% ifequal %}`` or ``{% ifnotequal %}``
637 637
 anymore, unless you're the nostalgic type.
638 638
 
639  
-The operators supported are ``==``, ``!=``, ``<``, ``>``, ``<=``, ``>=`` and
640  
-``in``, all of which work like the Python operators, in addition to ``and``,
641  
-``or`` and ``not``, which were already supported.
  639
+The operators supported are ``==``, ``!=``, ``<``, ``>``, ``<=``, ``>=``,
  640
+``in`` and ``not in``, all of which work like the Python operators, in addition
  641
+ to ``and``, ``or`` and ``not``, which were already supported.
642 642
 
643 643
 Also, filters may now be used in the ``if`` expression. For example:
644 644
 
7  tests/regressiontests/templates/smartif.py
@@ -27,6 +27,13 @@ def test_in(self):
27 27
         self.assertCalcEqual(False, [1, 'in', None])
28 28
         self.assertCalcEqual(False, [None, 'in', list_])
29 29
 
  30
+    def test_not_in(self):
  31
+        list_ = [1,2,3]
  32
+        self.assertCalcEqual(False, [1, 'not', 'in', list_])
  33
+        self.assertCalcEqual(True, [4, 'not', 'in', list_])
  34
+        self.assertCalcEqual(False, [1, 'not', 'in', None])
  35
+        self.assertCalcEqual(True, [None, 'not', 'in', list_])
  36
+
30 37
     def test_precedence(self):
31 38
         # (False and False) or True == True   <- we want this one, like Python
32 39
         # False and (False or True) == False
6  tests/regressiontests/templates/tests.py
@@ -611,6 +611,12 @@ def get_template_tests(self):
611 611
             'if-tag-lte-01': ("{% if 1 <= 1 %}yes{% else %}no{% endif %}", {}, "yes"),
612 612
             'if-tag-lte-02': ("{% if 2 <= 1 %}yes{% else %}no{% endif %}", {}, "no"),
613 613
 
  614
+            # Contains
  615
+            'if-tag-in-01': ("{% if 1 in x %}yes{% else %}no{% endif %}", {'x':[1]}, "yes"),
  616
+            'if-tag-in-02': ("{% if 2 in x %}yes{% else %}no{% endif %}", {'x':[1]}, "no"),
  617
+            'if-tag-not-in-01': ("{% if 1 not in x %}yes{% else %}no{% endif %}", {'x':[1]}, "no"),
  618
+            'if-tag-not-in-02': ("{% if 2 not in x %}yes{% else %}no{% endif %}", {'x':[1]}, "yes"),
  619
+
614 620
             # AND
615 621
             'if-tag-and01': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'yes'),
616 622
             'if-tag-and02': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'no'),

0 notes on commit 960af90

Please sign in to comment.
Something went wrong with that request. Please try again.