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