Determining first and last element for each level in django-mptt - django

I am using django mptt to display navigational menu.
{% load mptt_tags %}
<ul class="nav_menu">
{% recursetree nav_link.get_descendants %}
{% if node.is_shown %}
<li>
{{ node.title }}
{% if not node.is_leaf_node %}
<ul class="nav_menu">
{{ children }}
</ul>
{% endif %}
</li>
{% endif %}
{% endrecursetree %}
</ul>
Is there a way to mark each first child of each nav_menu with a class first-child and to mark each last child of each nav_menu with a class last-child?
For example:
<ul class="nav_menu">
<li class="first-child">
Node 1
<ul class="nav_menu">
<li class="first-child last-child">
Node 1.1
</li>
</ul>
</li>
<li>
Node 2
<ul class="nav_menu">
<li class="first-child">
Node 2.1
</li>
<li class="last-child">
Node 2.2
</li>
</ul>
</li>
<li class="last-child">
Node 3
</li>
</ul>

A node can know whether or not it is the first or last at its level by querying get_previous_sibling and get_next_sibling.
<a class="{% if not node.get_previous_sibling %}first_child {% endif %}{% if not node.get_next_sibling %}last_child{% endif %} href="{{ node.url }}">{{ node.title }}</a>
These calls should work on the node cache, so won't hit the database. However, CSS already has pseudo-selectors for first-child and last-child, so it might be better to just do any styling using those rather than with explicit classes unless you're targeting older browsers.

If not all items in the database should be shown depending on a calculated value for each node, then first item and last item in the database don't necessary match first item and last item in the list. In this case, the first item and last item can be marked by javascript/jquery:
$(document).ready(function() {
$('.nav_menu li:first-child').addClass("first-child");
$('.nav_menu li:last-child').addClass("last-child");
});

Related

Using cycle to Django?

I am developing a Django application, i had some problem,
I hope to do the following effects on Django,
<div id='cssmenu'>
<ul>
<li class='active'><a href='#'><span>Home</span></a></li>
<li><a href='#'><span>Products</span></a></li>
<li><a href='#'><span>Company</span></a></li>
<li class='last'><a href='#'><span>Contact</span></a></li>
</ul>
</div>
Django Code,
<div id='cssmenu'>
{% for child in children %}
{% cycle 'active' 'last' as cssmenu silent %}
<li class="{{ cssmenu }}">
{{ child.get_menu_title }}
{% if child.children %}
<ul>
{% show_menu from_level to_level extra_inactive extra_active template "" "" child %}
</ul>
{% endif %}
</li>
{% endfor %}
</div>
Could you help me?
That's not what cycle is for: it's for alternating between two or more alternatives. You don't want that at all.
Instead, just use the forloop attributes:
{% for child in children %}
<li class="{% if forloop.first %}active{% elif forloop.last %}last{% endif %}">...</li>
{% endfor %}
Although I suppose you don't want the first one to always be active, but you haven't given any information about how you do want to determine where 'active' goes.

Cumulative count in recoursetree?

Using django-mppt I want to browse my category hierarchy displaying the ammount of objects related to the current category in any of it's children.
Much like drill_down_for_node in the example shown, but only with the current node childrens...
The optimal would be
{% recursetree obj.get_children cumulative count model.Foreignkey.category in o_count%}
<li>
<h2>{{ node }}</h2>
{% if not node.is_leaf_node %}
<ul class="children">
{{ children }} ({{o_count}})
</ul>
{% endif %}
</li>
{% endrecursetree %}
Any pointers?
Found a function for this on the TreeManager:
https://github.com/django-mptt/django-mptt/blob/master/mptt/managers.py#L250
Add the counts to the queryset in your view:
context['qs_with_related_count'] = model1.objects.add_related_count(
obj.get_children(),
model2,
'category',
'o_count',
True
)
and in template:
{% recursetree qs_with_related_count %}
<li>
<h2>{{ node }} ({{ node.o_count }})</h2>
{% if not node.is_leaf_node %}
<ul class="children">
{{ children }}
</ul>
{% endif %}
</li>
{% endrecursetree %}
Unfortunately I was trying to use a ManyToManyField as rel_field:
https://github.com/django-mptt/django-mptt/issues/90
but it looks like you are in luck ('category' not 'categories') :)

Django MPTT's template tags destroying HTML markup

