Package: django-navutils
Note: this package is still in beta. It has been successfully used in a few projects of my own. However, API may be subject to backward incompatible changes until the first major version is released.Django-navutils is a lightweight package for handling menu and breadcrumbs inside your django project.FeaturesRequirementsInstallUsageMenusBreadcrumbsChangelogLicenseFeaturesNo database involved (unless you want it): menus and breadcrumbs are plain old python codeHighly customizableConditionnal menu items display: you want to show a menu link to authenticated users only ? Anonymous ? Staff members ? A custom criteria ? You’re covered !i18n-friendly: you can rely on usual django translation mechanismsUnlimited menusSemi-automatic current menu node detectionRequirementsPython >= 2.7 or >= 3.3Django >= 1.7The menu system may be integrated in any project, but the breadcrumbs part requires
that you use class-based views.InstallPackage is available on pip and can be installed viapip installdjango-navutils.You’ll also have to addnavutilsto yoursettings.INSTALLED_APPSAlso add the following tosettings.CONTEXT_PROCESSORS:CONTEXT_PROCESSORS=(# ...'navutils.context_processors.menus',)UsageMenusNavutils represents menus usingMenuandNodeinstances, each menu being a collection of
node instances representing a menu link. Nodes may have children, which are alsoNodeinstances.Let’s see a minimal example.yourapp/menu.py:fromnavutilsimportmenumain_menu=menu.Menu('main')menu.register(main_menu)# will be shown to everybodyblog=menu.Node(id='blog',label='Blog',pattern_name='blog:index')main_menu.register(blog)# nodes created with a pattern_name argument will use django reverseassertblog.get_url()=='/blog'# if you want to disable reversion, use the url argumentdjango=menu.Node(id='django',label='Django project',url='http://djangoproject.com',link_attrs={'target':'_blank'})# Each node instance can accept an arbitrary number of childrenblog.add(menu.Node(id='last_entries',label='Last entries',pattern_name='blog:last_entries'))blog.add(menu.Node(id='archives',label='Archives',pattern_name='blog:archives'))# will be shown to anonymous users onlylogin=menu.AnonymousNode(id='login',label='Login',pattern_name='accounts_login',link_attrs={'class':'big-button'})main_menu.register(login)# will be shown to authenticated users onlylogout=menu.AuthenticatedNode(id='logout',label='Logout',pattern_name='accounts_logout')main_menu.register(logout)yourapp/templates/index.html:{% load navutils_tags %}
{% render_menu menu=menus.main user=request.user %}For an anonymous user, this would output something like:<navclass="main-menu"><ul><liclass="has-children menu-item"><ahref="/blog">Blog<a><ulclass="sub-menu"><liclass="menu-item"><ahref="/blog/latest">Latest entries</a></li><liclass="menu-item"><ahref="/blog/archives">Archives</a></li></ul></li><liclass="menu-item"><ahref="http://djangoproject.com"target="_blank">Django project</a></li><liclass="menu-item"><ahref="/login"class="big-button">Login</a></li></ul></nav>You can also directly set children nodes on parent instanciation with thechildrenargument:user=menu.Node(id='user',label='Greetings',pattern_name='user:dashboard',children=[menu.Node(id='logout',label='Logout',pattern_name='user:logout'),# you can nest children indefinitelymenu.Node(id='settings',label='Settings',pattern_name='user:settings',children=[menu.Node(id='newsletter',label='Newsletter',pattern_name='user:settings:newsletter')],),])Nodes can be customized in many ways:heavily_customized_node=menu.Node('customized','My custom menu',# Supports arbitrary template values as well# like {{ request.user }}url='#',# Supports arbitrary template values as well# like {{ request.user }}# a custom CSS class that will be applied to the node on renderingcss_class='custom-class',# the <a> title attributetitle='click me!',# a path to a custom template for rendering the node# it's also possible to globally specify a custom template by naming# your template '<yourapp>/templates/navutils/node.html'template='myapp/menu/mynode.html',# extra context you can use in your node templatecontext={'foo':'bar'},# a dict of attributes that will be applied as HTML attributes on the <li>attrs={'style':'background-color: white;'}# a dict of attributes that will be applied as HTML attributes on the <a>link_attrs={'target':'_blank','data-something':'fancy-stuff'})Current nodeYou’ll probably want to highlight the current node in some way. Navutils provide
a view mixin you an inherit from in order to achieve this.Assuming the following menu:fromnavutilsimportmenumain_menu=menu.Menu(id='main')menu.register(main_menu)login=menu.Node(id='login',label='Login',pattern_name='account_login')main_menu.register(login)You can bind a view to a menu node with the following code:fromnavutilsimportMenuMixinclassLogin(MenuMixin,TemplateView):current_menu_item='login'Under the hood, the mixin will pass the value to the context and acurrentclass will be addedto the login node if the view is displayed. Note that you can achieve the same result
with django function-based views, as long as you manually pass the node identifier in the context,
under thecurrent_menu_itemkey.Node referenceNavutils provide a few node subclasses that address common use cases.NodeThe base Node type, will be displayed to anybody.AnonymousNodeDisplayed to anonymous users only.AuthenticatedNodeDisplayd to authenticated users only.StaffNodeDisplayed to staff users/superusers only.PermissionNodeDisplayed to users that have the given permission. Usage:vip_node=menu.PermissionNode('vip',label='VIP Area',pattern_name='vip:index',permission='access_vip_area')AllPermissionsNodeDisplayed to users that match a list of permission. Usage:permissions=['myapp.access_vip_area','myapp.drink_champagne']champagne_node=menu.AllPermissionsNode('champagne',label='Champagne!',pattern_name='vip:champagne',permissions=permissions)AnyPermissionsNodeDisplayed to users that match any given permission. Usage:permissions=['myapp.can_party','myapp.can_have_fun']have_a_good_time=menu.AnyPermissionsNode('good-time',label='Have a good time',pattern_name='good_time',permissions=permissions)PassTestNodeDisplayed to users that match a custom test. Usage:defcan_drink_alcohol(user,context):returnuser.age>=21oruser.looks_mature_for_his_agedrink_alcohol=menu.PassTestNode('drink',label='Have a beer',pattern_name='beer',test=can_drink_alcohol)If it’s not enough, you can also override the default templates:navutils/menu.html: the menu wrapper that loop through the nodesnavutils/node.html: called for displaying each node instanceAnd of course, you’re free to create your own sub-classes.BreadcrumbsBreadcrumbs are set up into views, and therefore can only be used with class-based views.First of all, you’ll probably want to define a base mixin for all your views:fromnavutilsimportBreadcrumbsMixin,BreadcrumbclassBaseMixin(BreadcrumbsMixin):defget_breadcrumbs(self):breadcrumbs=super(BaseMixin,self).get_breadcrumbs()breadcrumbs.append(Breadcrumb('Home',url='/'))returnbreadcrumbsThen, you can inherit from this view everywhere:# breadcrumbs = Home > BlogclassBlogView(BaseMixin):title='Blog'# breadcrumbs = Home > LogoutclassLogoutView(BaseMixin):title='Logout'By default, the last element of the breadcrumb is deduced from thetitleattribute of the view.
However, for a complex hierarchy, you are free to override theget_breadcrumbsmethod:# you can trigger url reversing via pattern_name, as for menu nodesclassBlogMixin(BaseMixin)defget_breadcrumbs(self):breadcrumbs=super(BlogMixin,self).get_breadcrumbs()breadcrumbs.append(Breadcrumb('Blog',pattern_name='blog:index'))returnbreadcrumbs# breadcrumbs = Home > Blog > Last entriesclassBlogIndex(BlogMixin):title='Last entries'# for dynamic titles, just override the get_title method# breadcrumbs = Home > Blog > My category nameclassCategoryDetail(BlogMixin,DetailView):model=Categorydefget_title(self):# assuming your Category model has a title fieldreturnself.object.titleThe last step is to render the breadcrumbs in your template. The provided mixin takes
care with passing data in the context, so all you need is:{% load navutils_tags %}

{% render_breadcrumbs breadcrumbs %}The breadcrumbs part of navutils is bundled with two templates, feel free to override them:navutils/breadcrumbs.html: the breadcrumbs wrappernavutils/crumb.html: used to render each crumbThat’s it !ChangelogSeeCHANGES.rst.LicenseProject is licensed under BSD license.