Django inclusion tag with configurable template

前端 未结 5 934
不知归路
不知归路 2020-12-13 14:42

I\'ve created an inclusion tag, however I\'d like to be able to make the template optionally configurable. There doesn\'t seem to be support for this out of the box, so I\'d

5条回答
  •  陌清茗
    陌清茗 (楼主)
    2020-12-13 15:37

    I had to do something like this for a project and since we needed more than one of this kind of inclusion tag I made a decorator based on django inclusion_tag decorator. This is the code:

    # -*- 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
    

    You have to return a tuple with the template (or template list) and the context dict. Note that you have to pass the register (Library instance) in the decorator call:

    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})
    

    Hope this helps

提交回复
热议问题