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 item * The ``url`` of the item, which can be a string or a callable which accepts the request object 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 of ``MenuItem`` 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 returns ``True`` or ``False`` if the ``MenuItem`` 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 %}
  • {% 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 the ``MenuItem`` object of the most specific URL match. #. ``submenu`` - This is the submenu object of the most specific URL match. #. ``has_submenu`` - This is ``True`` or ``False`` 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)) .. _simple_menu __init__.py source file: https://github.com/jazzband/django-simple-menu/blob/master/simple_menu/__init__.py .. _Bootstrap Navbar Component: https://getbootstrap.com/docs/5.1/components/navbar/