Kaydet (Commit) 524e71c9 authored tarafından Daniel Roseman's avatar Daniel Roseman Kaydeden (comit) Julien Phalip

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.
üst fc364374
......@@ -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
......@@ -563,383 +848,150 @@ A naive implementation of ``CycleNode`` might look something like this::
class CycleNode(template.Node):
def __init__(self, cyclevars):
self.cycle_iter = itertools.cycle(cyclevars)
def render(self, context):
return next(self.cycle_iter)
But, suppose we have two templates rendering the template snippet from above at
the same time:
1. Thread 1 performs its first loop iteration, ``CycleNode.render()``
returns 'row1'
2. Thread 2 performs its first loop iteration, ``CycleNode.render()``
returns 'row2'
3. Thread 1 performs its second loop iteration, ``CycleNode.render()``
returns 'row1'
4. Thread 2 performs its second loop iteration, ``CycleNode.render()``
returns 'row2'
The CycleNode is iterating, but it's iterating globally. As far as Thread 1
and Thread 2 are concerned, it's always returning the same value. This is
obviously not what we want!
To address this problem, Django provides a ``render_context`` that's associated
with the ``context`` of the template that is currently being rendered. The
``render_context`` behaves like a Python dictionary, and should be used to
store ``Node`` state between invocations of the ``render`` method.
Let's refactor our ``CycleNode`` implementation to use the ``render_context``::
class CycleNode(template.Node):
def __init__(self, cyclevars):
self.cyclevars = cyclevars
def render(self, context):
if self not in context.render_context:
context.render_context[self] = itertools.cycle(self.cyclevars)
cycle_iter = context.render_context[self]
return next(cycle_iter)
Note that it's perfectly safe to store global information that will not change
throughout the life of the ``Node`` as an attribute. In the case of
``CycleNode``, the ``cyclevars`` argument doesn't change after the ``Node`` is
instantiated, so we don't need to put it in the ``render_context``. But state
information that is specific to the template that is currently being rendered,
like the current iteration of the ``CycleNode``, should be stored in the
``render_context``.
.. note::
Notice how we used ``self`` to scope the ``CycleNode`` specific information
within the ``render_context``. There may be multiple ``CycleNodes`` in a
given template, so we need to be careful not to clobber another node's
state information. The easiest way to do this is to always use ``self`` as
the key into ``render_context``. If you're keeping track of several state
variables, make ``render_context[self]`` a dictionary.
Registering the tag
~~~~~~~~~~~~~~~~~~~
Finally, register the tag with your module's ``Library`` instance, as explained
in "Writing custom template filters" above. Example::
register.tag('current_time', do_current_time)
The ``tag()`` method takes two arguments:
1. The name of the template tag -- a string. If this is left out, the
name of the compilation function will be used.
2. The compilation function -- a Python function (not the name of the
function as a string).
As with filter registration, it is also possible to use this as a decorator::
@register.tag(name="current_time")
def do_current_time(parser, token):
...
@register.tag
def shout(parser, token):
...
If you leave off the ``name`` argument, as in the second example above, Django
will use the function's name as the tag name.
Passing template variables to the tag
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Although you can pass any number of arguments to a template tag using
``token.split_contents()``, the arguments are all unpacked as
string literals. A little more work is required in order to pass dynamic
content (a template variable) to a template tag as an argument.
While the previous examples have formatted the current time into a string and
returned the string, suppose you wanted to pass in a
:class:`~django.db.models.DateTimeField` from an object and have the template
tag format that date-time:
.. code-block:: html+django
<p>This post was last updated at {% format_time blog_entry.date_updated "%Y-%m-%d %I:%M %p" %}.</p>
Initially, ``token.split_contents()`` will return three values:
1. The tag name ``format_time``.
2. The string ``"blog_entry.date_updated"`` (without the surrounding
quotes).
3. The formatting string ``"%Y-%m-%d %I:%M %p"``. The return value from
``split_contents()`` will include the leading and trailing quotes for
string literals like this.
Now your tag should begin to look like this::
from django import template
def do_format_time(parser, token):
try:
# split_contents() knows not to split quoted strings.
tag_name, date_to_be_formatted, format_string = token.split_contents()
except ValueError:
raise template.TemplateSyntaxError("%r tag requires exactly two arguments" % token.contents.split()[0])
if not (format_string[0] == format_string[-1] and format_string[0] in ('"', "'")):
raise template.TemplateSyntaxError("%r tag's argument should be in quotes" % tag_name)
return FormatTimeNode(date_to_be_formatted, format_string[1:-1])
You also have to change the renderer to retrieve the actual contents of the
``date_updated`` property of the ``blog_entry`` object. This can be
accomplished by using the ``Variable()`` class in ``django.template``.
To use the ``Variable`` class, simply instantiate it with the name of the
variable to be resolved, and then call ``variable.resolve(context)``. So,
for example::
class FormatTimeNode(template.Node):
def __init__(self, date_to_be_formatted, format_string):
self.date_to_be_formatted = template.Variable(date_to_be_formatted)
self.format_string = format_string
def render(self, context):
try:
actual_date = self.date_to_be_formatted.resolve(context)
return actual_date.strftime(self.format_string)
except template.VariableDoesNotExist:
return ''
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".
self.cycle_iter = itertools.cycle(cyclevars)
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:
def render(self, context):
return next(self.cycle_iter)
.. code-block:: html+django
But, suppose we have two templates rendering the template snippet from above at
the same time:
{% show_results poll %}
1. Thread 1 performs its first loop iteration, ``CycleNode.render()``
returns 'row1'
2. Thread 2 performs its first loop iteration, ``CycleNode.render()``
returns 'row2'
3. Thread 1 performs its second loop iteration, ``CycleNode.render()``
returns 'row1'
4. Thread 2 performs its second loop iteration, ``CycleNode.render()``
returns 'row2'
...and the output will be something like this:
The CycleNode is iterating, but it's iterating globally. As far as Thread 1
and Thread 2 are concerned, it's always returning the same value. This is
obviously not what we want!
.. code-block:: html
To address this problem, Django provides a ``render_context`` that's associated
with the ``context`` of the template that is currently being rendered. The
``render_context`` behaves like a Python dictionary, and should be used to
store ``Node`` state between invocations of the ``render`` method.
<ul>
<li>First choice</li>
<li>Second choice</li>
<li>Third choice</li>
</ul>
Let's refactor our ``CycleNode`` implementation to use the ``render_context``::
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::
class CycleNode(template.Node):
def __init__(self, cyclevars):
self.cyclevars = cyclevars
def show_results(poll):
choices = poll.choice_set.all()
return {'choices': choices}
def render(self, context):
if self not in context.render_context:
context.render_context[self] = itertools.cycle(self.cyclevars)
cycle_iter = context.render_context[self]
return next(cycle_iter)
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:
Note that it's perfectly safe to store global information that will not change
throughout the life of the ``Node`` as an attribute. In the case of
``CycleNode``, the ``cyclevars`` argument doesn't change after the ``Node`` is
instantiated, so we don't need to put it in the ``render_context``. But state
information that is specific to the template that is currently being rendered,
like the current iteration of the ``CycleNode``, should be stored in the
``render_context``.
.. code-block:: html+django
.. note::
Notice how we used ``self`` to scope the ``CycleNode`` specific information
within the ``render_context``. There may be multiple ``CycleNodes`` in a
given template, so we need to be careful not to clobber another node's
state information. The easiest way to do this is to always use ``self`` as
the key into ``render_context``. If you're keeping track of several state
variables, make ``render_context[self]`` a dictionary.
<ul>
{% for choice in choices %}
<li> {{ choice }} </li>
{% endfor %}
</ul>
Registering the tag
~~~~~~~~~~~~~~~~~~~
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::
Finally, register the tag with your module's ``Library`` instance, as explained
in "Writing custom template filters" above. Example::
# Here, register is a django.template.Library instance, as before
register.inclusion_tag('results.html')(show_results)
register.tag('current_time', do_current_time)
Alternatively it is possible to register the inclusion tag using a
:class:`django.template.Template` instance::
The ``tag()`` method takes two arguments:
from django.template.loader import get_template
t = get_template('results.html')
register.inclusion_tag(t)(show_results)
1. The name of the template tag -- a string. If this is left out, the
name of the compilation function will be used.
2. The compilation function -- a Python function (not the name of the
function as a string).
As always, decorator syntax works as well, so we could have written::
As with filter registration, it is also possible to use this as a decorator::
@register.inclusion_tag('results.html')
def show_results(poll):
@register.tag(name="current_time")
def do_current_time(parser, token):
...
...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.
@register.tag
def shout(parser, token):
...
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::
If you leave off the ``name`` argument, as in the second example above, Django
will use the function's name as the tag name.
# 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)
Passing template variables to the tag
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(Note that the first parameter to the function *must* be called ``context``.)
Although you can pass any number of arguments to a template tag using
``token.split_contents()``, the arguments are all unpacked as
string literals. A little more work is required in order to pass dynamic
content (a template variable) to a template tag as an argument.
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:
While the previous examples have formatted the current time into a string and
returned the string, suppose you wanted to pass in a
:class:`~django.db.models.DateTimeField` from an object and have the template
tag format that date-time:
.. code-block:: html+django
Jump directly to <a href="{{ link }}">{{ title }}</a>.
<p>This post was last updated at {% format_time blog_entry.date_updated "%Y-%m-%d %I:%M %p" %}.</p>
Then, any time you want to use that custom tag, load its library and call it
without any arguments, like so:
Initially, ``token.split_contents()`` will return three values:
.. code-block:: html+django
1. The tag name ``format_time``.
2. The string ``"blog_entry.date_updated"`` (without the surrounding
quotes).
3. The formatting string ``"%Y-%m-%d %I:%M %p"``. The return value from
``split_contents()`` will include the leading and trailing quotes for
string literals like this.
{% jump_link %}
Now your tag should begin to look like this::
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.
from django import template
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.
def do_format_time(parser, token):
try:
# split_contents() knows not to split quoted strings.
tag_name, date_to_be_formatted, format_string = token.split_contents()
except ValueError:
raise template.TemplateSyntaxError("%r tag requires exactly two arguments" % token.contents.split()[0])
if not (format_string[0] == format_string[-1] and format_string[0] in ('"', "'")):
raise template.TemplateSyntaxError("%r tag's argument should be in quotes" % tag_name)
return FormatTimeNode(date_to_be_formatted, format_string[1:-1])
``inclusion_tag`` functions may accept any number of positional or keyword
arguments. For example::
You also have to change the renderer to retrieve the actual contents of the
``date_updated`` property of the ``blog_entry`` object. This can be
accomplished by using the ``Variable()`` class in ``django.template``.
@register.inclusion_tag('my_template.html')
def my_tag(a, b, *args, **kwargs):
warning = kwargs['warning']
profile = kwargs['profile']
...
return ...
To use the ``Variable`` class, simply instantiate it with the name of the
variable to be resolved, and then call ``variable.resolve(context)``. So,
for example::
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:
class FormatTimeNode(template.Node):
def __init__(self, date_to_be_formatted, format_string):
self.date_to_be_formatted = template.Variable(date_to_be_formatted)
self.format_string = format_string
.. code-block:: html+django
def render(self, context):
try:
actual_date = self.date_to_be_formatted.resolve(context)
return actual_date.strftime(self.format_string)
except template.VariableDoesNotExist:
return ''
{% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile %}
Variable resolution will throw a ``VariableDoesNotExist`` exception if it
cannot resolve the string passed to it in the current context of the page.
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>`.
.. _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::
context-updating template tag, you might want to consider using the
:ref:`assignment tag <howto-custom-template-tags-assignment-tags>` shortcut
we introduced above.
# 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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment