Decorate Template Tag (In-Line include and extend with local context)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
Inside main template:

{% block step_1 %}
    <!-- Step 1: Item Details -->
    {% with wizard.step_1 as step %}
        {% decorate "item/wizard/ajax_step_decorator.html" %}
            {% localblock step_ready_js %}
                $(this).standardWizardStepFormBind(step, formId);
            {% endlocalblock %}
        {% enddecorate %}
    {% endwith %}
    <!-- / Step 1 -->
{% endblock %}

{% block step_2 %}
    <!-- Step 2: Category Select -->
    {% with wizard.step_2 as step %}
        {% decorate "item/wizard/ajax_step_decorator.html" %}
            {% localblock step_ready_js %}
                $(this).categorySelectLiveQueryActions();
            {% endlocalblock %}
        {% enddecorate %}
    {% endwith %}
    <!-- / Step 2 -->
{% endblock %}


Decorator: item/wizard/ajax_step_decorator.html

{% load conversation %}
{% load common_webdesign %}

<form id="{{ step.get_form_id }}" name="{{ step.get_form_name }}" action="{{ step.get_action }}" method="POST" class="standard-form">{% cid_hidden %}
    <div id="{{ step.get_div_id }}">
        <div id="{{ step.get_inner_div_id }}">
        {% if step.available %}
            {% localblock step_available_body %}
                {% include step.template_name %}
            {% endlocalblock %}
        {% else %}
            {% localblock step_unavailable_body %}
                {% include "item/wizard/disabled_step_block.html" %}
            {% endlocalblock %}
        {% endif %}
        </div>
    </div>
</form>

<script language="javascript">
    $(this).ready(function() {
        // Provides: step, formName, formId & disabledId
        {% include "item/wizard/step_javascript_vars.html" %}

        {% localblock step_ready_js %}{% endlocalblock %}
    });
</script>

Implementation (templatetags):

def decorate(parser, token):
    nodelist = parser.parse(('enddecorate',))
    parser.delete_first_token()

    try:
        tag_name, template_name = token.split_contents()
    except ValueError:
        raise TemplateSyntaxError, "%r tag requires one argument" % \
            token.contents.split()[0]

    decorateNode = DecorateNode(nodelist, parser.compile_filter(template_name))

    return decorateNode

class DecorateNode(Node):
    def __init__(self, nodelist, template_name):
        self.nodelist = nodelist
        self.template_name = template_name
        self.blocks = dict([(n.name, n) for n in nodelist.get_nodes_by_type(LocalBlockNode)])

    def render(self, context):
        template_name = self.template_name.resolve(context)
        t = get_template(template_name)
        context['localblock'] = self.blocks

        return t.render(context)

class LocalBlockNode(Node):
    def __init__(self, name, nodelist, parent=None):
        self.name, self.nodelist, self.parent = name, nodelist, parent

    def __repr__(self):
        return "<LocalBlock Node: %s. Contents: %r>" % (self.name, self.nodelist)

    def render(self, context):
        try:
            if self.name and 'localblock' in context:
                local_blocks = context['localblock']
                if local_blocks and self.name in local_blocks:
                    block = local_blocks[self.name]
                    return block.nodelist.render(context)
        except Except, e:
            logger.error("Failed to render LocalBlockNode")
            logger.error(e)
            raise

        # Failed to render contextual element, render this one:
        return self.nodelist.render(context)

def localblock(parser, token):
    """
    Define a block that can be overridden by child templates.
    """
    bits = token.contents.split()
    if len(bits) != 2:
        raise TemplateSyntaxError, "'%s' tag takes only one argument" % bits[0]
    block_name = bits[1]
    nodelist = parser.parse(('endlocalblock', 'endlocalblock %s' % block_name))
    parser.delete_first_token()
    return LocalBlockNode(block_name, nodelist)

register.tag(localblock)
register.tag(decorate)

More like this

  1. Silently-failing include tag by brutasse 3 years, 10 months ago
  2. "Partial Templates" - an alternative to "include" by vigrid 5 years, 2 months ago
  3. Template Tag Render Decorator by stephen_mcd 4 years, 1 month ago
  4. Private Context Decorator by acdha 4 years, 8 months ago
  5. {% renderonce %} template tag by corrr 3 years, 10 months ago

Comments

mogga (on March 3, 2010):

I think this is what I need... but i don't really understand what it does from your example... probably because it's hard to read with the javascript interspersed... something simple for simpletons please!

#

(Forgotten your password?)