Is there a better way to write out my django-treemenu template? - django

I'm using django-treemenus
I'm curious to see if there is a better way to write out my menu.html which is my menu template. For each level that I add to my menu I have to manually add a level to my menu template.
Here is my menu.html (menu template). It works, but could it be written more efficiently?
{% load tree_menu_tags %}
{% ifequal menu_type "horizontal" %}
<ul><!-- Root -->
{% for menu_item in menu.root_item.children %}
<!-- Level 1-->
{% if menu_item.has_children %}
<li>{{ menu_item.caption }}
<ul>
{% for child in menu_item.children %}
<!-- Level 2-->
{% if child.has_children %}
<li>{{ child.caption }}
<ul>
{% for childchild in child.children %}
<!-- Level 3-->
{% if childchild.has_children %}
<li>{{ childchild.caption }}
<ul>
{% for childchildchild in childchild.children %}
{% show_menu_item childchildchild %}
{% endfor %}
</ul>
</li>
{% else %}
<li>{{ childchild.caption }}</li>
{% endif %}
<!-- End Level 3 -->
{% endfor %}
</ul>
</li>
{% else %}
<li>{{ child.caption }}</li>
{% endif %}
<!-- End Level 2 -->
{% endfor %}
</ul>
</li>
{% else %}
<li>{{ menu_item.caption }}</li>
{% endif %}
<!-- End Level 1 -->
{% endfor %}
</ul><!-- End Root -->
{% endifequal %}

I'd use a custom template tag for this kind of stuff: http://docs.djangoproject.com/en/dev/howto/custom-template-tags/
Here you can find some great examples: http://code.google.com/p/django-page-cms/source/browse/trunk/pages/templatetags/pages_tags.py?r=783

Related

'else', expected 'empty' or 'endfor'. Did you forget to register or load this tag?

{% if movies.has_other_pages %}
<ul class="pagination">
{% if movies.has_previous %}
<li class="pagination-item">
Previous
</li>
{% else %}
<li class="pagination-item">
<a>Previous</a>
</li>
{% endif %}
{% for i in movies.paginator.page_range %}
{% if movies.number == i %}
<li class="pagination-item current-page"><a>{{i}}</a></li>
{% else %}
<li class="pagination-item">{{i}}</li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
I'm trying to use paginator in django but it seems like something is wrong but I can't seem to figure it out.

How do I implement a recursive and extensible Twig template?

