1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
|
===============================
Working with trees in templates
===============================
.. highlightlang:: html+django
.. contents::
:depth: 3
Getting started
=========================
Before you can use these tags/filters, you must:
* add "mptt" to your ``INSTALLED_APPS`` in ``settings.py``
* add ``{% load mptt_tags %}`` in your template.
Recursive tags
==============
.. versionadded:: 0.4
For most setups, recursive tags are the easiest and most efficient way to render
trees.
``recursetree``
~~~~~~~~~~~~~~~
.. versionadded:: 0.4
This tag renders a section of your template recursively for each node in your
tree.
For example::
<ul class="root">
{% recursetree nodes %}
<li>
{{ node.name }}
{% if not node.is_leaf_node %}
<ul class="children">
{{ children }}
</ul>
{% endif %}
</li>
{% endrecursetree %}
</ul>
Note the special variables ``node`` and ``children``.
These are magically inserted into your context while you're inside the
``recursetree`` tag.
``node`` is an instance of your MPTT model.
``children`` : This variable holds the rendered HTML for the children of
``node``.
.. note::
If you already have variables called ``node`` or ``children`` in your
template, and you need to access them inside the ``recursetree`` block,
you'll need to alias them to some other name first::
{% with node as friendly_node %}
{% recursetree nodes %}
{{ node.name }} is friends with {{ friendly_node.name }}
{{ children }}
{% endrecursetree %}
{% endwith %}
Iterative tags
==============
Why? These tags are better suited to unusually deep trees. If you expect to
have trees with depth > 20, you should use these instead of the above.
``full_tree_for_model``
~~~~~~~~~~~~~~~~~~~~~~~
Populates a template variable with a ``QuerySet`` containing the full
tree for a given model.
Usage::
{% full_tree_for_model [model] as [varname] %}
The model is specified in ``[appname].[modelname]`` format.
Example::
{% full_tree_for_model tests.Genre as genres %}
``drilldown_tree_for_node``
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Populates a template variable with the drilldown tree for a given node,
optionally counting the number of items associated with its children.
A drilldown tree consists of a node's ancestors, itself and its
immediate children or all descendants. For example, a drilldown tree
for a book category "Personal Finance" might look something like::
Books
Business, Finance & Law
Personal Finance
Budgeting (220)
Financial Planning (670)
Usage::
{% drilldown_tree_for_node [node] as [varname] %}
Extended usage::
{% drilldown_tree_for_node [node] as [varname] all_descendants %}
{% drilldown_tree_for_node [node] as [varname] count [foreign_key] in [count_attr] %}
{% drilldown_tree_for_node [node] as [varname] cumulative count [foreign_key] in [count_attr] %}
The foreign key is specified in ``[appname].[modelname].[fieldname]``
format, where ``fieldname`` is the name of a field in the specified
model which relates it to the given node's model.
When this form is used, a ``count_attr`` attribute on each child of the
given node in the drilldown tree will contain a count of the number of
items associated with it through the given foreign key.
If cumulative is also specified, this count will be for items related to
the child node and all of its descendants.
Examples::
{% drilldown_tree_for_node genre as drilldown %}
{% drilldown_tree_for_node genre as drilldown count tests.Game.genre in game_count %}
{% drilldown_tree_for_node genre as drilldown cumulative count tests.Game.genre in game_count %}
See `Examples`_ for an example of how to render a drilldown
tree as a nested list.
Filters
=======
``tree_info`` filter
~~~~~~~~~~~~~~~~~~~~
Given a list of tree items, iterates over the list, generating
two-tuples of the current tree item and a ``dict`` containing
information about the tree structure around the item, with the following
keys:
``'new_level'``
``True`` if the current item is the start of a new level in
the tree, ``False`` otherwise.
``'closed_levels'``
A list of levels which end after the current item. This will
be an empty list if the next item's level is the same as or
greater than the level of the current item.
An optional argument can be provided to specify extra details about the
structure which should appear in the ``dict``. This should be a
comma-separated list of feature names. The valid feature names are:
ancestors
Adds a list of unicode representations of the ancestors of the
current node, in descending order (root node first, immediate
parent last), under the key ``'ancestors'``.
For example: given the sample tree below, the contents of the list
which would be available under the ``'ancestors'`` key are given
on the right::
Books -> []
Sci-fi -> ['Books']
Dystopian Futures -> ['Books', 'Sci-fi']
Using this filter with unpacking in a ``{% for %}`` tag, you should have
enough information about the tree structure to create a hierarchical
representation of the tree.
Example::
{% for genre,structure in genres|tree_info %}
{% if structure.new_level %}<ul><li>{% else %}</li><li>{% endif %}
{{ genre.name }}
{% for level in structure.closed_levels %}</li></ul>{% endfor %}
{% endfor %}
``tree_path``
~~~~~~~~~~~~~
Creates a tree path represented by a list of items by joining the items
with a separator, which can be provided as an optional argument,
defaulting to ``' :: '``.
Each path item will be coerced to unicode, so a list of model instances
may be given if required.
Example::
{{ some_list|tree_path }}
{{ some_node.get_ancestors|tree_path:" > " }}
Examples
========
Using ``drilldown_tree_for_node`` and ``tree_info`` together to render a
drilldown menu for a node, with cumulative counts of related items for the node's
children::
{% drilldown_tree_for_node genre as drilldown cumulative count tests.Game.genre in game_count %}
{% for node,structure in drilldown|tree_info %}
{% if structure.new_level %}<ul><li>{% else %}</li><li>{% endif %}
{% if node == genre %}
<strong>{{ node.name }}</strong>
{% else %}
<a href="{{ node.get_absolute_url }}">{{ node.name }}</a>
{% if node.parent_id == genre.pk %}({{ node.game_count }}){% endif %}
{% endif %}
{% for level in structure.closed_levels %}</li></ul>{% endfor %}
{% endfor %}
Using ``tree_info`` (with its optional argument) and ``tree_path`` together
to create a multiple-select, which:
* doesn't contain root nodes
* displays the full path to each node
::
<select name="classifiers" multiple="multiple" size="10">
{% for node,structure in classifiers|tree_info:"ancestors" %}
{% if node.is_child_node %}
<option value="{{ node.pk }}">
{{ structure.ancestors|tree_path }} :: {{ node }}
</option>
{% endif %}
{% endfor %}
</select>
|