From 524e71c9c20da57722b22b978d87690379ca4ade Mon Sep 17 00:00:00 2001
From: Daniel Roseman <daniel@roseman.org.uk>
Date: Sat, 15 Nov 2014 12:08:40 +0000
Subject: [PATCH] Fixed #20435 -- Reordered the custom template tags docs.

Introduced the various shortcuts before explaining the more complex
parser/render functionality.

Also removed non-decorator syntax: it's been years since Django
supported a Python version without decorators.
---
 docs/howto/custom-template-tags.txt | 603 ++++++++++++++--------------
 1 file changed, 293 insertions(+), 310 deletions(-)

diff --git a/docs/howto/custom-template-tags.txt b/docs/howto/custom-template-tags.txt
index 321185407b..d69cedad35 100644
--- a/docs/howto/custom-template-tags.txt
+++ b/docs/howto/custom-template-tags.txt
@@ -372,7 +372,291 @@ conversions in templates <time-zones-in-templates>`.
 Writing custom template tags
 ----------------------------
 
-Tags are more complex than filters, because tags can do anything.
+Tags are more complex than filters, because tags can do anything. Django
+provides a number of shortcuts that make writing most types of tags easier.
+First we'll explore those shortcuts, then explain how to write a tag from
+scratch for those cases when the shortcuts aren't powerful enough.
+
+.. _howto-custom-template-tags-simple-tags:
+
+Simple tags
+~~~~~~~~~~~
+
+.. method:: django.template.Library.simple_tag()
+
+Many template tags take a number of arguments -- strings or template variables
+-- and return a string after doing some processing based solely on
+the input arguments and some external information. For example, a
+``current_time`` tag might accept a format string and return the time as a
+string formatted accordingly.
+
+To ease the creation of these types of tags, Django provides a helper function,
+``simple_tag``. This function, which is a method of
+``django.template.Library``, takes a function that accepts any number of
+arguments, wraps it in a ``render`` function and the other necessary bits
+mentioned above and registers it with the template system.
+
+Our ``current_time`` function could thus be written like this::
+
+    import datetime
+    from django import template
+
+    register = template.Library()
+
+    @register.simple_tag
+    def current_time(format_string):
+        return datetime.datetime.now().strftime(format_string)
+
+A few things to note about the ``simple_tag`` helper function:
+
+* Checking for the required number of arguments, etc., has already been
+  done by the time our function is called, so we don't need to do that.
+* The quotes around the argument (if any) have already been stripped away,
+  so we just receive a plain string.
+* If the argument was a template variable, our function is passed the
+  current value of the variable, not the variable itself.
+
+If your template tag needs to access the current context, you can use the
+``takes_context`` argument when registering your tag::
+
+    @register.simple_tag(takes_context=True)
+    def current_time(context, format_string):
+        timezone = context['timezone']
+        return your_get_current_time_method(timezone, format_string)
+
+Note that the first argument *must* be called ``context``.
+
+For more information on how the ``takes_context`` option works, see the section
+on :ref:`inclusion tags<howto-custom-template-tags-inclusion-tags>`.
+
+If you need to rename your tag, you can provide a custom name for it::
+
+    register.simple_tag(lambda x: x - 1, name='minusone')
+
+    @register.simple_tag(name='minustwo')
+    def some_function(value):
+        return value - 2
+
+``simple_tag`` functions may accept any number of positional or keyword
+arguments. For example::
+
+    @register.simple_tag
+    def my_tag(a, b, *args, **kwargs):
+        warning = kwargs['warning']
+        profile = kwargs['profile']
+        ...
+        return ...
+
+Then in the template any number of arguments, separated by spaces, may be
+passed to the template tag. Like in Python, the values for keyword arguments
+are set using the equal sign ("``=``") and must be provided after the
+positional arguments. For example:
+
+.. code-block:: html+django
+
+    {% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile %}
+
+.. _howto-custom-template-tags-inclusion-tags:
+
+Inclusion tags
+~~~~~~~~~~~~~~
+
+.. method:: django.template.Library.inclusion_tag()
+
+Another common type of template tag is the type that displays some data by
+rendering *another* template. For example, Django's admin interface uses custom
+template tags to display the buttons along the bottom of the "add/change" form
+pages. Those buttons always look the same, but the link targets change
+depending on the object being edited -- so they're a perfect case for using a
+small template that is filled with details from the current object. (In the
+admin's case, this is the ``submit_row`` tag.)
+
+These sorts of tags are called "inclusion tags".
+
+Writing inclusion tags is probably best demonstrated by example. Let's write a
+tag that outputs a list of choices for a given ``Poll`` object, such as was
+created in the :ref:`tutorials <creating-models>`. We'll use the tag like this:
+
+.. code-block:: html+django
+
+    {% show_results poll %}
+
+...and the output will be something like this:
+
+.. code-block:: html
+
+    <ul>
+      <li>First choice</li>
+      <li>Second choice</li>
+      <li>Third choice</li>
+    </ul>
+
+First, define the function that takes the argument and produces a dictionary of
+data for the result. The important point here is we only need to return a
+dictionary, not anything more complex. This will be used as a template context
+for the template fragment. Example::
+
+    def show_results(poll):
+        choices = poll.choice_set.all()
+        return {'choices': choices}
+
+Next, create the template used to render the tag's output. This template is a
+fixed feature of the tag: the tag writer specifies it, not the template
+designer. Following our example, the template is very simple:
+
+.. code-block:: html+django
+
+    <ul>
+    {% for choice in choices %}
+        <li> {{ choice }} </li>
+    {% endfor %}
+    </ul>
+
+Now, create and register the inclusion tag by calling the ``inclusion_tag()``
+method on a ``Library`` object. Following our example, if the above template is
+in a file called ``results.html`` in a directory that's searched by the
+template loader, we'd register the tag like this::
+
+    # Here, register is a django.template.Library instance, as before
+    @register.inclusion_tag('results.html')
+    def show_results(poll):
+        ...
+
+Alternatively it is possible to register the inclusion tag using a
+:class:`django.template.Template` instance::
+
+    from django.template.loader import get_template
+    t = get_template('results.html')
+    register.inclusion_tag(t)(show_results)
+
+...when first creating the function.
+
+Sometimes, your inclusion tags might require a large number of arguments,
+making it a pain for template authors to pass in all the arguments and remember
+their order. To solve this, Django provides a ``takes_context`` option for
+inclusion tags. If you specify ``takes_context`` in creating a template tag,
+the tag will have no required arguments, and the underlying Python function
+will have one argument -- the template context as of when the tag was called.
+
+For example, say you're writing an inclusion tag that will always be used in a
+context that contains ``home_link`` and ``home_title`` variables that point
+back to the main page. Here's what the Python function would look like::
+
+    @register.inclusion_tag('link.html', takes_context=True)
+    def jump_link(context):
+        return {
+            'link': context['home_link'],
+            'title': context['home_title'],
+        }
+
+Note that the first parameter to the function *must* be called ``context``.
+
+In that ``register.inclusion_tag()`` line, we specified ``takes_context=True``
+and the name of the template. Here's what the template ``link.html`` might look
+like:
+
+.. code-block:: html+django
+
+    Jump directly to <a href="{{ link }}">{{ title }}</a>.
+
+Then, any time you want to use that custom tag, load its library and call it
+without any arguments, like so:
+
+.. code-block:: html+django
+
+    {% jump_link %}
+
+Note that when you're using ``takes_context=True``, there's no need to pass
+arguments to the template tag. It automatically gets access to the context.
+
+The ``takes_context`` parameter defaults to ``False``. When it's set to
+``True``, the tag is passed the context object, as in this example. That's the
+only difference between this case and the previous ``inclusion_tag`` example.
+
+``inclusion_tag`` functions may accept any number of positional or keyword
+arguments. For example::
+
+    @register.inclusion_tag('my_template.html')
+    def my_tag(a, b, *args, **kwargs):
+        warning = kwargs['warning']
+        profile = kwargs['profile']
+        ...
+        return ...
+
+Then in the template any number of arguments, separated by spaces, may be
+passed to the template tag. Like in Python, the values for keyword arguments
+are set using the equal sign ("``=``") and must be provided after the
+positional arguments. For example:
+
+.. code-block:: html+django
+
+    {% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile %}
+
+.. _howto-custom-template-tags-assignment-tags:
+
+Assignment tags
+~~~~~~~~~~~~~~~
+
+.. method:: django.template.Library.assignment_tag()
+
+To ease the creation of tags setting a variable in the context, Django provides
+a helper function, ``assignment_tag``. This function works the same way as
+:ref:`simple_tag<howto-custom-template-tags-simple-tags>`, except that it
+stores the tag's result in a specified context variable instead of directly
+outputting it.
+
+Our earlier ``current_time`` function could thus be written like this::
+
+    @register.assignment_tag
+    def get_current_time(format_string):
+        return datetime.datetime.now().strftime(format_string)
+
+You may then store the result in a template variable using the ``as`` argument
+followed by the variable name, and output it yourself where you see fit:
+
+.. code-block:: html+django
+
+    {% get_current_time "%Y-%m-%d %I:%M %p" as the_time %}
+    <p>The time is {{ the_time }}.</p>
+
+If your template tag needs to access the current context, you can use the
+``takes_context`` argument when registering your tag::
+
+    @register.assignment_tag(takes_context=True)
+    def get_current_time(context, format_string):
+        timezone = context['timezone']
+        return your_get_current_time_method(timezone, format_string)
+
+Note that the first parameter to the function *must* be called ``context``.
+
+For more information on how the ``takes_context`` option works, see the section
+on :ref:`inclusion tags<howto-custom-template-tags-inclusion-tags>`.
+
+``assignment_tag`` functions may accept any number of positional or keyword
+arguments. For example::
+
+    @register.assignment_tag
+    def my_tag(a, b, *args, **kwargs):
+        warning = kwargs['warning']
+        profile = kwargs['profile']
+        ...
+        return ...
+
+Then in the template any number of arguments, separated by spaces, may be
+passed to the template tag. Like in Python, the values for keyword arguments
+are set using the equal sign ("``=``") and must be provided after the
+positional arguments. For example:
+
+.. code-block:: html+django
+
+    {% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile as the_result %}
+
+Advanced custom template tags
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Sometimes the basic features for custom template tag creation aren't enough.
+Don't worry, Django gives you complete access to the internals required to build
+a template tag from the ground up.
 
 A quick overview
 ~~~~~~~~~~~~~~~~
@@ -399,10 +683,11 @@ For each template tag the template parser encounters, it calls a Python
 function with the tag contents and the parser object itself. This function is
 responsible for returning a ``Node`` instance based on the contents of the tag.
 
-For example, let's write a template tag, ``{% current_time %}``, that displays
-the current date/time, formatted according to a parameter given in the tag, in
-:func:`~time.strftime` syntax. It's a good idea to decide the tag syntax before
-anything else. In our case, let's say the tag should be used like this:
+For example, let's write a full implementation of our simple template tag,
+``{% current_time %}``, that displays the current date/time, formatted according
+to a parameter given in the tag, in :func:`~time.strftime` syntax. It's a good
+idea to decide the tag syntax before anything else. In our case, let's say the
+tag should be used like this:
 
 .. code-block:: html+django
 
@@ -708,239 +993,6 @@ for example::
 Variable resolution will throw a ``VariableDoesNotExist`` exception if it
 cannot resolve the string passed to it in the current context of the page.
 
-.. _howto-custom-template-tags-simple-tags:
-
-Simple tags
-~~~~~~~~~~~
-
-.. method:: django.template.Library.simple_tag()
-
-Many template tags take a number of arguments -- strings or template variables
--- and return a string after doing some processing based solely on
-the input arguments and some external information. For example, the
-``current_time`` tag we wrote above is of this variety: we give it a format
-string, it returns the time as a string.
-
-To ease the creation of these types of tags, Django provides a helper function,
-``simple_tag``. This function, which is a method of
-``django.template.Library``, takes a function that accepts any number of
-arguments, wraps it in a ``render`` function and the other necessary bits
-mentioned above and registers it with the template system.
-
-Our earlier ``current_time`` function could thus be written like this::
-
-    import datetime
-    from django import template
-
-    register = template.Library()
-
-    def current_time(format_string):
-        return datetime.datetime.now().strftime(format_string)
-
-    register.simple_tag(current_time)
-
-The decorator syntax also works::
-
-    @register.simple_tag
-    def current_time(format_string):
-        ...
-
-A few things to note about the ``simple_tag`` helper function:
-
-* Checking for the required number of arguments, etc., has already been
-  done by the time our function is called, so we don't need to do that.
-* The quotes around the argument (if any) have already been stripped away,
-  so we just receive a plain string.
-* If the argument was a template variable, our function is passed the
-  current value of the variable, not the variable itself.
-
-If your template tag needs to access the current context, you can use the
-``takes_context`` argument when registering your tag::
-
-    # The first argument *must* be called "context" here.
-    def current_time(context, format_string):
-        timezone = context['timezone']
-        return your_get_current_time_method(timezone, format_string)
-
-    register.simple_tag(takes_context=True)(current_time)
-
-Or, using decorator syntax::
-
-    @register.simple_tag(takes_context=True)
-    def current_time(context, format_string):
-        timezone = context['timezone']
-        return your_get_current_time_method(timezone, format_string)
-
-For more information on how the ``takes_context`` option works, see the section
-on :ref:`inclusion tags<howto-custom-template-tags-inclusion-tags>`.
-
-If you need to rename your tag, you can provide a custom name for it::
-
-    register.simple_tag(lambda x: x - 1, name='minusone')
-
-    @register.simple_tag(name='minustwo')
-    def some_function(value):
-        return value - 2
-
-``simple_tag`` functions may accept any number of positional or keyword
-arguments. For example::
-
-    @register.simple_tag
-    def my_tag(a, b, *args, **kwargs):
-        warning = kwargs['warning']
-        profile = kwargs['profile']
-        ...
-        return ...
-
-Then in the template any number of arguments, separated by spaces, may be
-passed to the template tag. Like in Python, the values for keyword arguments
-are set using the equal sign ("``=``") and must be provided after the
-positional arguments. For example:
-
-.. code-block:: html+django
-
-    {% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile %}
-
-.. _howto-custom-template-tags-inclusion-tags:
-
-Inclusion tags
-~~~~~~~~~~~~~~
-
-Another common type of template tag is the type that displays some data by
-rendering *another* template. For example, Django's admin interface uses custom
-template tags to display the buttons along the bottom of the "add/change" form
-pages. Those buttons always look the same, but the link targets change
-depending on the object being edited -- so they're a perfect case for using a
-small template that is filled with details from the current object. (In the
-admin's case, this is the ``submit_row`` tag.)
-
-These sorts of tags are called "inclusion tags".
-
-Writing inclusion tags is probably best demonstrated by example. Let's write a
-tag that outputs a list of choices for a given ``Poll`` object, such as was
-created in the :ref:`tutorials <creating-models>`. We'll use the tag like this:
-
-.. code-block:: html+django
-
-    {% show_results poll %}
-
-...and the output will be something like this:
-
-.. code-block:: html
-
-    <ul>
-      <li>First choice</li>
-      <li>Second choice</li>
-      <li>Third choice</li>
-    </ul>
-
-First, define the function that takes the argument and produces a dictionary of
-data for the result. The important point here is we only need to return a
-dictionary, not anything more complex. This will be used as a template context
-for the template fragment. Example::
-
-    def show_results(poll):
-        choices = poll.choice_set.all()
-        return {'choices': choices}
-
-Next, create the template used to render the tag's output. This template is a
-fixed feature of the tag: the tag writer specifies it, not the template
-designer. Following our example, the template is very simple:
-
-.. code-block:: html+django
-
-    <ul>
-    {% for choice in choices %}
-        <li> {{ choice }} </li>
-    {% endfor %}
-    </ul>
-
-Now, create and register the inclusion tag by calling the ``inclusion_tag()``
-method on a ``Library`` object. Following our example, if the above template is
-in a file called ``results.html`` in a directory that's searched by the
-template loader, we'd register the tag like this::
-
-    # Here, register is a django.template.Library instance, as before
-    register.inclusion_tag('results.html')(show_results)
-
-Alternatively it is possible to register the inclusion tag using a
-:class:`django.template.Template` instance::
-
-    from django.template.loader import get_template
-    t = get_template('results.html')
-    register.inclusion_tag(t)(show_results)
-
-As always, decorator syntax works as well, so we could have written::
-
-    @register.inclusion_tag('results.html')
-    def show_results(poll):
-        ...
-
-...when first creating the function.
-
-Sometimes, your inclusion tags might require a large number of arguments,
-making it a pain for template authors to pass in all the arguments and remember
-their order. To solve this, Django provides a ``takes_context`` option for
-inclusion tags. If you specify ``takes_context`` in creating a template tag,
-the tag will have no required arguments, and the underlying Python function
-will have one argument -- the template context as of when the tag was called.
-
-For example, say you're writing an inclusion tag that will always be used in a
-context that contains ``home_link`` and ``home_title`` variables that point
-back to the main page. Here's what the Python function would look like::
-
-    # The first argument *must* be called "context" here.
-    def jump_link(context):
-        return {
-            'link': context['home_link'],
-            'title': context['home_title'],
-        }
-    # Register the custom tag as an inclusion tag with takes_context=True.
-    register.inclusion_tag('link.html', takes_context=True)(jump_link)
-
-(Note that the first parameter to the function *must* be called ``context``.)
-
-In that ``register.inclusion_tag()`` line, we specified ``takes_context=True``
-and the name of the template. Here's what the template ``link.html`` might look
-like:
-
-.. code-block:: html+django
-
-    Jump directly to <a href="{{ link }}">{{ title }}</a>.
-
-Then, any time you want to use that custom tag, load its library and call it
-without any arguments, like so:
-
-.. code-block:: html+django
-
-    {% jump_link %}
-
-Note that when you're using ``takes_context=True``, there's no need to pass
-arguments to the template tag. It automatically gets access to the context.
-
-The ``takes_context`` parameter defaults to ``False``. When it's set to
-``True``, the tag is passed the context object, as in this example. That's the
-only difference between this case and the previous ``inclusion_tag`` example.
-
-``inclusion_tag`` functions may accept any number of positional or keyword
-arguments. For example::
-
-    @register.inclusion_tag('my_template.html')
-    def my_tag(a, b, *args, **kwargs):
-        warning = kwargs['warning']
-        profile = kwargs['profile']
-        ...
-        return ...
-
-Then in the template any number of arguments, separated by spaces, may be
-passed to the template tag. Like in Python, the values for keyword arguments
-are set using the equal sign ("``=``") and must be provided after the
-positional arguments. For example:
-
-.. code-block:: html+django
-
-    {% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile %}
-
 Setting a variable in the context
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -1024,79 +1076,10 @@ The difference here is that ``do_current_time()`` grabs the format string and
 the variable name, passing both to ``CurrentTimeNode3``.
 
 Finally, if you only need to have a simple syntax for your custom
-context-updating template tag, you might want to consider using an
-:ref:`assignment tag <howto-custom-template-tags-assignment-tags>`.
+context-updating template tag, you might want to consider using the
+:ref:`assignment tag <howto-custom-template-tags-assignment-tags>` shortcut
+we introduced above.
 
-.. _howto-custom-template-tags-assignment-tags:
-
-Assignment tags
-~~~~~~~~~~~~~~~
-
-To ease the creation of tags setting a variable in the context, Django provides
-a helper function, ``assignment_tag``. This function works the same way as
-:ref:`simple_tag<howto-custom-template-tags-simple-tags>`, except that it
-stores the tag's result in a specified context variable instead of directly
-outputting it.
-
-Our earlier ``current_time`` function could thus be written like this::
-
-    def get_current_time(format_string):
-        return datetime.datetime.now().strftime(format_string)
-
-    register.assignment_tag(get_current_time)
-
-The decorator syntax also works::
-
-    @register.assignment_tag
-    def get_current_time(format_string):
-        ...
-
-You may then store the result in a template variable using the ``as`` argument
-followed by the variable name, and output it yourself where you see fit:
-
-.. code-block:: html+django
-
-    {% get_current_time "%Y-%m-%d %I:%M %p" as the_time %}
-    <p>The time is {{ the_time }}.</p>
-
-If your template tag needs to access the current context, you can use the
-``takes_context`` argument when registering your tag::
-
-    # The first argument *must* be called "context" here.
-    def get_current_time(context, format_string):
-        timezone = context['timezone']
-        return your_get_current_time_method(timezone, format_string)
-
-    register.assignment_tag(takes_context=True)(get_current_time)
-
-Or, using decorator syntax::
-
-    @register.assignment_tag(takes_context=True)
-    def get_current_time(context, format_string):
-        timezone = context['timezone']
-        return your_get_current_time_method(timezone, format_string)
-
-For more information on how the ``takes_context`` option works, see the section
-on :ref:`inclusion tags<howto-custom-template-tags-inclusion-tags>`.
-
-``assignment_tag`` functions may accept any number of positional or keyword
-arguments. For example::
-
-    @register.assignment_tag
-    def my_tag(a, b, *args, **kwargs):
-        warning = kwargs['warning']
-        profile = kwargs['profile']
-        ...
-        return ...
-
-Then in the template any number of arguments, separated by spaces, may be
-passed to the template tag. Like in Python, the values for keyword arguments
-are set using the equal sign ("``=``") and must be provided after the
-positional arguments. For example:
-
-.. code-block:: html+django
-
-    {% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile as the_result %}
 
 Parsing until another block tag
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~