I'm using Django MPTT to handle my hierarchical Data in my Django application. I'd like to render a TreeView using the recursetree tag. However, with this code :
{% recursetree system_list %}
<li>{{ node.title }}
{% if not node.is_leaf_node %}
<ul>
{{ children.title }}
</ul>
{% endif %}
</li>
{% endrecursetree %}
I get such a markup :
<li>Uppeur kar parK
<ul>
<Li>Caillou
<Ul>
&Lt;Li&Gt;Patate
&Lt;/Li&Gt;
&Lt;Li&Gt;Courgette
&Lt;/Li&Gt;
&Lt;Li&Gt;Artichaud
&Lt;/Li&Gt;
&Lt;Li&Gt;Brocoli
&Lt;/Li&Gt;
</Ul>
</Li>
<Li>Pierre
</Li>
</ul>
</li>
Do you know why the markup is escaped after the second level ? How could I fix that ?
Try {{ children }} instead of {{ children.title }}.

Django template {%for%} tag add li every 4th element

I need to represent collection in the template and wrap every four elements in the
<li></li>
The template should be like this:
<ul>
<li>
<a></a>
<a></a>
<a></a>
<a></a>
</li>
<li>
<a></a>
<a></a>
<a></a>
<a></a>
</li>
<li>
<a></a>
<a></a>
<a></a>
<a></a>
</li>
</ul>
So i need to do it in the {% for %}
{% for obj in objects %}
{#add at 1th and every 4th element li wrap somehow#}
<a>{{object}}</a>
{# the same closing tag li#}
{% endfor %}
The following should solve your problem, using built-in template tags :
<ul>
<li>
{% for obj in objects %}
<a>{{ obj }}</a>
{# if the the forloop counter is divisible by 4, close the <li> tag and open a new one #}
{% if forloop.counter|divisibleby:4 %}
</li>
<li>
{% endif %}
{% endfor %}
</li>
</ul>
You can use the divisibleby tag as mentioned before, but for template clearing purposes I usually prefer a helper function that returns a generator:
def grouped(l, n):
for i in xrange(0, len(l), n):
yield l[i:i+n]
example simplistic view:
from app.helpers import grouped
def foo(request):
context['object_list'] = grouped(Bar.objects.all(), 4)
return render_to_response('index.html', context)
example template:
{% for group in object_list %}
<ul>
{% for object in group %}
<li>{{ object }}</li>
{% endfor %}
</ul>
{% endfor %}
you can use divisibleby built-in filter, here is link to django documentation
so something like this would work
{% if value|divisibleby 4 %}
#your conditional code
{% endif %}
if you want to work it with checking first forloop and last forloop you could use this :
<ul>
{% for obj in objects %}
{% if forloop.first %}
<li>
{% endif %}
<a>{{obj}}</a>
{% if forloop.counter|divisibleby:4 and not forloop.first %}
</li>
<li>
{% endif %}
{% if forloop.last %}
</li>
{% endif %}
{% endfor %}
</ul>
I personally would consider to separate the elements in the view before passing them to the template and then using nested for loops. Except this you really only have the filter or templatetag option as Vaibhav Mishra mentioned.

Nested dictionaries and Django template

When there is a dictionary:
menu = {'title': 'Link1',
'children': {'title': 'Child of Link1',
'children': #etc..}
}
How would one iterate over it so that the output becomes:
<ul>
<li>Link1
<ul>
<li>Child of Link 1
<ul>
<li>Grandchild of Link 1
<ul>etc..</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
Currently I use this, but it obviously only goes to a depth of one.
<ul id="mainnavigation">
{% for k,v in admin.items %} #ContextProcessor, because this menu needs to know the current path.
<li class="expandable">{{ v.title }}
{% if v.children != None %}
<ul>
{% for id,child in v.children.items %}
<li class="expandable">{{ child.title }}</li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>
One way would be to manually repeat the loop for each level, however such a solution is ugly and I am hoping there is a more DRY one.
You'll have to make custom template tag for this. I would choose inclusion tag.
{% for k,v in var %}
{% my_tag v.children %}
{% endfor %}
If you already know that every part is just going to have title & children for each, why not just do nested tuples? You can then just use the built in filter, unordered_list.
So, it'd be:
menu = (Link 1 (Child of Link 1 (Grandchild of Link 1 (...) )))
and
<ul id="mainnavigation">
{{ menu|unordered_list }}
</ul>