Как написать расширение "Joiner" для Jinja2? - PullRequest
1 голос
/ 01 октября 2010

Здравствуйте, я пытался создать расширение для 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

1 Ответ

4 голосов
/ 02 октября 2010

Будет ли работать встроенный класс 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 %}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...