Skip to content

Instantly share code, notes, and snippets.

@LowerDeez
Last active October 24, 2017 08:26
Show Gist options
  • Save LowerDeez/caabff058372d7624a582de7c9ceeff0 to your computer and use it in GitHub Desktop.
Save LowerDeez/caabff058372d7624a582de7c9ceeff0 to your computer and use it in GitHub Desktop.
Django-mptt. Getting recursive tree objects for jinja2 templates to avoid n+1 queries for children nodes when using translations fields in model(parler)
<!--Another jinja template for variant with cache categories-->
<ul class="categories">
<li>
<form id="all_categories_form" action="{{ url('articles:home') }}" method="get">
<input type="hidden" name="is_clear" value="True">
<a href="#" onclick="document.getElementById('all_categories_form').submit();">All</a>
</form>
</li>
{% cache 150 "categories_tree" %}
<form action="" method="get" class="form-group">
{% for node in categories recursive %}
{% set node_children = node.get_children() %}
{% if node.has_articles %} {# using to hide child categories, which have no articles #}
<li>
{% if node_children %}
<a class="category-dropdown" id="{{ node.id }}" type="button"
data-toggle="collapse" data-target="#category-{{ node.id }}">
<span class="glyphicon glyphicon-chevron-right"></span>
</a>&nbsp;
{% endif %}
<input class="category-checkbox" id="category-check-{{ node.id }}"
type="checkbox" name="categories" value="{{ node.id }}">&nbsp;
<a
{% if category %}
{% if node.slug == category.slug %}
class="selected"
{% endif %}
{% endif %} href="{{ node.get_absolute_url() }}" >
{{ node.name }}
</a>
</li>
{% endif %}
{% if node_children %}
<div id="category-{{ node.id }}" class="collapse">
<ul class="children child-categories">
{{ loop(node_children) }}
</ul>
</div>
{% endif %}
{% endfor %}
<button class="btn btn-success btn-category-search" type="submit">Find several categories</button>
</form>
{% endcache %}
</ul>
{% load i18n %}
{% load mptt_tags %}
<ul class="categories">
<li {% if not category %}class="selected"{% endif %}>
<a href="{% url 'articles:home' %}">{% trans "All" %}</a>
</li>
{% recursetree categories %}
<li {% if node.slug == category.slug %}class="selected"{% endif %}>
<a class="selected" href="{{ node.get_absolute_url }}">{{ node.name }}</a>
</li>
{% if not node.is_leaf_node %}
<ul class="children">
{{ children }}
</ul>
{% endif %}
{% endrecursetree %}
</ul>
<!-- Jinja template-->
<ul class="categories">
<li {% if not category %}class="selected"{% endif %}>
<a href="{{ url('articles:home') }}">{{ _('All') }}</a>
</li>
{% for node in categories recursive %}
<li>
<a {% if category %}
{% if node.slug == category.slug %}
class="selected"
{% endif %}
{% endif %} href="{{ node.get_absolute_url() }}">{{ node.name }}</a>
</li>
{% if not node.is_leaf_node() %}
<ul class="children">
{{ loop(node.get_children()) }}
</ul>
{% endif %}
{% endfor %}
</ul>
class YourListView(ListView):
def get_context_data(self, **kwargs):
# if you want to display all categories
categories = Category.objects.select_related('parent')\
.prefetch_related('translations').get_cached_trees()
context['categories'] = categories
# cached categories but only with items, which related to some objects
categories = cache.get('{}-categories'.format(reverse('articles:home')))
if categories:
prefetch_related_objects(categories, 'translations')
if not categories:
categories = Category.objects.with_articles()
# remove categories, which are children of another categories in queryset,
# because they will be rendering in template
for cat in categories:
categories = categories.exclude(id__in=cat.get_descendants())
cache.set('{}-categories'.format(reverse('articles:home')), categories, CACHE_TTL)
context['categories'] = categories
# Method in category maneger, instead articles, you can have any model, which have TreeManyToManyField and related_name (articles in this case):
# Example:
# categories = TreeManyToManyField(Category, verbose_name=_("Categories"), blank=True, related_name='articles')
# def with_articles(self):
# return self.get_queryset().exclude(articles__isnull=True).select_related('parent').distinct()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment