Browse Source

Fixed #15057 - documented change in [14992]

Thanks to Tai Lee for the patch.

Refs #15025, #7153

git-svn-id: http://code.djangoproject.com/svn/django/trunk@15188 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Luke Plant 14 years ago
parent
commit
7b8c38250c
3 changed files with 50 additions and 29 deletions
  1. 3 3
      docs/intro/tutorial03.txt
  2. 31 26
      docs/ref/templates/api.txt
  3. 16 0
      docs/releases/1.3.txt

+ 3 - 3
docs/intro/tutorial03.txt

@@ -415,9 +415,9 @@ like:
 
 The template system uses dot-lookup syntax to access variable attributes. In
 the example of ``{{ poll.question }}``, first Django does a dictionary lookup
-on the object ``poll``. Failing that, it tries attribute lookup -- which works,
-in this case. If attribute lookup had failed, it would've tried calling the
-method ``question()`` on the poll object.
+on the object ``poll``. Failing that, it tries an attribute lookup -- which
+works, in this case. If attribute lookup had failed, it would've tried a
+list-index lookup.
 
 Method-calling happens in the ``{% for %}`` loop: ``poll.choice_set.all`` is
 interpreted as the Python code ``poll.choice_set.all()``, which returns an

+ 31 - 26
docs/ref/templates/api.txt

@@ -115,18 +115,15 @@ Variable names must consist of any letter (A-Z), any digit (0-9), an underscore
 or a dot.
 
 Dots have a special meaning in template rendering. A dot in a variable name
-signifies **lookup**. Specifically, when the template system encounters a dot
-in a variable name, it tries the following lookups, in this order:
+signifies a **lookup**. Specifically, when the template system encounters a
+dot in a variable name, it tries the following lookups, in this order:
 
     * Dictionary lookup. Example: ``foo["bar"]``
     * Attribute lookup. Example: ``foo.bar``
-    * Method call. Example: ``foo.bar()``
     * List-index lookup. Example: ``foo[bar]``
 
 The template system uses the first lookup type that works. It's short-circuit
-logic.
-
-Here are a few examples::
+logic. Here are a few examples::
 
     >>> from django.template import Context, Template
     >>> t = Template("My name is {{ person.first_name }}.")
@@ -141,26 +138,34 @@ Here are a few examples::
     >>> t.render(Context({"person": p}))
     "My name is Ron."
 
-    >>> class PersonClass2:
-    ...     def first_name(self):
-    ...         return "Samantha"
-    >>> p = PersonClass2()
-    >>> t.render(Context({"person": p}))
-    "My name is Samantha."
-
     >>> t = Template("The first stooge in the list is {{ stooges.0 }}.")
     >>> c = Context({"stooges": ["Larry", "Curly", "Moe"]})
     >>> t.render(c)
     "The first stooge in the list is Larry."
 
-Method lookups are slightly more complex than the other lookup types. Here are
-some things to keep in mind:
+If any part of the variable is callable, the template system will try calling
+it. Example::
+
+    >>> class PersonClass2:
+    ...     def name(self):
+    ...         return "Samantha"
+    >>> t = Template("My name is {{ person.name }}.")
+    >>> t.render(Context({"person": PersonClass2}))
+    "My name is Samantha."
+
+.. versionchanged:: 1.3
+    Previously, only variables that originated with an attribute lookup would
+    be called by the template system. This change was made for consistency
+    across lookup types.
+
+Callable variables are slightly more complex than variables which only require
+straight lookups. Here are some things to keep in mind:
 
-    * If, during the method lookup, a method raises an exception, the exception
-      will be propagated, unless the exception has an attribute
+    * If the variable raises an exception when called, the exception will be
+      propagated, unless the exception has an attribute
       ``silent_variable_failure`` whose value is ``True``. If the exception
-      *does* have a ``silent_variable_failure`` attribute, the variable will
-      render as an empty string. Example::
+      *does* have a ``silent_variable_failure`` attribute whose value is
+      ``True``, the variable will render as an empty string. Example::
 
         >>> t = Template("My name is {{ person.first_name }}.")
         >>> class PersonClass3:
@@ -187,12 +192,12 @@ some things to keep in mind:
       with Django model objects, any ``DoesNotExist`` exception will fail
       silently.
 
-    * A method call will only work if the method has no required arguments.
-      Otherwise, the system will move to the next lookup type (list-index
-      lookup).
+    * A variable can only be called if it has no required arguments. Otherwise,
+      the system will return an empty string.
 
-    * Obviously, some methods have side effects, and it'd be either foolish or
-      a security hole to allow the template system to access them.
+    * Obviously, there can be side effects when calling some variables, and
+      it'd be either foolish or a security hole to allow the template system
+      to access them.
 
       A good example is the :meth:`~django.db.models.Model.delete` method on
       each Django model object. The template system shouldn't be allowed to do
@@ -200,8 +205,8 @@ some things to keep in mind:
 
         I will now delete this valuable data. {{ data.delete }}
 
-      To prevent this, set a function attribute ``alters_data`` on the method.
-      The template system won't execute a method if the method has
+      To prevent this, set an ``alters_data`` attribute on the callable
+      variable. The template system won't call a variable if it has
       ``alters_data=True`` set. The dynamically-generated
       :meth:`~django.db.models.Model.delete` and
       :meth:`~django.db.models.Model.save` methods on Django model objects get

+ 16 - 0
docs/releases/1.3.txt

@@ -392,7 +392,23 @@ if you need to instantiate an empty ``FormSet``, don't pass in the data or use
     >>> formset = ArticleFormSet()
     >>> formset = ArticleFormSet(data=None)
 
+Callables in templates
+~~~~~~~~~~~~~~~~~~~~~~
+
+Previously, a callable in a template would only be called automatically as part
+of the variable resolution process if it was retrieved via attribute
+lookup. This was an inconsistency that could result in confusing and unhelpful
+behaviour::
+
+    >>> Template("{{ user.get_full_name }}").render(Context({'user': user}))
+    u'Joe Bloggs'
+    >>> Template("{{ full_name }}").render(Context({'full_name': user.get_full_name}))
+    u'<bound method User.get_full_name of <...
 
+This has been resolved in Django 1.3 - the result in both cases will be ``u'Joe
+Bloggs'``. Although the previous behaviour was not useful for a template language
+designed for web designers, and was never deliberately supported, it is possible
+that some templates may be broken by this change.
 
 .. _deprecated-features-1.3: