3

I'm trying to generate a list akin to:

<ul>
    <li>Parent 1
        <ul>
            <li>Child 1</li>
            <li>Child 2</li>
            <li class="last">Child 3</li>
        </ul>
    </li>
    <li>Parent 2
        <ul>
            <li>Child 1</li>
            <li>Child 2</li>
            <li class="last">Child 3</li>
        </ul>
    </li>
    <li class="last">Parent 3
        <ul>
            <li>Child 1</li>
            <li>Child 2</li>
            <li class="last">Child 3</li>
        </ul>
    </li>
</ul>

I originally just did:

<ul>
    {% recursetree mytree %}
    <li class="{% if not node.get_next_sibling %}last{% endif %}">
        {{ node }}
        {% if not node.is_leaf_node %}
        <ul>
            {{ children }}
        </ul>
        {% endif %}
    </li>
</ul>

However, the call to node.get_next_sibling results in an extra query for each item. Obviously, that's not ideal. So I tried using tree_info and structure.closed_levels to determine the last item:

{% for node,structure in mytree|tree_info %}
    {% if structure.new_level %}<ul><li>{% else %}</li><li>{% endif %}
        <li class="{% if structure.closed_levels|length > 0 %}last{% endif %}">
            {{ node }}
    {% for level in structure.closed_levels %}</li></ul>{% endfor %}
{% endfor %}

This works great, except the last root level item does not get the "last" class, because its structure.closed_levels is always an empty list. (It really only works for child items).

I'm sure I'm not the first to need to accomplish something similar, so I'm hoping someone here might have a solution already.

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
  • Haha. FWIW, it's *much* easier to add a class to the "first" item than the "last". Just have to alter my CSS a little to compensate for that. Still interested in finding out a way to label the "last" item though. – Chris Pratt Jan 05 '12 at 20:25
  • 1
    Snarky answer: `$('ul li:last-child').addClass('last')` – Elf Sternberg Jan 05 '12 at 22:25
  • Haha. Yeah, I thought of that too, but it's a positioning thing (need to remove margin from last item), so it would break if JS was disabled. – Chris Pratt Jan 05 '12 at 22:37
  • How likely is it that you're using IE6? Less than 1% of the US is still using IE6. I mean, if that's the concern, then the CSS last-child ought to work just as well. Or are you worried that CSS will also be disabled? – Elf Sternberg Jan 09 '12 at 18:26
  • Actually, IE7 also doesn't support `:last-child` either, and while we're not worried about IE6, IE7 unfortunately is still relevant. – Chris Pratt Jan 09 '12 at 19:36
  • We use mptt in merengue project http://www.merengueproject.org/ and this works. If you are interested you can download the code. See this code http://dev.merengueproject.org/browser/trunk/merengueproj/merengue/section/templates/section/inc.item_menu_tag.html – Goin Jan 27 '12 at 22:40

2 Answers2

1

I imagine you should be able to get the info you need from the MPTT order info. Here's a good intro to how MPTT works (linked from the django-mptt docs). The key is keeping a reference to the parent around, so you can check whether your node's "right" attribute is one less than your parent's "left".

django-mptt special-cases the root nodes, to let you have multiple trees. If you're iterating over the nodes in a single tree, something like this should work (though I haven't tested):

<ul class="root">
    {% recursetree nodes %}
        <li class="{% if parent == None or node.rgt|plusone == parent.lft %}last{% endif %}">
            {{ node.name }}
            {% if not node.is_leaf_node %}
                {% with node as parent %}
                <ul class="children">
                    {{ children }}
                </ul>
                {% endwith %}
            {% endif %}
        </li>
    {% endrecursetree %}
</ul>

If, however, "nodes" holds a list of all your roots, you'll need to catch that explicitly. Something like this should do the trick:

{% with nodes|last as lastnode %}
<ul class="root">
    {% recursetree nodes %}
        <li class="{% if node == lastnode or parent and node.rgt|plusone == parent.lft %}last{% endif %}">
            {{ node.name }}
            {% if not node.is_leaf_node %}
                {% with node as parent %}
                <ul class="children">
                    {{ children }}
                </ul>
                {% endwith %}
            {% endif %}
        </li>
    {% endrecursetree %}
</ul>
{% endwith %}

You'll notice that the code above references a "plusone" template filter. Something like this should do:

from django import template

register = template.Library()

@register.filter
def plusone(value):
    return value + 1

All that being said, this is a little too much template computation for my liking. If this is something you're doing regularly, it's probably wise to wrap it up in a custom template tag.

Gabriel Grant
  • 5,415
  • 2
  • 32
  • 40
0

You should use the{{forloop.last}} or {{if 1 == forloop.revcounter}} 2nd last would be {{if 2 == forloop.revcounter}}

to add classes with django logic...

if you just want to add styles you can use css selecotrs :last-child instead of adding a class

css selectors to work for jquery if you need it for javascript and are using jquery

frederik-b
  • 178
  • 1
  • 6