Implemented 'smart if' template tag, allowing filters and various operators to be used in the 'if' tag

Thanks to Chris Beaven for the initial patch, Fredrik Lundh for the basis
of the parser methodology and Russell Keith-Magee for code reviews.

There are some BACKWARDS INCOMPATIBILITIES in rare cases - in particular, if
you were using the keywords 'and', 'or' or 'not' as variable names within
the 'if' expression, which was previously allowed in some cases.



git-svn-id: http://code.djangoproject.com/svn/django/trunk@11806 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Luke Plant 2009-12-09 22:40:36 +00:00
parent 25020ddb05
commit 2c2f5aee4d
7 changed files with 514 additions and 93 deletions

View file

@ -313,6 +313,9 @@ displayed by the ``{{ athlete_list|length }}`` variable.
As you can see, the ``if`` tag can take an optional ``{% else %}`` clause that
will be displayed if the test fails.
Boolean operators
^^^^^^^^^^^^^^^^^
``if`` tags may use ``and``, ``or`` or ``not`` to test a number of variables or
to negate a given variable::
@ -338,24 +341,153 @@ to negate a given variable::
There are some athletes and absolutely no coaches.
{% endif %}
``if`` tags don't allow ``and`` and ``or`` clauses within the same tag, because
the order of logic would be ambiguous. For example, this is invalid::
.. versionchanged:: 1.2
Use of both ``and`` and ``or`` clauses within the same tag is allowed, with
``and`` having higher precedence than ``or`` e.g.::
{% if athlete_list and coach_list or cheerleader_list %}
If you need to combine ``and`` and ``or`` to do advanced logic, just use nested
``if`` tags. For example::
will be interpreted like:
{% if athlete_list %}
{% if coach_list or cheerleader_list %}
We have athletes, and either coaches or cheerleaders!
{% endif %}
.. code-block:: python
if (athlete_list and coach_list) or cheerleader_list
Use of actual brackets in the ``if`` tag is invalid syntax. If you need them to
indicate precedence, you should use nested ``if`` tags.
.. versionadded:: 1.2
``if`` tags may also use the operators ``==``, ``!=``, ``<``, ``>``,
``<=``, ``>=`` and ``in`` which work as follows:
``==`` operator
^^^^^^^^^^^^^^^
Equality. Example::
{% if somevar == "x" %}
This appears if variable somevar equals the string "x"
{% endif %}
Multiple uses of the same logical operator are fine, as long as you use the
same operator. For example, this is valid::
``!=`` operator
^^^^^^^^^^^^^^^
Inequality. Example::
{% if somevar != "x" %}
This appears if variable somevar does not equal the string "x",
or if somevar is not found in the context
{% endif %}
``<`` operator
^^^^^^^^^^^^^^
Less than. Example::
{% if somevar < 100 %}
This appears if variable somevar is less than 100.
{% endif %}
``>`` operator
^^^^^^^^^^^^^^
Greater than. Example::
{% if somevar > 0 %}
This appears if variable somevar is greater than 0.
{% endif %}
``<=`` operator
^^^^^^^^^^^^^^^
Less than or equal to. Example::
{% if somevar <= 100 %}
This appears if variable somevar is less than 100 or equal to 100.
{% endif %}
``>=`` operator
^^^^^^^^^^^^^^^
Greater than or equal to. Example::
{% if somevar >= 1 %}
This appears if variable somevar is greater than 1 or equal to 1.
{% endif %}
``in`` operator
^^^^^^^^^^^^^^^
Contained within. This operator is supported by many Python containers to test
whether the given value is in the container. The following are some examples of
how ``x in y`` will be interpreted::
{% if "bc" in "abcdef" %}
This appears since "bc" is a substring of "abcdef"
{% endif %}
{% if "hello" in greetings %}
If greetings is a list or set, one element of which is the string
"hello", this will appear.
{% endif %}
{% if user in users %}
If users is a QuerySet, this will appear if user is an
instance that belongs to the QuerySet.
{% endif %}
The comparison operators cannot be 'chained' like in Python or in mathematical
notation. For example, instead of using::
{% if a > b > c %} (WRONG)
you should use::
{% if a > b and b > c %}
Filters
^^^^^^^
You can also use filters in the ``if`` expression. For example::
{% if messages|length >= 100 %}
You have lots of messages today!
{% endif %}
Complex expressions
^^^^^^^^^^^^^^^^^^^
All of the above can be combined to form complex expressions. For such
expressions, it can be important to know how the operators are grouped when the
expression is evaluated - that is, the precedence rules. The precedence of the
operators, from lowest to highest, is as follows:
* ``or``
* ``and``
* ``not``
* ``in``
* ``==``, ``!=``, ``<``, ``>``,``<=``, ``>=``
(This follows Python exactly). So, for example, the following complex if tag:
{% if a == b or c == d and e %}
...will be interpreted as:
.. code-block:: python
(a == b) or ((c == d) and e)
If you need different precedence, you will need to use nested if tags. Sometimes
that is better for clarity anyway, for the sake of those who do not know the
precedence rules.
{% if athlete_list or coach_list or parent_list or teacher_list %}
.. templatetag:: ifchanged
@ -427,6 +559,9 @@ You cannot check for equality with Python objects such as ``True`` or
``False``. If you need to test if something is true or false, use the ``if``
tag instead.
.. versionadded:: 1.2
An alternative to the ``ifequal`` tag is to use the :ttag:`if` tag and the ``==`` operator.
.. templatetag:: ifnotequal
ifnotequal
@ -434,6 +569,9 @@ ifnotequal
Just like ``ifequal``, except it tests that the two arguments are not equal.
.. versionadded:: 1.2
An alternative to the ``ifnotequal`` tag is to use the :ttag:`if` tag and the ``!=`` operator.
.. templatetag:: include
include