mirror of
https://github.com/django/django.git
synced 2024-11-21 10:59:04 +01:00
Fixed #35535 -- Added template tag decorator simple_block_tag().
Co-authored-by: Natalia <124304+nessita@users.noreply.github.com>
This commit is contained in:
parent
9543c605c3
commit
4c452cc377
@ -153,6 +153,90 @@ class Library:
|
|||||||
else:
|
else:
|
||||||
raise ValueError("Invalid arguments provided to simple_tag")
|
raise ValueError("Invalid arguments provided to simple_tag")
|
||||||
|
|
||||||
|
def simple_block_tag(self, func=None, takes_context=None, name=None, end_name=None):
|
||||||
|
"""
|
||||||
|
Register a callable as a compiled block template tag. Example:
|
||||||
|
|
||||||
|
@register.simple_block_tag
|
||||||
|
def hello(content):
|
||||||
|
return 'world'
|
||||||
|
"""
|
||||||
|
|
||||||
|
def dec(func):
|
||||||
|
nonlocal end_name
|
||||||
|
|
||||||
|
(
|
||||||
|
params,
|
||||||
|
varargs,
|
||||||
|
varkw,
|
||||||
|
defaults,
|
||||||
|
kwonly,
|
||||||
|
kwonly_defaults,
|
||||||
|
_,
|
||||||
|
) = getfullargspec(unwrap(func))
|
||||||
|
function_name = name or func.__name__
|
||||||
|
|
||||||
|
if end_name is None:
|
||||||
|
end_name = f"end{function_name}"
|
||||||
|
|
||||||
|
@wraps(func)
|
||||||
|
def compile_func(parser, token):
|
||||||
|
tag_params = params.copy()
|
||||||
|
|
||||||
|
if takes_context:
|
||||||
|
if len(tag_params) >= 2 and tag_params[1] == "content":
|
||||||
|
del tag_params[1]
|
||||||
|
else:
|
||||||
|
raise TemplateSyntaxError(
|
||||||
|
f"{function_name!r} is decorated with takes_context=True so"
|
||||||
|
" it must have a first argument of 'context' and a second "
|
||||||
|
"argument of 'content'"
|
||||||
|
)
|
||||||
|
elif tag_params and tag_params[0] == "content":
|
||||||
|
del tag_params[0]
|
||||||
|
else:
|
||||||
|
raise TemplateSyntaxError(
|
||||||
|
f"'{function_name}' must have a first argument of 'content'"
|
||||||
|
)
|
||||||
|
|
||||||
|
bits = token.split_contents()[1:]
|
||||||
|
target_var = None
|
||||||
|
if len(bits) >= 2 and bits[-2] == "as":
|
||||||
|
target_var = bits[-1]
|
||||||
|
bits = bits[:-2]
|
||||||
|
|
||||||
|
nodelist = parser.parse((end_name,))
|
||||||
|
parser.delete_first_token()
|
||||||
|
|
||||||
|
args, kwargs = parse_bits(
|
||||||
|
parser,
|
||||||
|
bits,
|
||||||
|
tag_params,
|
||||||
|
varargs,
|
||||||
|
varkw,
|
||||||
|
defaults,
|
||||||
|
kwonly,
|
||||||
|
kwonly_defaults,
|
||||||
|
takes_context,
|
||||||
|
function_name,
|
||||||
|
)
|
||||||
|
|
||||||
|
return SimpleBlockNode(
|
||||||
|
nodelist, func, takes_context, args, kwargs, target_var
|
||||||
|
)
|
||||||
|
|
||||||
|
self.tag(function_name, compile_func)
|
||||||
|
return func
|
||||||
|
|
||||||
|
if func is None:
|
||||||
|
# @register.simple_block_tag(...)
|
||||||
|
return dec
|
||||||
|
elif callable(func):
|
||||||
|
# @register.simple_block_tag
|
||||||
|
return dec(func)
|
||||||
|
else:
|
||||||
|
raise ValueError("Invalid arguments provided to simple_block_tag")
|
||||||
|
|
||||||
def inclusion_tag(self, filename, func=None, takes_context=None, name=None):
|
def inclusion_tag(self, filename, func=None, takes_context=None, name=None):
|
||||||
"""
|
"""
|
||||||
Register a callable as an inclusion tag:
|
Register a callable as an inclusion tag:
|
||||||
@ -243,6 +327,23 @@ class SimpleNode(TagHelperNode):
|
|||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleBlockNode(SimpleNode):
|
||||||
|
def __init__(self, nodelist, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.nodelist = nodelist
|
||||||
|
|
||||||
|
def get_resolved_arguments(self, context):
|
||||||
|
resolved_args, resolved_kwargs = super().get_resolved_arguments(context)
|
||||||
|
|
||||||
|
# Restore the "content" argument.
|
||||||
|
# It will move depending on whether takes_context was passed.
|
||||||
|
resolved_args.insert(
|
||||||
|
1 if self.takes_context else 0, self.nodelist.render(context)
|
||||||
|
)
|
||||||
|
|
||||||
|
return resolved_args, resolved_kwargs
|
||||||
|
|
||||||
|
|
||||||
class InclusionNode(TagHelperNode):
|
class InclusionNode(TagHelperNode):
|
||||||
def __init__(self, func, takes_context, args, kwargs, filename):
|
def __init__(self, func, takes_context, args, kwargs, filename):
|
||||||
super().__init__(func, takes_context, args, kwargs)
|
super().__init__(func, takes_context, args, kwargs)
|
||||||
|
@ -498,6 +498,195 @@ you see fit:
|
|||||||
{% current_time "%Y-%m-%d %I:%M %p" as the_time %}
|
{% current_time "%Y-%m-%d %I:%M %p" as the_time %}
|
||||||
<p>The time is {{ the_time }}.</p>
|
<p>The time is {{ the_time }}.</p>
|
||||||
|
|
||||||
|
.. _howto-custom-template-tags-simple-block-tags:
|
||||||
|
|
||||||
|
Simple block tags
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
.. versionadded:: 5.2
|
||||||
|
|
||||||
|
.. method:: django.template.Library.simple_block_tag()
|
||||||
|
|
||||||
|
When a section of rendered template needs to be passed into a custom tag,
|
||||||
|
Django provides the ``simple_block_tag`` helper function to accomplish this.
|
||||||
|
Similar to :meth:`~django.template.Library.simple_tag()`, this function accepts
|
||||||
|
a custom tag function, but with the additional ``content`` argument, which
|
||||||
|
contains the rendered content as defined inside the tag. This allows dynamic
|
||||||
|
template sections to be easily incorporated into custom tags.
|
||||||
|
|
||||||
|
For example, a custom block tag which creates a chart could look like this::
|
||||||
|
|
||||||
|
from django import template
|
||||||
|
from myapp.charts import render_chart
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_block_tag
|
||||||
|
def chart(content):
|
||||||
|
return render_chart(source=content)
|
||||||
|
|
||||||
|
The ``content`` argument contains everything in between the ``{% chart %}``
|
||||||
|
and ``{% endchart %}`` tags:
|
||||||
|
|
||||||
|
.. code-block:: html+django
|
||||||
|
|
||||||
|
{% chart %}
|
||||||
|
digraph G {
|
||||||
|
label = "Chart for {{ request.user }}"
|
||||||
|
A -> {B C}
|
||||||
|
}
|
||||||
|
{% endchart %}
|
||||||
|
|
||||||
|
If there are other template tags or variables inside the ``content`` block,
|
||||||
|
they will be rendered before being passed to the tag function. In the example
|
||||||
|
above, ``request.user`` will be resolved by the time ``render_chart`` is
|
||||||
|
called.
|
||||||
|
|
||||||
|
Block tags are closed with ``end{name}`` (for example, ``endchart``). This can
|
||||||
|
be customized with the ``end_name`` parameter::
|
||||||
|
|
||||||
|
@register.simple_block_tag(end_name="endofchart")
|
||||||
|
def chart(content):
|
||||||
|
return render_chart(source=content)
|
||||||
|
|
||||||
|
Which would require a template definition like this:
|
||||||
|
|
||||||
|
.. code-block:: html+django
|
||||||
|
|
||||||
|
{% chart %}
|
||||||
|
digraph G {
|
||||||
|
label = "Chart for {{ request.user }}"
|
||||||
|
A -> {B C}
|
||||||
|
}
|
||||||
|
{% endofchart %}
|
||||||
|
|
||||||
|
A few things to note about ``simple_block_tag``:
|
||||||
|
|
||||||
|
* The first argument must be called ``content``, and it will contain the
|
||||||
|
contents of the template tag as a rendered string.
|
||||||
|
* Variables passed to the tag are not included in the rendering context of the
|
||||||
|
content, as would be when using the ``{% with %}`` tag.
|
||||||
|
|
||||||
|
Just like :ref:`simple_tag<howto-custom-template-tags-simple-tags>`,
|
||||||
|
``simple_block_tag``:
|
||||||
|
|
||||||
|
* Validates the quantity and quality of the arguments.
|
||||||
|
* Strips quotes from arguments if necessary.
|
||||||
|
* Escapes the output accordingly.
|
||||||
|
* Supports passing ``takes_context=True`` at registration time to access
|
||||||
|
context. Note that in this case, the first argument to the custom function
|
||||||
|
*must* be called ``context``, and ``content`` must follow.
|
||||||
|
* Supports renaming the tag by passing the ``name`` argument when registering.
|
||||||
|
* Supports accepting any number of positional or keyword arguments.
|
||||||
|
* Supports storing the result in a template variable using the ``as`` variant.
|
||||||
|
|
||||||
|
.. admonition:: Content Escaping
|
||||||
|
|
||||||
|
``simple_block_tag`` behaves similarly to ``simple_tag`` regarding
|
||||||
|
auto-escaping. For details on escaping and safety, refer to ``simple_tag``.
|
||||||
|
Because the ``content`` argument has already been rendered by Django, it is
|
||||||
|
already escaped.
|
||||||
|
|
||||||
|
A complete example
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Consider a custom template tag that generates a message box that supports
|
||||||
|
multiple message levels and content beyond a simple phrase. This could be
|
||||||
|
implemented using a ``simple_block_tag`` as follows:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:caption: ``testapp/templatetags/testapptags.py``
|
||||||
|
|
||||||
|
from django import template
|
||||||
|
from django.utils.html import format_html
|
||||||
|
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_block_tag(takes_context=True)
|
||||||
|
def msgbox(context, content, level):
|
||||||
|
format_kwargs = {
|
||||||
|
"level": level.lower(),
|
||||||
|
"level_title": level.capitalize(),
|
||||||
|
"content": content,
|
||||||
|
"open": " open" if level.lower() == "error" else "",
|
||||||
|
"site": context.get("site", "My Site"),
|
||||||
|
}
|
||||||
|
result = """
|
||||||
|
<div class="msgbox {level}">
|
||||||
|
<details{open}>
|
||||||
|
<summary>
|
||||||
|
<strong>{level_title}</strong>: Please read for <i>{site}</i>
|
||||||
|
</summary>
|
||||||
|
<p>
|
||||||
|
{content}
|
||||||
|
</p>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
return format_html(result, **format_kwargs)
|
||||||
|
|
||||||
|
When combined with a minimal view and corresponding template, as shown here:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:caption: ``testapp/views.py``
|
||||||
|
|
||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
|
||||||
|
def simpleblocktag_view(request):
|
||||||
|
return render(request, "test.html", context={"site": "Important Site"})
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: html+django
|
||||||
|
:caption: ``testapp/templates/test.html``
|
||||||
|
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% load testapptags %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
{% msgbox level="error" %}
|
||||||
|
Please fix all errors. Further documentation can be found at
|
||||||
|
<a href="http://example.com">Docs</a>.
|
||||||
|
{% endmsgbox %}
|
||||||
|
|
||||||
|
{% msgbox level="info" %}
|
||||||
|
More information at: <a href="http://othersite.com">Other Site</a>/
|
||||||
|
{% endmsgbox %}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
The following HTML is produced as the rendered output:
|
||||||
|
|
||||||
|
.. code-block:: html
|
||||||
|
|
||||||
|
<div class="msgbox error">
|
||||||
|
<details open>
|
||||||
|
<summary>
|
||||||
|
<strong>Error</strong>: Please read for <i>Important Site</i>
|
||||||
|
</summary>
|
||||||
|
<p>
|
||||||
|
Please fix all errors. Further documentation can be found at
|
||||||
|
<a href="http://example.com">Docs</a>.
|
||||||
|
</p>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="msgbox info">
|
||||||
|
<details>
|
||||||
|
<summary>
|
||||||
|
<strong>Info</strong>: Please read for <i>Important Site</i>
|
||||||
|
</summary>
|
||||||
|
<p>
|
||||||
|
More information at: <a href="http://othersite.com">Other Site</a>
|
||||||
|
</p>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
|
||||||
.. _howto-custom-template-tags-inclusion-tags:
|
.. _howto-custom-template-tags-inclusion-tags:
|
||||||
|
|
||||||
Inclusion tags
|
Inclusion tags
|
||||||
|
@ -323,7 +323,9 @@ Signals
|
|||||||
Templates
|
Templates
|
||||||
~~~~~~~~~
|
~~~~~~~~~
|
||||||
|
|
||||||
* ...
|
* The new :meth:`~django.template.Library.simple_block_tag` decorator enables
|
||||||
|
the creation of simple block tags, which can accept and use a section of the
|
||||||
|
template.
|
||||||
|
|
||||||
Tests
|
Tests
|
||||||
~~~~~
|
~~~~~
|
||||||
|
@ -20,6 +20,16 @@ def make_data_div(value):
|
|||||||
return '<div data-name="%s"></div>' % value
|
return '<div data-name="%s"></div>' % value
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_block_tag
|
||||||
|
def div(content, id="test"):
|
||||||
|
return format_html("<div id='{}'>{}</div>", id, content)
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_block_tag(end_name="divend")
|
||||||
|
def div_custom_end(content):
|
||||||
|
return format_html("<div>{}</div>", content)
|
||||||
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def noop(value, param=None):
|
def noop(value, param=None):
|
||||||
"""A noop filter that always return its first argument and does nothing with
|
"""A noop filter that always return its first argument and does nothing with
|
||||||
@ -51,6 +61,12 @@ def one_param(arg):
|
|||||||
one_param.anything = "Expected one_param __dict__"
|
one_param.anything = "Expected one_param __dict__"
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_block_tag
|
||||||
|
def one_param_block(content, arg):
|
||||||
|
"""Expected one_param_block __doc__"""
|
||||||
|
return f"one_param_block - Expected result: {arg} with content {content}"
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=False)
|
@register.simple_tag(takes_context=False)
|
||||||
def explicit_no_context(arg):
|
def explicit_no_context(arg):
|
||||||
"""Expected explicit_no_context __doc__"""
|
"""Expected explicit_no_context __doc__"""
|
||||||
@ -60,6 +76,12 @@ def explicit_no_context(arg):
|
|||||||
explicit_no_context.anything = "Expected explicit_no_context __dict__"
|
explicit_no_context.anything = "Expected explicit_no_context __dict__"
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_block_tag(takes_context=False)
|
||||||
|
def explicit_no_context_block(content, arg):
|
||||||
|
"""Expected explicit_no_context_block __doc__"""
|
||||||
|
return f"explicit_no_context_block - Expected result: {arg} with content {content}"
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=True)
|
@register.simple_tag(takes_context=True)
|
||||||
def no_params_with_context(context):
|
def no_params_with_context(context):
|
||||||
"""Expected no_params_with_context __doc__"""
|
"""Expected no_params_with_context __doc__"""
|
||||||
@ -72,6 +94,15 @@ def no_params_with_context(context):
|
|||||||
no_params_with_context.anything = "Expected no_params_with_context __dict__"
|
no_params_with_context.anything = "Expected no_params_with_context __dict__"
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_block_tag(takes_context=True)
|
||||||
|
def no_params_with_context_block(context, content):
|
||||||
|
"""Expected no_params_with_context_block __doc__"""
|
||||||
|
return (
|
||||||
|
"no_params_with_context_block - Expected result (context value: %s) "
|
||||||
|
"(content value: %s)" % (context["value"], content)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=True)
|
@register.simple_tag(takes_context=True)
|
||||||
def params_and_context(context, arg):
|
def params_and_context(context, arg):
|
||||||
"""Expected params_and_context __doc__"""
|
"""Expected params_and_context __doc__"""
|
||||||
@ -84,6 +115,20 @@ def params_and_context(context, arg):
|
|||||||
params_and_context.anything = "Expected params_and_context __dict__"
|
params_and_context.anything = "Expected params_and_context __dict__"
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_block_tag(takes_context=True)
|
||||||
|
def params_and_context_block(context, content, arg):
|
||||||
|
"""Expected params_and_context_block __doc__"""
|
||||||
|
return (
|
||||||
|
"params_and_context_block - Expected result (context value: %s) "
|
||||||
|
"(content value: %s): %s"
|
||||||
|
% (
|
||||||
|
context["value"],
|
||||||
|
content,
|
||||||
|
arg,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag
|
@register.simple_tag
|
||||||
def simple_two_params(one, two):
|
def simple_two_params(one, two):
|
||||||
"""Expected simple_two_params __doc__"""
|
"""Expected simple_two_params __doc__"""
|
||||||
@ -93,16 +138,48 @@ def simple_two_params(one, two):
|
|||||||
simple_two_params.anything = "Expected simple_two_params __dict__"
|
simple_two_params.anything = "Expected simple_two_params __dict__"
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_block_tag
|
||||||
|
def simple_two_params_block(content, one, two):
|
||||||
|
"""Expected simple_two_params_block __doc__"""
|
||||||
|
return "simple_two_params_block - Expected result (content value: %s): %s, %s" % (
|
||||||
|
content,
|
||||||
|
one,
|
||||||
|
two,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag
|
@register.simple_tag
|
||||||
def simple_keyword_only_param(*, kwarg):
|
def simple_keyword_only_param(*, kwarg):
|
||||||
return "simple_keyword_only_param - Expected result: %s" % kwarg
|
return "simple_keyword_only_param - Expected result: %s" % kwarg
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_block_tag
|
||||||
|
def simple_keyword_only_param_block(content, *, kwarg):
|
||||||
|
return (
|
||||||
|
"simple_keyword_only_param_block - Expected result (content value: %s): %s"
|
||||||
|
% (
|
||||||
|
content,
|
||||||
|
kwarg,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag
|
@register.simple_tag
|
||||||
def simple_keyword_only_default(*, kwarg=42):
|
def simple_keyword_only_default(*, kwarg=42):
|
||||||
return "simple_keyword_only_default - Expected result: %s" % kwarg
|
return "simple_keyword_only_default - Expected result: %s" % kwarg
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_block_tag
|
||||||
|
def simple_keyword_only_default_block(content, *, kwarg=42):
|
||||||
|
return (
|
||||||
|
"simple_keyword_only_default_block - Expected result (content value: %s): %s"
|
||||||
|
% (
|
||||||
|
content,
|
||||||
|
kwarg,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag
|
@register.simple_tag
|
||||||
def simple_one_default(one, two="hi"):
|
def simple_one_default(one, two="hi"):
|
||||||
"""Expected simple_one_default __doc__"""
|
"""Expected simple_one_default __doc__"""
|
||||||
@ -112,6 +189,16 @@ def simple_one_default(one, two="hi"):
|
|||||||
simple_one_default.anything = "Expected simple_one_default __dict__"
|
simple_one_default.anything = "Expected simple_one_default __dict__"
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_block_tag
|
||||||
|
def simple_one_default_block(content, one, two="hi"):
|
||||||
|
"""Expected simple_one_default_block __doc__"""
|
||||||
|
return "simple_one_default_block - Expected result (content value: %s): %s, %s" % (
|
||||||
|
content,
|
||||||
|
one,
|
||||||
|
two,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag
|
@register.simple_tag
|
||||||
def simple_unlimited_args(one, two="hi", *args):
|
def simple_unlimited_args(one, two="hi", *args):
|
||||||
"""Expected simple_unlimited_args __doc__"""
|
"""Expected simple_unlimited_args __doc__"""
|
||||||
@ -123,6 +210,15 @@ def simple_unlimited_args(one, two="hi", *args):
|
|||||||
simple_unlimited_args.anything = "Expected simple_unlimited_args __dict__"
|
simple_unlimited_args.anything = "Expected simple_unlimited_args __dict__"
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_block_tag
|
||||||
|
def simple_unlimited_args_block(content, one, two="hi", *args):
|
||||||
|
"""Expected simple_unlimited_args_block __doc__"""
|
||||||
|
return "simple_unlimited_args_block - Expected result (content value: %s): %s" % (
|
||||||
|
content,
|
||||||
|
", ".join(str(arg) for arg in [one, two, *args]),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag
|
@register.simple_tag
|
||||||
def simple_only_unlimited_args(*args):
|
def simple_only_unlimited_args(*args):
|
||||||
"""Expected simple_only_unlimited_args __doc__"""
|
"""Expected simple_only_unlimited_args __doc__"""
|
||||||
@ -134,6 +230,18 @@ def simple_only_unlimited_args(*args):
|
|||||||
simple_only_unlimited_args.anything = "Expected simple_only_unlimited_args __dict__"
|
simple_only_unlimited_args.anything = "Expected simple_only_unlimited_args __dict__"
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_block_tag
|
||||||
|
def simple_only_unlimited_args_block(content, *args):
|
||||||
|
"""Expected simple_only_unlimited_args_block __doc__"""
|
||||||
|
return (
|
||||||
|
"simple_only_unlimited_args_block - Expected result (content value: %s): %s"
|
||||||
|
% (
|
||||||
|
content,
|
||||||
|
", ".join(str(arg) for arg in args),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag
|
@register.simple_tag
|
||||||
def simple_unlimited_args_kwargs(one, two="hi", *args, **kwargs):
|
def simple_unlimited_args_kwargs(one, two="hi", *args, **kwargs):
|
||||||
"""Expected simple_unlimited_args_kwargs __doc__"""
|
"""Expected simple_unlimited_args_kwargs __doc__"""
|
||||||
@ -146,6 +254,38 @@ def simple_unlimited_args_kwargs(one, two="hi", *args, **kwargs):
|
|||||||
simple_unlimited_args_kwargs.anything = "Expected simple_unlimited_args_kwargs __dict__"
|
simple_unlimited_args_kwargs.anything = "Expected simple_unlimited_args_kwargs __dict__"
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_block_tag
|
||||||
|
def simple_unlimited_args_kwargs_block(content, one, two="hi", *args, **kwargs):
|
||||||
|
"""Expected simple_unlimited_args_kwargs_block __doc__"""
|
||||||
|
return (
|
||||||
|
"simple_unlimited_args_kwargs_block - Expected result (content value: %s): "
|
||||||
|
"%s / %s"
|
||||||
|
% (
|
||||||
|
content,
|
||||||
|
", ".join(str(arg) for arg in [one, two, *args]),
|
||||||
|
", ".join("%s=%s" % (k, v) for (k, v) in kwargs.items()),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_block_tag(takes_context=True)
|
||||||
|
def simple_block_tag_without_context_parameter(arg):
|
||||||
|
"""Expected simple_block_tag_without_context_parameter __doc__"""
|
||||||
|
return "Expected result"
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_block_tag
|
||||||
|
def simple_tag_without_content_parameter(arg):
|
||||||
|
"""Expected simple_tag_without_content_parameter __doc__"""
|
||||||
|
return "Expected result"
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_block_tag(takes_context=True)
|
||||||
|
def simple_tag_with_context_without_content_parameter(context, arg):
|
||||||
|
"""Expected simple_tag_with_context_without_content_parameter __doc__"""
|
||||||
|
return "Expected result"
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=True)
|
@register.simple_tag(takes_context=True)
|
||||||
def simple_tag_without_context_parameter(arg):
|
def simple_tag_without_context_parameter(arg):
|
||||||
"""Expected simple_tag_without_context_parameter __doc__"""
|
"""Expected simple_tag_without_context_parameter __doc__"""
|
||||||
@ -157,6 +297,12 @@ simple_tag_without_context_parameter.anything = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_block_tag(takes_context=True)
|
||||||
|
def simple_tag_takes_context_without_params_block():
|
||||||
|
"""Expected simple_tag_takes_context_without_params_block __doc__"""
|
||||||
|
return "Expected result"
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=True)
|
@register.simple_tag(takes_context=True)
|
||||||
def simple_tag_takes_context_without_params():
|
def simple_tag_takes_context_without_params():
|
||||||
"""Expected simple_tag_takes_context_without_params __doc__"""
|
"""Expected simple_tag_takes_context_without_params __doc__"""
|
||||||
@ -168,24 +314,52 @@ simple_tag_takes_context_without_params.anything = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_block_tag
|
||||||
|
def simple_block_tag_without_content():
|
||||||
|
return "Expected result"
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_block_tag(takes_context=True)
|
||||||
|
def simple_block_tag_with_context_without_content():
|
||||||
|
return "Expected result"
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=True)
|
@register.simple_tag(takes_context=True)
|
||||||
def escape_naive(context):
|
def escape_naive(context):
|
||||||
"""A tag that doesn't even think about escaping issues"""
|
"""A tag that doesn't even think about escaping issues"""
|
||||||
return "Hello {}!".format(context["name"])
|
return "Hello {}!".format(context["name"])
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_block_tag(takes_context=True)
|
||||||
|
def escape_naive_block(context, content):
|
||||||
|
"""A block tag that doesn't even think about escaping issues"""
|
||||||
|
return "Hello {}: {}!".format(context["name"], content)
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=True)
|
@register.simple_tag(takes_context=True)
|
||||||
def escape_explicit(context):
|
def escape_explicit(context):
|
||||||
"""A tag that uses escape explicitly"""
|
"""A tag that uses escape explicitly"""
|
||||||
return escape("Hello {}!".format(context["name"]))
|
return escape("Hello {}!".format(context["name"]))
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_block_tag(takes_context=True)
|
||||||
|
def escape_explicit_block(context, content):
|
||||||
|
"""A block tag that uses escape explicitly"""
|
||||||
|
return escape("Hello {}: {}!".format(context["name"], content))
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=True)
|
@register.simple_tag(takes_context=True)
|
||||||
def escape_format_html(context):
|
def escape_format_html(context):
|
||||||
"""A tag that uses format_html"""
|
"""A tag that uses format_html"""
|
||||||
return format_html("Hello {0}!", context["name"])
|
return format_html("Hello {0}!", context["name"])
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_block_tag(takes_context=True)
|
||||||
|
def escape_format_html_block(context, content):
|
||||||
|
"""A block tag that uses format_html"""
|
||||||
|
return format_html("Hello {0}: {1}!", context["name"], content)
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=True)
|
@register.simple_tag(takes_context=True)
|
||||||
def current_app(context):
|
def current_app(context):
|
||||||
return str(context.current_app)
|
return str(context.current_app)
|
||||||
|
@ -243,6 +243,343 @@ class SimpleTagTests(TagTestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleBlockTagTests(TagTestCase):
|
||||||
|
def test_simple_block_tags(self):
|
||||||
|
c = Context({"value": 42})
|
||||||
|
|
||||||
|
templates = [
|
||||||
|
(
|
||||||
|
"{% load custom %}{% div %}content{% enddiv %}",
|
||||||
|
"<div id='test'>content</div>",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"{% load custom %}{% one_param_block 37 %}inner"
|
||||||
|
"{% endone_param_block %}",
|
||||||
|
"one_param_block - Expected result: 37 with content inner",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"{% load custom %}{% explicit_no_context_block 37 %}inner"
|
||||||
|
"{% endexplicit_no_context_block %}",
|
||||||
|
"explicit_no_context_block - Expected result: 37 with content inner",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"{% load custom %}{% no_params_with_context_block %}inner"
|
||||||
|
"{% endno_params_with_context_block %}",
|
||||||
|
"no_params_with_context_block - Expected result (context value: 42) "
|
||||||
|
"(content value: inner)",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"{% load custom %}{% params_and_context_block 37 %}inner"
|
||||||
|
"{% endparams_and_context_block %}",
|
||||||
|
"params_and_context_block - Expected result (context value: 42) "
|
||||||
|
"(content value: inner): 37",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"{% load custom %}{% simple_two_params_block 37 42 %}inner"
|
||||||
|
"{% endsimple_two_params_block %}",
|
||||||
|
"simple_two_params_block - Expected result (content value: inner): "
|
||||||
|
"37, 42",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"{% load custom %}{% simple_keyword_only_param_block kwarg=37 %}thirty "
|
||||||
|
"seven{% endsimple_keyword_only_param_block %}",
|
||||||
|
"simple_keyword_only_param_block - Expected result (content value: "
|
||||||
|
"thirty seven): 37",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"{% load custom %}{% simple_keyword_only_default_block %}forty two"
|
||||||
|
"{% endsimple_keyword_only_default_block %}",
|
||||||
|
"simple_keyword_only_default_block - Expected result (content value: "
|
||||||
|
"forty two): 42",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"{% load custom %}{% simple_keyword_only_default_block kwarg=37 %}"
|
||||||
|
"thirty seven{% endsimple_keyword_only_default_block %}",
|
||||||
|
"simple_keyword_only_default_block - Expected result (content value: "
|
||||||
|
"thirty seven): 37",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"{% load custom %}{% simple_one_default_block 37 %}inner"
|
||||||
|
"{% endsimple_one_default_block %}",
|
||||||
|
"simple_one_default_block - Expected result (content value: inner): "
|
||||||
|
"37, hi",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'{% load custom %}{% simple_one_default_block 37 two="hello" %}inner'
|
||||||
|
"{% endsimple_one_default_block %}",
|
||||||
|
"simple_one_default_block - Expected result (content value: inner): "
|
||||||
|
"37, hello",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'{% load custom %}{% simple_one_default_block one=99 two="hello" %}'
|
||||||
|
"inner{% endsimple_one_default_block %}",
|
||||||
|
"simple_one_default_block - Expected result (content value: inner): "
|
||||||
|
"99, hello",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"{% load custom %}{% simple_one_default_block 37 42 %}inner"
|
||||||
|
"{% endsimple_one_default_block %}",
|
||||||
|
"simple_one_default_block - Expected result (content value: inner): "
|
||||||
|
"37, 42",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"{% load custom %}{% simple_unlimited_args_block 37 %}thirty seven"
|
||||||
|
"{% endsimple_unlimited_args_block %}",
|
||||||
|
"simple_unlimited_args_block - Expected result (content value: thirty "
|
||||||
|
"seven): 37, hi",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"{% load custom %}{% simple_unlimited_args_block 37 42 56 89 %}numbers"
|
||||||
|
"{% endsimple_unlimited_args_block %}",
|
||||||
|
"simple_unlimited_args_block - Expected result "
|
||||||
|
"(content value: numbers): 37, 42, 56, 89",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"{% load custom %}{% simple_only_unlimited_args_block %}inner"
|
||||||
|
"{% endsimple_only_unlimited_args_block %}",
|
||||||
|
"simple_only_unlimited_args_block - Expected result (content value: "
|
||||||
|
"inner): ",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"{% load custom %}{% simple_only_unlimited_args_block 37 42 56 89 %}"
|
||||||
|
"numbers{% endsimple_only_unlimited_args_block %}",
|
||||||
|
"simple_only_unlimited_args_block - Expected result "
|
||||||
|
"(content value: numbers): 37, 42, 56, 89",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"{% load custom %}"
|
||||||
|
'{% simple_unlimited_args_kwargs_block 37 40|add:2 56 eggs="scrambled" '
|
||||||
|
"four=1|add:3 %}inner content"
|
||||||
|
"{% endsimple_unlimited_args_kwargs_block %}",
|
||||||
|
"simple_unlimited_args_kwargs_block - Expected result (content value: "
|
||||||
|
"inner content): 37, 42, 56 / eggs=scrambled, four=4",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
for entry in templates:
|
||||||
|
with self.subTest(entry[0]):
|
||||||
|
t = self.engine.from_string(entry[0])
|
||||||
|
self.assertEqual(t.render(c), entry[1])
|
||||||
|
|
||||||
|
def test_simple_block_tag_errors(self):
|
||||||
|
errors = [
|
||||||
|
(
|
||||||
|
"'simple_one_default_block' received unexpected keyword argument "
|
||||||
|
"'three'",
|
||||||
|
"{% load custom %}"
|
||||||
|
'{% simple_one_default_block 99 two="hello" three="foo" %}'
|
||||||
|
"{% endsimple_one_default_block %}",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"'simple_two_params_block' received too many positional arguments",
|
||||||
|
"{% load custom %}{% simple_two_params_block 37 42 56 %}"
|
||||||
|
"{% endsimple_two_params_block %}",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"'simple_one_default_block' received too many positional arguments",
|
||||||
|
"{% load custom %}{% simple_one_default_block 37 42 56 %}"
|
||||||
|
"{% endsimple_one_default_block %}",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"'simple_keyword_only_param_block' did not receive value(s) for the "
|
||||||
|
"argument(s): 'kwarg'",
|
||||||
|
"{% load custom %}{% simple_keyword_only_param_block %}"
|
||||||
|
"{% endsimple_keyword_only_param_block %}",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"'simple_keyword_only_param_block' received multiple values for "
|
||||||
|
"keyword argument 'kwarg'",
|
||||||
|
"{% load custom %}"
|
||||||
|
"{% simple_keyword_only_param_block kwarg=42 kwarg=37 %}"
|
||||||
|
"{% endsimple_keyword_only_param_block %}",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"'simple_keyword_only_default_block' received multiple values for "
|
||||||
|
"keyword argument 'kwarg'",
|
||||||
|
"{% load custom %}{% simple_keyword_only_default_block kwarg=42 "
|
||||||
|
"kwarg=37 %}{% endsimple_keyword_only_default_block %}",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"'simple_unlimited_args_kwargs_block' received some positional "
|
||||||
|
"argument(s) after some keyword argument(s)",
|
||||||
|
"{% load custom %}"
|
||||||
|
'{% simple_unlimited_args_kwargs_block 37 40|add:2 eggs="scrambled" 56 '
|
||||||
|
"four=1|add:3 %}{% endsimple_unlimited_args_kwargs_block %}",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"'simple_unlimited_args_kwargs_block' received multiple values for "
|
||||||
|
"keyword argument 'eggs'",
|
||||||
|
"{% load custom %}"
|
||||||
|
"{% simple_unlimited_args_kwargs_block 37 "
|
||||||
|
'eggs="scrambled" eggs="scrambled" %}'
|
||||||
|
"{% endsimple_unlimited_args_kwargs_block %}",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Unclosed tag on line 1: 'div'. Looking for one of: enddiv.",
|
||||||
|
"{% load custom %}{% div %}Some content",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Unclosed tag on line 1: 'simple_one_default_block'. Looking for one "
|
||||||
|
"of: endsimple_one_default_block.",
|
||||||
|
"{% load custom %}{% simple_one_default_block %}Some content",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"'simple_tag_without_content_parameter' must have a first argument "
|
||||||
|
"of 'content'",
|
||||||
|
"{% load custom %}{% simple_tag_without_content_parameter %}",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"'simple_tag_with_context_without_content_parameter' is decorated with "
|
||||||
|
"takes_context=True so it must have a first argument of 'context' and "
|
||||||
|
"a second argument of 'content'",
|
||||||
|
"{% load custom %}"
|
||||||
|
"{% simple_tag_with_context_without_content_parameter %}",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
for entry in errors:
|
||||||
|
with self.subTest(entry[1]):
|
||||||
|
with self.assertRaisesMessage(TemplateSyntaxError, entry[0]):
|
||||||
|
self.engine.from_string(entry[1])
|
||||||
|
|
||||||
|
def test_simple_block_tag_escaping_autoescape_off(self):
|
||||||
|
c = Context({"name": "Jack & Jill"}, autoescape=False)
|
||||||
|
t = self.engine.from_string(
|
||||||
|
"{% load custom %}{% escape_naive_block %}{{ name }} again"
|
||||||
|
"{% endescape_naive_block %}"
|
||||||
|
)
|
||||||
|
self.assertEqual(t.render(c), "Hello Jack & Jill: Jack & Jill again!")
|
||||||
|
|
||||||
|
def test_simple_block_tag_naive_escaping(self):
|
||||||
|
c = Context({"name": "Jack & Jill"})
|
||||||
|
t = self.engine.from_string(
|
||||||
|
"{% load custom %}{% escape_naive_block %}{{ name }} again"
|
||||||
|
"{% endescape_naive_block %}"
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
t.render(c), "Hello Jack & Jill: Jack &amp; Jill again!"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_simple_block_tag_explicit_escaping(self):
|
||||||
|
# Check we don't double escape
|
||||||
|
c = Context({"name": "Jack & Jill"})
|
||||||
|
t = self.engine.from_string(
|
||||||
|
"{% load custom %}{% escape_explicit_block %}again"
|
||||||
|
"{% endescape_explicit_block %}"
|
||||||
|
)
|
||||||
|
self.assertEqual(t.render(c), "Hello Jack & Jill: again!")
|
||||||
|
|
||||||
|
def test_simple_block_tag_format_html_escaping(self):
|
||||||
|
# Check we don't double escape
|
||||||
|
c = Context({"name": "Jack & Jill"})
|
||||||
|
t = self.engine.from_string(
|
||||||
|
"{% load custom %}{% escape_format_html_block %}again"
|
||||||
|
"{% endescape_format_html_block %}"
|
||||||
|
)
|
||||||
|
self.assertEqual(t.render(c), "Hello Jack & Jill: again!")
|
||||||
|
|
||||||
|
def test_simple_block_tag_missing_context(self):
|
||||||
|
# The 'context' parameter must be present when takes_context is True
|
||||||
|
msg = (
|
||||||
|
"'simple_block_tag_without_context_parameter' is decorated with "
|
||||||
|
"takes_context=True so it must have a first argument of 'context'"
|
||||||
|
)
|
||||||
|
with self.assertRaisesMessage(TemplateSyntaxError, msg):
|
||||||
|
self.engine.from_string(
|
||||||
|
"{% load custom %}{% simple_block_tag_without_context_parameter 123 %}"
|
||||||
|
"{% endsimple_block_tag_without_context_parameter %}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_simple_block_tag_missing_context_no_params(self):
|
||||||
|
msg = (
|
||||||
|
"'simple_tag_takes_context_without_params_block' is decorated with "
|
||||||
|
"takes_context=True so it must have a first argument of 'context'"
|
||||||
|
)
|
||||||
|
with self.assertRaisesMessage(TemplateSyntaxError, msg):
|
||||||
|
self.engine.from_string(
|
||||||
|
"{% load custom %}{% simple_tag_takes_context_without_params_block %}"
|
||||||
|
"{% endsimple_tag_takes_context_without_params_block %}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_simple_block_tag_missing_content(self):
|
||||||
|
# The 'content' parameter must be present when takes_context is True
|
||||||
|
msg = (
|
||||||
|
"'simple_block_tag_without_content' must have a first argument of 'content'"
|
||||||
|
)
|
||||||
|
with self.assertRaisesMessage(TemplateSyntaxError, msg):
|
||||||
|
self.engine.from_string(
|
||||||
|
"{% load custom %}{% simple_block_tag_without_content %}"
|
||||||
|
"{% endsimple_block_tag_without_content %}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_simple_block_tag_with_context_missing_content(self):
|
||||||
|
# The 'content' parameter must be present when takes_context is True
|
||||||
|
msg = "'simple_block_tag_with_context_without_content' is decorated with "
|
||||||
|
"takes_context=True so it must have a first argument of 'context' and a "
|
||||||
|
"second argument of 'content'"
|
||||||
|
with self.assertRaisesMessage(TemplateSyntaxError, msg):
|
||||||
|
self.engine.from_string(
|
||||||
|
"{% load custom %}{% simple_block_tag_with_context_without_content %}"
|
||||||
|
"{% endsimple_block_tag_with_context_without_content %}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_simple_block_gets_context(self):
|
||||||
|
c = Context({"name": "Jack & Jill"})
|
||||||
|
t = self.engine.from_string("{% load custom %}{% div %}{{ name }}{% enddiv %}")
|
||||||
|
self.assertEqual(t.render(c), "<div id='test'>Jack & Jill</div>")
|
||||||
|
|
||||||
|
def test_simple_block_capture_as(self):
|
||||||
|
c = Context({"name": "Jack & Jill"})
|
||||||
|
t = self.engine.from_string(
|
||||||
|
"{% load custom %}{% div as div_content %}{{ name }}{% enddiv %}"
|
||||||
|
"My div is: {{ div_content }}"
|
||||||
|
)
|
||||||
|
self.assertEqual(t.render(c), "My div is: <div id='test'>Jack & Jill</div>")
|
||||||
|
|
||||||
|
def test_simple_block_nested(self):
|
||||||
|
c = Context({"name": "Jack & Jill"})
|
||||||
|
t = self.engine.from_string(
|
||||||
|
"{% load custom %}Start{% div id='outer' %}Before{% div id='inner' %}"
|
||||||
|
"{{ name }}{% enddiv %}After{% enddiv %}End"
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
t.render(c),
|
||||||
|
"Start<div id='outer'>Before<div id='inner'>Jack & Jill</div>After"
|
||||||
|
"</div>End",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_different_simple_block_nested(self):
|
||||||
|
c = Context({"name": "Jack & Jill"})
|
||||||
|
t = self.engine.from_string(
|
||||||
|
"{% load custom %}Start{% div id='outer' %}Before"
|
||||||
|
"{% simple_keyword_only_default_block %}Inner"
|
||||||
|
"{% endsimple_keyword_only_default_block %}"
|
||||||
|
"After{% enddiv %}End"
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
t.render(c),
|
||||||
|
"Start<div id='outer'>Before"
|
||||||
|
"simple_keyword_only_default_block - Expected result (content value: "
|
||||||
|
"Inner): 42After</div>End",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_custom_end_tag(self):
|
||||||
|
c = Context({"name": "Jack & Jill"})
|
||||||
|
t = self.engine.from_string(
|
||||||
|
"{% load custom %}{% div_custom_end %}{{ name }}{% divend %}"
|
||||||
|
)
|
||||||
|
self.assertEqual(t.render(c), "<div>Jack & Jill</div>")
|
||||||
|
|
||||||
|
with self.assertRaisesMessage(
|
||||||
|
TemplateSyntaxError,
|
||||||
|
"'enddiv_custom_end', expected 'divend'. Did you forget to register or "
|
||||||
|
"load this tag?",
|
||||||
|
):
|
||||||
|
self.engine.from_string(
|
||||||
|
"{% load custom %}{% div_custom_end %}{{ name }}{% enddiv_custom_end %}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class InclusionTagTests(TagTestCase):
|
class InclusionTagTests(TagTestCase):
|
||||||
def test_inclusion_tags(self):
|
def test_inclusion_tags(self):
|
||||||
c = Context({"value": 42})
|
c = Context({"value": 42})
|
||||||
|
@ -120,6 +120,47 @@ class SimpleTagRegistrationTests(SimpleTestCase):
|
|||||||
self.assertTrue(hasattr(func_wrapped, "cache_info"))
|
self.assertTrue(hasattr(func_wrapped, "cache_info"))
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleBlockTagRegistrationTests(SimpleTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.library = Library()
|
||||||
|
|
||||||
|
def test_simple_block_tag(self):
|
||||||
|
@self.library.simple_block_tag
|
||||||
|
def func(content):
|
||||||
|
return content
|
||||||
|
|
||||||
|
self.assertIn("func", self.library.tags)
|
||||||
|
|
||||||
|
def test_simple_block_tag_parens(self):
|
||||||
|
@self.library.simple_tag()
|
||||||
|
def func(content):
|
||||||
|
return content
|
||||||
|
|
||||||
|
self.assertIn("func", self.library.tags)
|
||||||
|
|
||||||
|
def test_simple_block_tag_name_kwarg(self):
|
||||||
|
@self.library.simple_block_tag(name="name")
|
||||||
|
def func(content):
|
||||||
|
return content
|
||||||
|
|
||||||
|
self.assertIn("name", self.library.tags)
|
||||||
|
|
||||||
|
def test_simple_block_tag_invalid(self):
|
||||||
|
msg = "Invalid arguments provided to simple_block_tag"
|
||||||
|
with self.assertRaisesMessage(ValueError, msg):
|
||||||
|
self.library.simple_block_tag("invalid")
|
||||||
|
|
||||||
|
def test_simple_tag_wrapped(self):
|
||||||
|
@self.library.simple_block_tag
|
||||||
|
@functools.lru_cache(maxsize=32)
|
||||||
|
def func(content):
|
||||||
|
return content
|
||||||
|
|
||||||
|
func_wrapped = self.library.tags["func"].__wrapped__
|
||||||
|
self.assertIs(func_wrapped, func)
|
||||||
|
self.assertTrue(hasattr(func_wrapped, "cache_info"))
|
||||||
|
|
||||||
|
|
||||||
class TagRegistrationTests(SimpleTestCase):
|
class TagRegistrationTests(SimpleTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.library = Library()
|
self.library = Library()
|
||||||
|
Loading…
Reference in New Issue
Block a user