[Django]-Django inclusion tag with configurable template

8πŸ‘

βœ…

The inclusion_tag decorator is just a shortcut – it’s meant as a simple way of rendering a specific template with a specific context. As soon as you want to move outside of that, it can no longer help you. But that just means you’ll have to write the tag the long way, as explained in the documentation, and pass the template you want as a parameter.

38πŸ‘

I use simple_tag when i need to do that:

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,
        ...
    }))
πŸ‘€bbrik

14πŸ‘

This post saved my life: http://djangosnippets.org/snippets/1329/

The key is to add to a "dummy template":

{% extends template %}
πŸ‘€cmaluenda

4πŸ‘

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

πŸ‘€gonz

0πŸ‘

A solution could be a regular inclusion_tag which pass dynamic template name to context.

Like this :

# 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

And the template:

<!-- include_tag.html -->

{% include dynamic_template %}

The tricks here is, when I call {% tag_manager %}, it includes include_tag.html which in turn includes the template returned by resolve_template() (not included for brevity).

Hope this helps…

πŸ‘€cvng

0πŸ‘

A variation of the @cvngβ€˜s answer that doesn’t require creating an include_tag.html template:

@register.inclusion_tag(template.Template('{% include dynamic_template %}'),
                        takes_context=True)
def dynamic_tag(context):
    context.update({
        'dynamic_template': 'path/to/your/template.html',
    })
    return context
πŸ‘€geogeo

Leave a comment