outliner template tag

  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
123
124
125
126
127
SAMPLE USAGE:

{% load handytags %}
{% outliner 'generic_section.html' companies sectionizers %}
    	<ul>
        {% for company in objs %}
            <li>{{ company.description|safe }}</li>
        {% endfor %}
        </ul>
{% endoutliner %}			   

generic_section.html:

{% for section in sections %}
	<div class="depth{{ section.depth }}">
	    <h{{ section.depth }} class="{{ sectionizer.css_class }}" id="header{{section.outline_level}}">
	    	{{ section.key }}
	    </h{{ section.depth }}>
	    <div id="outline{{section.outline_level}}">
	    	{{ section.html }}
	    </div>
	</div>
{% endfor %}

calling view:

        city_method = lambda company: company.city
        type_method = lambda company: company.get_membership().type.name
        sectionizer_dct = {
            'by_type':
                {
                    'key_method': type_method,
                    'css_class': 'company_type',
                },
            'by_city':
                {
                    'key_method': city_method,
                    'css_class': 'company_city',
                },
            }
        desired_keys = ['by_city', 'by_type'] # can dynamically set the order
        sectionizers = [sectionizer_dct[k] for k in desired_keys]
        context['companies'] = Company.objects
        context['sectionizers'] = sectionizers
    return render_with_request(...)


IMPLEMENTATION:

class OutlinerNode(Node):
    def __init__(self, nodelist, sectionizer_node, results, sectionizers):
        self.nodelist = nodelist
        self.sectionizer_node = sectionizer_node
        self.results = results
        self.sectionizers = sectionizers
    
    def render(self, context):
        queryset = self.results.resolve(context)
        sectionizers = self.sectionizers.resolve(context)
        
        def render_one_section(objs, outline_level):
            # expand current context?
            new_context = Context({
                    'objs': objs,
                    'outline_level': outline_level,
                })
            return self.nodelist.render(new_context)
        
        inner_method = render_one_section

        # we are building up decorators from the inside out, popping off
        # the end of the list
        while len(sectionizers):
            sectionizer = sectionizers.pop()
            inner_method = self.section_decorator(sectionizer, inner_method)
        # this call in turns works from the outside in
        return inner_method(queryset, '')
    
    def section_decorator(self, sectionizer, inner_method):
        # It is intentional not to inline this method...otherwise you
        # deal with all kinds of scope issues with closures.
        def render(objs, outline_level):
            return self.generic_build_sections(objs, outline_level, sectionizer, inner_method)
        return render
    
    def generic_build_sections(self, results, outline_level, sectionizer, inner_render):
        try:
            key_method = sectionizer['key_method']
        except:
            key_method = sectionizer.key_method
        results = list(results)
        results.sort(key=key_method)
        sections = [] 
        i = 0
        prefix = outline_level
        if outline_level:
            prefix += '_'
        depth = len(prefix.split('_'))
        for key, objs in groupby(results, key_method):
            i += 1 # human numbering for Outline
            outline_level = prefix + str(i)
            sections.append({
                'key': key,
                'html': inner_render(objs, outline_level),
                'outline_level': outline_level,
                'depth': depth,
                })
        context = Context({
            'sections': sections,
            'sectionizer': sectionizer,
            })
        return self.sectionizer_node.render(context)
 

@register.tag
def outliner(parser, token):
    nodelist = parser.parse(('endoutliner',))
    parser.delete_first_token()
    bits = token.split_contents()
    if len(bits) != 4:
        raise TemplateSyntaxError, "%r tag takes three arguments" % bits[0]
    path, results_var, sectionizers_var = bits[1:]
    if path[0] in ('"', "'") and path[-1] == path[0]:
        sectionizer_node = ConstantIncludeNode(path[1:-1])
    else:
        sectionizer_node = IncludeNode(bits[1])
    return OutlinerNode(nodelist, sectionizer_node, Variable(results_var), Variable(sectionizers_var))

More like this

  1. testdata tag for templates by showell 4 years, 11 months ago
  2. Complex Formsets by smagala 5 years, 2 months ago
  3. Mobilize your Django site by stevena0 5 years ago
  4. Repeat blocks with new context / simple Jinja-like macro system by miracle2k 6 years, 8 months ago
  5. Twin column model admin index by richardbolt 6 years ago

Comments

(Forgotten your password?)