Nodes are useful for structuring a website; however, they are inherently unsuitable for creating site navigation.
The most glaring problem is that a navigation tree based on Nodes would have one Node as the root, whereas navigation usually has multiple objects at the top level.
Additionally, navigation needs to have display text that is relevant to the current context; however, Nodes do not have a field for that, and View subclasses with a name or title field will generally need to use it for database-searchable names.
Finally, Node structures are inherently unordered, while navigation is inherently ordered.
shipherd exists to resolve these issues by separating navigation structures from Node structures. It is instead structured around the way that site navigation works in the wild:
The Navigation model supplies these features by attaching itself to a Node via ForeignKey and adding a navigation property to Node which provides access to a Node instance’s inherited Navigations.
Each entry in the navigation bar is then represented by a NavigationItem, which stores information such as the order and text for the entry. Given an HttpRequest, a NavigationItem can also tell whether it is_active() or has_active_descendants().
Since the common pattern is to recurse through a navigation tree and render each part similarly, shipherd also ships with the recursenavigation template tag.
Bases: object, UserDict.DictMixin
The NavigationMapper is a dictionary-like object which allows easy fetching of the root items of a navigation for a node according to a key. A NavigationMapper instance will be available on each node instance as Node.navigation if shipherd is in the INSTALLED_APPS
Bases: philo.models.base.Entity
Navigation represents a group of NavigationItems that have an intrinsic relationship in terms of navigating a website. For example, a main navigation versus a side navigation, or a authenticated navigation versus an anonymous navigation.
A Navigation‘s NavigationItems will be accessible from its related Node and that Node‘s descendants through a NavigationMapper instance at Node.navigation. Example:
>>> node.navigation_set.all()
[]
>>> parent = node.parent
>>> items = parent.navigation_set.get(key='main').roots.all()
>>> parent.navigation["main"] == node.navigation["main"] == list(items)
True
A NavigationManager instance.
The Node which the Navigation is attached to. The Navigation will also be available to all the Node‘s descendants and will override any Navigation with the same key on any of the Node‘s ancestors.
Each Navigation has a key which consists of one or more word characters so that it can easily be accessed in a template as {{ node.navigation.this_key }}.
There is no limit to the depth of a tree of NavigationItems, but depth will limit how much of the tree will be displayed.
Bases: philo.models.base.TreeEntity, philo.models.nodes.TargetURLModel
NavigationItem(id, parent_id, lft, rght, tree_id, level, target_node_id, url_or_subpath, reversing_parameters_json, navigation_id, text, order)
A ForeignKey to a Navigation instance. If this is not null, then the NavigationItem will be a root node of the Navigation instance.
The text which will be displayed in the navigation. This is a CharField instance with max length 50.
The order in which the NavigationItem will be displayed.
Returns True if the NavigationItem is considered active for a given request and False otherwise.
Returns True if the NavigationItem has active descendants and False otherwise.
The recursenavigation templatetag takes two arguments:
It will then recursively loop over each NavigationItem in the Navigation and render the template chunk within the block. recursenavigation sets the following variables in the context:
Variable | Description |
---|---|
navloop.depth | The current depth of the loop (1 is the top level) |
navloop.depth0 | The current depth of the loop (0 is the top level) |
navloop.counter | The current iteration of the current level(1-indexed) |
navloop.counter0 | The current iteration of the current level(0-indexed) |
navloop.first | True if this is the first time through the current level |
navloop.last | True if this is the last time through the current level |
navloop.parentloop | This is the loop one level “above” the current one |
item | The current item in the loop (a NavigationItem instance) |
children | If accessed, performs the next level of recursion. |
navloop.active | True if the item is active for this request |
navloop.active_descendants | True if the item has active descendants for this request |
Example:
<ul>
{% recursenavigation node "main" %}
<li{% if navloop.active %} class='active'{% endif %}>
<a href="{{ item.get_target_url }}">{{ item.text }}</a>
{% if item.get_children %}
<ul>
{{ children }}
</ul>
{% endif %}
</li>
{% endrecursenavigation %}
</ul>
Note
{% recursenavigation %} requires that the current HttpRequest be present in the context as request. The simplest way to do this is with the request context processor. Simply make sure that django.core.context_processors.request is included in your TEMPLATE_CONTEXT_PROCESSORS setting.
Returns True if the node has a Navigation with the given key and False otherwise. If key is None, returns whether the node has any Navigations at all.
Returns the Node which hosts the Navigation which node has inherited for key. Returns node if any exceptions are encountered.