如何编写“连接器” Jinja2 的扩展?

发布于 2024-09-25 19:46:10 字数 1990 浏览 6 评论 0原文

您好,我一直在尝试为 jinja2 创建一个扩展,它将使用分隔符连接多个项目,同时跳过计算结果为空格的项目(模板片段)。

这些片段有几个,您永远不知道哪些片段将是非空的,哪些片段将是非空的。

听起来像是一项微不足道的任务,但我真的很难让它在 jinja2 中工作。也许部分原因是 jinja 不允许定义自定义模板节点。

您有什么建议吗?下面是一个将完成解析工作的片段,但它缺少评估部分。

class JoinerExtension(Extension):
    """Template tag that joins non-whitespace (string) items
    with a specified separator

    Usage syntax:

    {% joinitems separator='|' %}
    ....
    {% separator %}
    ....
    {% separator %}
    ....
    {% endjoinitems %}

    where value of "separator" within the joinitems tag
    can be an expression, not necessarily a sting
    """

    tags = set(['joinitems'])

    def parse(self, parser):
        """parse function for the 
        joinitems template tag
        """
        lineno = next(parser.stream).lineno

        #1) read separator
        separator = None
        while parser.stream.current.type != 'block_end':
            name = parser.stream.expect('name')
            if name.value != 'separator':
                parser.fail('found %r, "separator" expected' %
                            name.value, name.lineno,
                            exc=TemplateAssertionError)

            # expressions
            if parser.stream.current.type == 'assign':
                next(parser.stream)
                separator = parser.parse_expression()
            else:
                var = parser.stream.current
                parser.fail('assignment expected after the separator' %
                            var.value, var.lineno,
                            exc=TemplateAssertionError)

        #2) read the items
        items = list()
        end_tags = ['name:separator', 'name:endjoinitems']
        while True:
            item = parser.parse_statements(end_tags)
            items.append(item)
            if parser.stream.current.test('name:separator'):
                next(parser.stream)
            else:
                next(parser.stream)
                break

Hi I've been trying to create an extension for jinja2 that would join multiple items with a separator, while skipping items (template fragments) that evaluate to whitespace.

There are several of those fragments and you never know in advance which ones will be non-empty and which ones will.

Sounds like a trivial task, but I had real hard time making this to work in jinja2. Maybe part of the reason is that jinja does not allow to define custom template nodes.

Would you have any suggestions? Below is a snippet that will do the parsing job, but it's missing the evaluation part.

class JoinerExtension(Extension):
    """Template tag that joins non-whitespace (string) items
    with a specified separator

    Usage syntax:

    {% joinitems separator='|' %}
    ....
    {% separator %}
    ....
    {% separator %}
    ....
    {% endjoinitems %}

    where value of "separator" within the joinitems tag
    can be an expression, not necessarily a sting
    """

    tags = set(['joinitems'])

    def parse(self, parser):
        """parse function for the 
        joinitems template tag
        """
        lineno = next(parser.stream).lineno

        #1) read separator
        separator = None
        while parser.stream.current.type != 'block_end':
            name = parser.stream.expect('name')
            if name.value != 'separator':
                parser.fail('found %r, "separator" expected' %
                            name.value, name.lineno,
                            exc=TemplateAssertionError)

            # expressions
            if parser.stream.current.type == 'assign':
                next(parser.stream)
                separator = parser.parse_expression()
            else:
                var = parser.stream.current
                parser.fail('assignment expected after the separator' %
                            var.value, var.lineno,
                            exc=TemplateAssertionError)

        #2) read the items
        items = list()
        end_tags = ['name:separator', 'name:endjoinitems']
        while True:
            item = parser.parse_statements(end_tags)
            items.append(item)
            if parser.stream.current.test('name:separator'):
                next(parser.stream)
            else:
                next(parser.stream)
                break

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

一江春梦 2024-10-02 19:46:10

内置的 joiner 类可能有效吗?这是文档中的一个简单示例。

{% set pipe = joiner("|") %}
{% if categories %} {{ pipe() }}
    Categories: {{ categories|join(", ") }}
{% endif %}
{% if author %} {{ pipe() }}
    Author: {{ author() }}
{% endif %}
{% if can_edit %} {{ pipe() }}
    <a href="?action=edit">Edit</a>
{% endif %}

您提到提前不知道哪些片段将为空;也许可以在“显示”之前将每个片段的值存储在变量中,以便您可以确定哪些片段确实是空的。例如:

{% set pipe = joiner("|") %}
{% set fragment = gen_fragment1() %}
{% if fragment|trim is not "" %} 
    {{ pipe() }} {{ fragment }}
{% endif %}
...

您甚至可以将上述模式封装在宏中以减少重复:

{% set pipe = joiner("|") %}
{{ print_if_notblank(pipe, gen_fragment1()) }}
{{ print_if_notblank(pipe, gen_fragment2()) }}
...

其中 print_if_notblank 是定义为的宏:

{% macro print_if_notblank(separator, content) %}
    {% if content|trim is not "" %}
        {{ separator() }} {{ content }}
    {% endif %}
{% endmacro %}

Would the built-in joiner class potentially work? Here is a simple example from the documentation.

{% set pipe = joiner("|") %}
{% if categories %} {{ pipe() }}
    Categories: {{ categories|join(", ") }}
{% endif %}
{% if author %} {{ pipe() }}
    Author: {{ author() }}
{% endif %}
{% if can_edit %} {{ pipe() }}
    <a href="?action=edit">Edit</a>
{% endif %}

You mentioned that it is not known ahead of time which fragments will be empty; perhaps it is possible to store the value of each fragment in a variable before "displaying" it so that you can determine which fragments are indeed empty. For example:

{% set pipe = joiner("|") %}
{% set fragment = gen_fragment1() %}
{% if fragment|trim is not "" %} 
    {{ pipe() }} {{ fragment }}
{% endif %}
...

You could even encapsulate the above pattern in a macro to reduce repetition:

{% set pipe = joiner("|") %}
{{ print_if_notblank(pipe, gen_fragment1()) }}
{{ print_if_notblank(pipe, gen_fragment2()) }}
...

where print_if_notblank is a macro defined as:

{% macro print_if_notblank(separator, content) %}
    {% if content|trim is not "" %}
        {{ separator() }} {{ content }}
    {% endif %}
{% endmacro %}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文