I'm implementing a simple website menu, but this time I'm using Twig as my template language. The depth of the menu tree is one or greater. Here's my Twig code so far (sanitized and simplified):
{# file menu1.html.twig #}
<ul>
{% import _self as renderer %}
{% for item in menu.items %}
{{ renderer.renderItem(item) }}
{% endfor %}
</ul>
{% macro renderItem(item) %}
{% block itemtag %}
<li>
{% endblock %}
{{ item.name }}
{% if item.hasItems() %}
<ul>
{% import _self as renderer %}
{% for subitem in item.items %}
{{ renderer.renderItem(subitem) }}
{% endfor %}
</ul>
{% endif %}
</li>
{% endmacro %}
Now I need to override the "itemtag" element in another template:
{# file menu2.html.twig #}
{% extends "menu1.html.twig" %}
{% block itemtag %}
<li data-foo="bar">
{% endblock %}
This will not work, as explained very well here: https://stackoverflow.com/a/26650103/220817
So how do I write a Twig template that can traverse and render a tree structure, and still allow extending templates to override certain elements in the rendered markup?
You need to use macro's if you want to do something recursive as macro's can self import themself. Here is an example of a recursive macro:
{% macro menu(m, class, currentLevel, maxLevel) %}
{%if not class is defined %}
{% set class = '' %}
{% endif %}
{%if not currentLevel is defined or currentLevel is null %}
{% set currentLevel = 1 %}
{% endif %}
{%if m is iterable%}
{% set links = m %}
{% else %}
{% set links = m.getLinks() %}
{% if m.showRoot() and m.hasRoot() %}
<li class="root{%if class != ''%} {{class}}{%endif%}">
<a href="{%if m.getRoot().getTreeLeft() > 1 %}{{ m.getRoot().getRoute() }}{% else %}#site_url#{% endif %}" {% if m.getRoot().PageId in selected_page_ids %} class="selected"{% endif %}>{{ m.getRoot().name }}</a>
</li>
{% endif %}
{% if maxLevel is null %}{% set maxLevel = m.getLevel() %}{% endif %}
{% endif %}
{% if not links is empty %}
{% for link in links %}
{% if link['selected'] is defined and link['selected'] %}
{% set is_selected = true %}
{% else %}
{% set is_selected = false %}
{% endif %}
{% set anchor_class = '' %}
{% if link.PageId in selected_page_ids or is_selected %}
{% set anchor_class='selected' %}
{% endif %}
{% if method_exists(link, 'getPageCssClass') and link.getPageCssClass() != null %}
{% set class= class~' '~link.getPageCssClass().getTitle() %}
{% endif %}
<li{%if (class|trim) != '' %} class="{{ (class|trim) }}"{% endif %}>
<a href="{{ link.getRoute }}"{%if (anchor_class|trim) != '' %} class="{{ (anchor_class|trim) }}" {% endif %} {% if link.PageId > 0 %}data-page-id="{{ link.PageId }}"{% endif %}>{{ link.name }}</a>
{% if link.hasPublicChildren and currentLevel < maxLevel %}
<ul>
{{ _self.menu(link.getPublicChildren, 'sec', (currentLevel+1), maxLevel) }}
</ul>
{% endif %}
</li>
{% endfor %}
{% endif %}
{% endmacro %}

Django template using variable in two blocks

This is my template code:
{% block content %}
{% get_latest as latest_posts %}
<ul>
{% for post in latest_posts %}
<li>
<p>{{ post.title|safe }}</p>
</li>
{% endfor %}
</ul>
{% endblock %}
{% block sidebar %}
<ul>
{% for post in latest_posts %}
<li>
<p>{{ post.title|safe }}</p>
</li>
{% endfor %}
</ul>
{% endblock %}
In the content block the for loop does works, but in the sidebar block I can't use the variable latest_posts. Can anyone help me with this?
The variable's scope is limited to the containing {% block content %}. You can either repeat the declaration inside {% block sidebar %}, or move it up a level so it's outside of {% block content %}.
example
{% get_latest as latest_posts %}
{% block content %}
<ul>
{% for post in latest_posts %}
<li>
<p>{{ post.title|safe }}</p>
</li>
{% endfor %}
</ul>
{% endblock %}
{% block sidebar %}
<ul>
{% for post in latest_posts %}
<li>
<p>{{ post.title|safe }}</p>
</li>
{% endfor %}
</ul>
{% endblock %}
or
{% block content %}
{% get_latest as latest_posts %}
<ul>
{% for post in latest_posts %}
<li>
<p>{{ post.title|safe }}</p>
</li>
{% endfor %}
</ul>
{% endblock %}
{% block sidebar %}
{% get_latest as latest_posts %}
<ul>
{% for post in latest_posts %}
<li>
<p>{{ post.title|safe }}</p>
</li>
{% endfor %}
</ul>
{% endblock %}
You can use the with statement:
Definition:
with Caches a complex variable under a simpler name. This is useful
when accessing an “expensive” method (e.g., one that hits the
database) multiple times.
Wrap your existing block into this with clause and you can safe yourself some queries. also it should solve your problem :-) Just remove the {% get_latest as latest_posts %} line
{% with latest_posts=get_latest %}
{% block content %}
<ul>
{% for post in latest_posts %}
<li>
<p>{{ post.title|safe }}</p>
</li>
{% endfor %}
</ul>
{% endblock %}
{% block sidebar %}
<ul>
{% for post in latest_posts %}
<li>
<p>{{ post.title|safe }}</p>
</li>
{% endfor %}
</ul>
{% endblock %}
{% endwith %}

Using autopagination in django and formatting issues

I'm using django-paginate and getting weird formatting issues with the {% paginate %} tag. I have attached an image of the problem.
I was just wondering what could be potentially causing this?
In the image below I'm on the first page. Notice that the 1 is cut off and also that the pages are strangely ordered and the previous/next is not really visible.
My template is just this for now:
{% extends "base.html" %}
{% load mptt_tags %}
{% load pagination_tags %}
{% load i18n %}
{% block body %}
{% autopaginate parts 20 %}
{% paginate %}
That's not related to Django, neither to Django-Paginate. It seems that you're using Bootstrap as your front-end framework, and that implies issues such that.
I've implemented a similar approach for this site manoomit.com, creating a custom template for managing pagination within django-paginate.
It looks like that:
{% if is_paginated %}
{% load i18n %}
<div class="pagination pagination-centered">
<ul>
{% if page_obj.has_previous %}
<li>‹‹ {% trans "previous" %}</li>
{% else %}
<li class="disabled">‹‹ {% trans "previous" %}</li>
{% endif %}
{% for page in pages %}
{% if page %}
{% ifequal page page_obj.number %}
<li class="active">{{ page }}</li>
{% else %}
<li>{{ page }}</li>
{% endifequal %}
{% else %}
...
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li>{% trans "next" %} ››</li>
{% else %}
<li class="disabled">{% trans "next" %} ››</li>
{% endif %}
</ul>
</div>
{% endif %}

How do you access the menu model from a Zotonic template?

I want to write my own style of menu, but I would prefer to do it in the templates rather than making my own menu scomp.
I basically want to be able to do something like:
{% if m.menu %}
<ul>
{% for top_level_id in m.menu %}
{% with m.rsc[top_level_id] as top_level %}
<li>{{ top_level.title }}
{% if top_level.menu %}
<ul>
{% for mid_level_id in top_level.menu %}
{% with m.rsc[mid_level_id] as mid_level %}
<li>{{ mid_level.title }}</li>
{% endwith %}
{% endfor %}
</ul>
{% endif %}
</li>
{% endwith %}
{% endfor %}
</ul>
{% endif %}
How do you access the menu model from a Zotonic template?
To add to my previous answer. The standard _menu.tpl receives a list with all menu items. This list is the result of a depth-first tree walk of the complete menu. Every menu is a record with
{MenuRscId, DepthOfMenu, NrInSubMenu, HasSubMenuFlag}
Where the top level menu has a depth of 1 and the first menu item in a menu has a nr of 1.
All menu items that the current user is not allowed to see are filtered out.
The code of the default template:
<ul id="{{ id_prefix }}navigation" class="clearfix at-menu do_superfish">
{% for mid,depth,nr,has_sub in menu %}
{% if not mid %}{% if depth > 1 %}</ul></li>{% endif %}
{% else %}
{% if nr == 1 and not forloop.first %}<ul{% if mid|member:path %} class="onpath"{% endif %}>{% endif %}
<li id="{{ id_prefix }}nav-item-{{nr}}"
class="{% if is_first %}first {% endif %}{% if is_last %}last{% endif %}">
<a href="{{ m.rsc[mid].page_url }}"
class="{{ m.rsc[mid].name }}{% if mid == id %} current{% else %}{% if mid|member:path %} onpath{% endif %}{% endif %}">{{ m.rsc[mid].short_title|default:m.rsc[mid].title }}</a>
{% if not has_sub %}</li>{% endif %}
{% endif %}
{% endfor %}
{% if forloop.last %}{% include "_menu_extra.tpl" %}{% endif %}
</ul>
The (upcoming) 0.5-release and tip of Zotonic use a template to display the menu. Check mod_menu/templates/_menu.tpl.
This template is called by the menu scomp.