Using django-simple-menu¶
Usage Overview¶
django-simple-menu
lets you define multiple different menus that you can
access from within your templates. This way you can have a menu for your main
navigation, another menu for logged in users, another menu for anonymous users,
etc.
To define your menus you need to create a file named menus.py
inside of the
app that you wish to hook menus up to. In the menus.py
file you should
import the Menu
and MenuItem
classes from the simple_menu
package:
from simple_menu import Menu, MenuItem
The Menu
class exposes a class method named add_item
that accepts two
arguments; the menu name you want to add to, and the MenuItem
you’re going
to add.
The MenuItem
class should be instantiated and passed to the add_item
class method with the appropriate parameters. MenuItem
accepts a wide
number of options to its constructor method, the majority of which are simply
attributes that become available in your templates when you’re rendering out
the menus.
MenuItem
requires the first two arguments:
The
title
of the itemThe
url
of the item, which can be a string or a callable which accepts the requestobject and returns a string.
The keywords that affect menu generation are:
The
weight
keyword argument affects sorting of the menu.The
children
keyword argument is either a list ofMenuItem
objects, or a callable which accepts the request object and returns a list.The
check
keyword argument is a callable that accepts the request object and returnsTrue
orFalse
if theMenuItem
should be visible for this request
Additional kwargs can be passed to MenuItem
and these will become
available in your templates. For example adding separator=True
could be
used to add separators to menus {% if item.separator %}<li
class="divider"></li>{% endif %}
For the full list of MenuItem
options see the
simple_menu __init__.py source file.
Usage Example¶
Example:
# Add two items to our main menu
Menu.add_item("main", MenuItem("Tools",
reverse("myapp.views.tools"),
weight=10,
icon="tools"))
Menu.add_item("main", MenuItem("Reports",
reverse("myapp.views.reports"),
weight=20,
icon="report"))
# Define children for the my account menu
myaccount_children = (
MenuItem("Edit Profile",
reverse("accounts.views.editprofile"),
weight=10,
icon="user"),
MenuItem("Admin",
reverse("admin:index"),
weight=80,
separator=True,
check=lambda request: request.user.is_superuser),
MenuItem("Logout",
reverse("accounts.views.logout"),
weight=90,
separator=True,
icon="user"),
)
# Add a My Account item to our user menu
Menu.add_item("user", MenuItem("My Account",
reverse("accounts.views.myaccount"),
weight=10,
children=myaccount_children))
Once you have your menus defined you need to incorporate them into your
templates. This is done through the generate_menu
template tag:
{% extends "base.html" %}
{% load simple_menu %}
{% block content %}
{% generate_menu %}
...
{% endblock %}
Note that generate_menu
must be called inside of a block.
Once you call generate_menu
all of your MenuItems will be evaluated and
the following items will be set in the context for you.
menus
- This is an object that contains all of the lists of menus as attribute names:{% for item in menus.user %} ... {% endfor %}
selected_menu
- This is theMenuItem
object of the most specific URL match.submenu
- This is the submenu object of the most specific URL match.has_submenu
- This isTrue
orFalse
if the selected menu has children.
See the bootstrap-navbar.html file in the templates dir of the source code for an example that renders menus for the Bootstrap Navbar Component. You can use it like:
{% with menu=menus.main %}{% include "bootstrap-navbar.html" %}{% endwith %}
Check generalizations¶
If your application is dynamic enough, or complex enough, you may find that you
want to generalize your check logic based on a permissions model, or something
similar. To accomplish this you can create your own custom MenuItem
implementation with a check
method.
This assumes you have a utils
package.
utils/menus.py
:
from django.core.urlresolvers import resolve
from simple_menu import MenuItem
class ViewMenuItem(MenuItem):
"""Custom MenuItem that checks permissions based on the view associated
with a URL"""
def check(self, request):
"""Check permissions based on our view"""
is_visible = True
match = resolve(self.url)
# do something with match, and possibly change is_visible...
self.visible = is_visible
reports/menus.py
:
from utils.menus import ViewMenuItem
from simple_menu import Menu, MenuItem
from django.core.urlresolvers import reverse
# Since we use ViewMenuItem here we do not need to define checks, instead
# the check logic will change their visibility based on the permissions
# attached to the views we reverse here.
reports_children = (
ViewMenuItem("Staff Only", reverse("reports.views.staff")),
ViewMenuItem("Superuser Only", reverse("reports.views.superuser"))
)
Menu.add_item("main", MenuItem("Reports Index",
reverse("reports.views.index"),
children=reports_children))