Тег включения Django с настраиваемым шаблоном

Я создал тег включения, однако я хотел бы иметь возможность сделать шаблон опционально настраиваемым. Похоже, что это не поддерживается из коробки, поэтому я хотел бы посмотреть, как люди это сделали - может быть, метод сначала ищет в каталоге шаблонов определенное имя шаблона, а затем возвращается к шаблону по умолчанию.

@register.inclusion_tag('foo.html', takes_context=True)

person Community    schedule 29.09.2009    source источник
comment
Чего вы пытаетесь добиться, чего {% include my_template %} не может?   -  person Dominic Rodger    schedule 29.09.2009
comment
Мне нужно добавить некоторые переменные в контекст с помощью пользовательского тега. Похоже, мне придется писать тег долго. Спасибо.   -  person    schedule 29.09.2009


Ответы (5)


Декоратор inclusion_tag — это просто ярлык — он предназначен для простого способа рендеринга определенного шаблона в определенном контексте. Как только вы захотите выйти за пределы этого, оно больше не сможет вам помочь. Но это просто означает, что вам придется писать тег длинным путем, как описано в документации, и передавать нужный шаблон в качестве параметра.

person Daniel Roseman    schedule 29.09.2009

Я использую simple_tag, когда мне нужно это сделать:

from django.template import Library, loader, Context

@register.simple_tag(takes_context=True)
def my_tag(context, template_name):

    var1 = ...

    t = loader.get_template(template_name)
    return t.render(Context({
        'var1': var1,
        ...
    }))
person bbrik    schedule 23.02.2012

Этот пост спас мне жизнь: http://djangosnippets.org/snippets/1329/

Ключ добавляется в «фиктивный шаблон»:

{% extends template %}
person cmaluenda    schedule 02.04.2013
comment
Это отличный совет! Более того, @register.inclusion_tag может принимать экземпляр Template (в дополнение к пути), поэтому вы можете сделать что-то вроде dummy = Template("""{% extends template %}"""), а затем @register.inclusion_tag(dummy, takes_context=True). ``` - person Emil; 01.03.2018

Мне пришлось сделать что-то подобное для проекта, и, поскольку нам нужно было более одного такого тега включения, я сделал декоратор на основе декоратора django include_tag. Это код:

# -*- coding: utf-8 -*-
from django import template
from inspect import getargspec
from django.template.context import Context
from django.template import Node, generic_tag_compiler, Variable
from django.utils.functional import curry


def inclusion_tag(register, context_class=Context, takes_context=False):
    def dec(func):
        params, xx, xxx, defaults = getargspec(func)
        if takes_context:
            if params[0] == 'context':
                params = params[1:]
            else:
                raise TemplateSyntaxError("Any tag function decorated with takes_context=True must have a first argument of 'context'")

        class InclusionNode(Node):
            def __init__(self, vars_to_resolve):
                self.vars_to_resolve = map(Variable, vars_to_resolve)

            def render(self, context):
                resolved_vars = [var.resolve(context) for var in self.vars_to_resolve]
                if takes_context:
                    args = [context] + resolved_vars
                else:
                    args = resolved_vars

                file_name, extra_context = func(*args)

                from django.template.loader import get_template, select_template
                if not isinstance(file_name, basestring) and is_iterable(file_name):
                    t = select_template(file_name)
                else:
                    t = get_template(file_name)
                self.nodelist = t.nodelist
                new_context = context_class(extra_context, autoescape=context.autoescape)
                # Copy across the CSRF token, if present, because inclusion
                # tags are often used for forms, and we need instructions
                # for using CSRF protection to be as simple as possible.
                csrf_token = context.get('csrf_token', None)
                if csrf_token is not None:
                    new_context['csrf_token'] = csrf_token
                return self.nodelist.render(new_context)

        compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, InclusionNode)
        compile_func.__doc__ = func.__doc__
        register.tag(getattr(func, "_decorated_function", func).__name__, compile_func)
        return func
    return dec

Вы должны вернуть кортеж с шаблоном (или списком шаблонов) и контекстом dict. Обратите внимание, что вы должны передать регистр (экземпляр библиотеки) в вызове декоратора:

from somewhere import inclusion_tag
@inclusion_tag(register)
def display_formset(formset):
    template_name = FORMSET_TEMPLATES.get(formset.model,
        'includes/inline_formset.html')
    return (template_name, {'formset': formset})

Надеюсь это поможет

person gonz    schedule 06.12.2010
comment
Любая идея, как обновить это для django 1.4? Для generic_tag_compiler теперь требуются аргументы токена и парсера. - person Gattster; 16.03.2012

Решением может быть обычный inclusion_tag, который передает имя динамического шаблона в context.

Как это :

# templatetags/tags.py

@register.inclusion_tag('include_tag.html', takes_context=True)
def tag_manager(context):
    context.update({
        'dynamic_template': resolve_template(context),
    })
    return context

И шаблон:

<!-- include_tag.html -->

{% include dynamic_template %}

Хитрость здесь в том, что когда я вызываю {% tag_manager %}, он включает include_tag.html, который, в свою очередь, включает шаблон, возвращаемый resolve_template() (не включен для краткости).

Надеюсь это поможет...

person cvng    schedule 23.10.2016