summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Plant <L.Plant.98@cantab.net>2010-03-08 23:25:37 +0000
committerLuke Plant <L.Plant.98@cantab.net>2010-03-08 23:25:37 +0000
commit960af90279081e7a4121736f3f2bc67077f11b31 (patch)
treeb886c53a203dacdd717a30da98e2ca2dc5f04ef4
parent021ba30ad1d4fffada4f2d43d47a4704bcfca45e (diff)
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
-rw-r--r--django/template/defaulttags.py4
-rw-r--r--django/template/smartif.py17
-rw-r--r--docs/ref/templates/builtins.txt5
-rw-r--r--docs/releases/1.2.txt6
-rw-r--r--tests/regressiontests/templates/smartif.py7
-rw-r--r--tests/regressiontests/templates/tests.py6
6 files changed, 38 insertions, 7 deletions
diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py
index 4b0a4d1fa6..03a6245de8 100644
--- a/django/template/defaulttags.py
+++ b/django/template/defaulttags.py
@@ -806,8 +806,8 @@ def do_if(parser, token):
Arguments and operators _must_ have a space between them, so
``{% if 1>2 %}`` is not a valid if tag.
- All supported operators are: ``or``, ``and``, ``in``, ``==`` (or ``=``),
- ``!=``, ``>``, ``>=``, ``<`` and ``<=``.
+ All supported operators are: ``or``, ``and``, ``in``, ``not in``
+ ``==`` (or ``=``), ``!=``, ``>``, ``>=``, ``<`` and ``<=``.
Operator precedence follows Python.
"""
diff --git a/django/template/smartif.py b/django/template/smartif.py
index e8a08e9087..c4e3a06b7c 100644
--- a/django/template/smartif.py
+++ b/django/template/smartif.py
@@ -97,6 +97,7 @@ OPERATORS = {
'and': infix(7, lambda x, y: x and y),
'not': prefix(8, operator.not_),
'in': infix(9, lambda x, y: x in y),
+ 'not in': infix(9, lambda x, y: x not in y),
'=': infix(10, operator.eq),
'==': infix(10, operator.eq),
'!=': infix(10, operator.ne),
@@ -150,11 +151,23 @@ class IfParser(object):
error_class = ValueError
def __init__(self, tokens):
- self.tokens = map(self.translate_tokens, tokens)
+ # pre-pass necessary to turn 'not','in' into single token
+ l = len(tokens)
+ mapped_tokens = []
+ i = 0
+ while i < l:
+ token = tokens[i]
+ if token == "not" and i + 1 < l and tokens[i+1] == "in":
+ token = "not in"
+ i += 1 # skip 'in'
+ mapped_tokens.append(self.translate_token(token))
+ i += 1
+
+ self.tokens = mapped_tokens
self.pos = 0
self.current_token = self.next()
- def translate_tokens(self, token):
+ def translate_token(self, token):
try:
op = OPERATORS[token]
except (KeyError, TypeError):
diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt
index 8bc4f8e4f6..c613c58107 100644
--- a/docs/ref/templates/builtins.txt
+++ b/docs/ref/templates/builtins.txt
@@ -440,6 +440,11 @@ how ``x in y`` will be interpreted::
instance that belongs to the QuerySet.
{% endif %}
+``not in`` operator
+~~~~~~~~~~~~~~~~~~~~
+
+Not contained within. This is the negation of the ``in`` operator.
+
The comparison operators cannot be 'chained' like in Python or in mathematical
notation. For example, instead of using::
diff --git a/docs/releases/1.2.txt b/docs/releases/1.2.txt
index f30e84a77d..24dc483f73 100644
--- a/docs/releases/1.2.txt
+++ b/docs/releases/1.2.txt
@@ -636,9 +636,9 @@ You can now do this:
There's really no reason to use ``{% ifequal %}`` or ``{% ifnotequal %}``
anymore, unless you're the nostalgic type.
-The operators supported are ``==``, ``!=``, ``<``, ``>``, ``<=``, ``>=`` and
-``in``, all of which work like the Python operators, in addition to ``and``,
-``or`` and ``not``, which were already supported.
+The operators supported are ``==``, ``!=``, ``<``, ``>``, ``<=``, ``>=``,
+``in`` and ``not in``, all of which work like the Python operators, in addition
+ to ``and``, ``or`` and ``not``, which were already supported.
Also, filters may now be used in the ``if`` expression. For example:
diff --git a/tests/regressiontests/templates/smartif.py b/tests/regressiontests/templates/smartif.py
index 0114753830..5e5d7704e7 100644
--- a/tests/regressiontests/templates/smartif.py
+++ b/tests/regressiontests/templates/smartif.py
@@ -27,6 +27,13 @@ class SmartIfTests(unittest.TestCase):
self.assertCalcEqual(False, [1, 'in', None])
self.assertCalcEqual(False, [None, 'in', list_])
+ def test_not_in(self):
+ list_ = [1,2,3]
+ self.assertCalcEqual(False, [1, 'not', 'in', list_])
+ self.assertCalcEqual(True, [4, 'not', 'in', list_])
+ self.assertCalcEqual(False, [1, 'not', 'in', None])
+ self.assertCalcEqual(True, [None, 'not', 'in', list_])
+
def test_precedence(self):
# (False and False) or True == True <- we want this one, like Python
# False and (False or True) == False
diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py
index 1af3637158..e603ec33c2 100644
--- a/tests/regressiontests/templates/tests.py
+++ b/tests/regressiontests/templates/tests.py
@@ -611,6 +611,12 @@ class Templates(unittest.TestCase):
'if-tag-lte-01': ("{% if 1 <= 1 %}yes{% else %}no{% endif %}", {}, "yes"),
'if-tag-lte-02': ("{% if 2 <= 1 %}yes{% else %}no{% endif %}", {}, "no"),
+ # Contains
+ 'if-tag-in-01': ("{% if 1 in x %}yes{% else %}no{% endif %}", {'x':[1]}, "yes"),
+ 'if-tag-in-02': ("{% if 2 in x %}yes{% else %}no{% endif %}", {'x':[1]}, "no"),
+ 'if-tag-not-in-01': ("{% if 1 not in x %}yes{% else %}no{% endif %}", {'x':[1]}, "no"),
+ 'if-tag-not-in-02': ("{% if 2 not in x %}yes{% else %}no{% endif %}", {'x':[1]}, "yes"),
+
# AND
'if-tag-and01': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'yes'),
'if-tag-and02': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'no'),