Menu System¶
Django Gentelella Widgets provides a flexible menu system through the MenuItem model. Menus can be displayed in the top navigation bar, sidebar, or sidebar footer, and support hierarchical structures, permissions, and custom widgets.
MenuItem Model¶
The MenuItem model stores menu configuration in the database and supports tree-based hierarchies using django-tree-queries.
Fields¶
Field |
Type |
Description |
|---|---|---|
|
CharField |
Display text for the menu item |
|
CharField |
URL pattern name (for reversed URLs) or direct URL path |
|
CharField |
Menu location: |
|
BooleanField |
If True, |
|
CharField |
Keyword arguments for URL reverse (format: |
|
CharField |
Positional arguments for URL reverse (comma-separated, can use context like |
|
BooleanField |
If True, |
|
CharField |
CSS class for icon (e.g., |
|
BooleanField |
If True, only display icon without title (useful for footer) |
|
IntegerField |
Order of items within the same parent |
|
ForeignKey |
Parent MenuItem for hierarchical menus (None for top-level) |
|
ManyToManyField |
Permissions required to view this menu item |
Menu Categories¶
djgentelella supports three menu locations:
- main - Top Navigation Bar
Horizontal menu in the top navigation area. Supports dropdowns for child items.
- sidebar - Left Sidebar
Vertical menu in the left sidebar. Supports multiple levels of nesting with collapsible sections.
- sidebarfooter - Sidebar Footer
Icons displayed at the bottom of the sidebar. Typically used for settings, logout, and help widgets.
Template Tags¶
Load the menu template tags in your template:
{% load gentelellamenu %}
Available Tags¶
{% top_menu %}Renders the top navigation bar menu (category
'main').{% sidebar_menu %}Renders the sidebar menu (category
'sidebar').{% footer_sidebar_menu %}Renders the sidebar footer icons (category
'sidebarfooter').{% render_external_widget %}Renders content for any menu widgets. Place this where widget content should appear.
{% render_menu_js_widget %}Renders JavaScript for menu widgets. Place this in the JS block.
{% render_extra_html_menu %}Renders additional HTML for menu widgets (modals, etc.).
Example Template¶
{% extends 'gentelella/base.html' %}
{% load gentelellamenu %}
{% block sidebar %}
{% sidebar_menu %}
{% endblock %}
{% block top_navigation %}
{% top_menu %}
{% endblock %}
{% block content %}
{% render_external_widget %}
<!-- Your content here -->
{% endblock %}
{% block js %}
{% render_menu_js_widget %}
{% endblock %}
Creating Menu Items¶
Basic Link¶
A simple link without URL reversal:
from djgentelella.models import MenuItem
MenuItem.objects.create(
title='Home',
url_name='/',
category='sidebar',
is_reversed=False,
icon='fa fa-home'
)
With Django URL Reverse¶
Link to a named URL pattern:
MenuItem.objects.create(
title='Dashboard',
url_name='dashboard',
category='sidebar',
is_reversed=True,
icon='fa fa-tachometer'
)
With URL Parameters (kwargs)¶
Pass keyword arguments to the URL reverse:
MenuItem.objects.create(
title='Edit User',
url_name='user_edit',
category='sidebar',
is_reversed=True,
reversed_kwargs='pk:1', # Results in reverse('user_edit', kwargs={'pk': 1})
icon='fa fa-user'
)
With Dynamic Context¶
Access template context values like request.user.pk:
MenuItem.objects.create(
title='My Profile',
url_name='user_profile',
category='sidebar',
is_reversed=True,
reversed_args='request.user.pk', # Dynamic value from context
icon='fa fa-user-circle'
)
Hierarchical Menus¶
Create parent-child relationships for nested menus:
# Create parent
settings_menu = MenuItem.objects.create(
parent=None,
title='Settings',
url_name='#',
category='sidebar',
is_reversed=False,
icon='fa fa-cog'
)
# Create children
MenuItem.objects.create(
parent=settings_menu,
title='General',
url_name='settings_general',
category='sidebar',
is_reversed=True,
icon='fa fa-sliders'
)
MenuItem.objects.create(
parent=settings_menu,
title='Security',
url_name='settings_security',
category='sidebar',
is_reversed=True,
icon='fa fa-shield'
)
Custom Widgets¶
Use a custom widget class for special menu functionality:
from django.urls import reverse
MenuItem.objects.create(
title='', # Widget handles its own title
url_name='djgentelella.notification.widgets.NotificationMenu',
category='main',
is_reversed=False,
reversed_args=reverse('notifications'), # API URL for widget
is_widget=True,
icon='fa fa-bell'
)
With Permissions¶
Restrict menu visibility based on Django permissions:
from django.contrib.auth.models import Permission
perm = Permission.objects.get(codename='can_manage_users')
item = MenuItem.objects.create(
title='User Management',
url_name='user_list',
category='sidebar',
is_reversed=True,
icon='fa fa-users'
)
item.permission.add(perm) # Only visible to users with this permission
Sidebar Footer Items¶
Create icon-only items for the sidebar footer:
MenuItem.objects.create(
title='Logout',
url_name='/accounts/logout/',
category='sidebarfooter',
is_reversed=False,
icon='fa fa-power-off',
only_icon=True
)
Responsive behaviour¶
The breakpoint is 992px, and it is the same one in gentelella/css/sidebar.css
and in custom.js (a single matchMedia), so the stylesheet and the script
can never disagree about which layout is in force.
At 992px and above the sidebar is a fixed, full-height column. The menu
itself scrolls inside it, and the sidebar-footer icons sit in a wrapping row at
the bottom of that column – in flow, so they can never cover a menu entry nor
grow wider than the sidebar. #menu_toggle collapses the column to the 70px
icon rail; a submenu then opens as a flyout beside the entry it belongs to,
positioned by positionRailFlyout in custom.js (it has to be
position: fixed, because the scrolling menu would clip an absolutely
positioned child to 70px).
Below 992px the sidebar becomes an off-canvas drawer. #menu_toggle
slides it over the page at its full width with a backdrop – labels, chevrons
and all three levels intact – and clicking the backdrop, pressing Escape or
following a leaf link closes it again. The body stays in nav-md throughout;
only body.sidebar-open is toggled. The page behind does not reflow, because
.right_col and .top_nav are already full width at this size.
In the top navigation bar, a nested level opens on click as well as on hover, so
it is reachable on a touch screen. Above the breakpoint it unfolds to the left
of its parent (the bar is right-aligned, so opening rightwards would leave the
window); below it, nested levels unfold as an indented block inside the parent
menu, which always fits, and custom.js caps the menu’s height to the room
left below it so a short window can still scroll to the last entry.
Everything above is covered by demoapp/tests/selenium/test_menu.py, which
sweeps eight viewport sizes and asserts of every entry that it is on screen and
is the topmost element at its own centre – that is, genuinely clickable rather
than merely present in the DOM.
If you ship your own theme through the site_theme setting, note that
sidebar.css is linked after it precisely so these rules survive the
change; site_extracss is linked after both and can still override them.
Using Django Admin¶
Menu items can also be managed through the Django admin interface. The MenuItem model is registered with the admin and supports:
Creating and editing menu items
Setting parent-child relationships
Assigning permissions
Ordering items by position
Management Commands¶
The demo application includes commands for creating sample menus:
python manage.py createdemoCreates a full demo menu structure with all widget examples, blog, dashboard, and various form widgets.
python manage.py demomenuCreates a simple menu structure for basic testing.
These commands can serve as reference for creating your own menu structures programmatically.
Complete Example¶
Here’s a complete example creating a typical application menu:
from django.urls import reverse
from djgentelella.models import MenuItem
def create_application_menu():
# Clear existing menu
MenuItem.objects.all().delete()
# Home section
home = MenuItem.objects.create(
title='Home',
url_name='/',
category='sidebar',
is_reversed=False,
icon='fa fa-home',
position=0
)
# Dashboard under Home
MenuItem.objects.create(
parent=home,
title='Dashboard',
url_name='dashboard',
category='sidebar',
is_reversed=True,
icon='fa fa-tachometer',
position=0
)
# Reports section
reports = MenuItem.objects.create(
title='Reports',
url_name='#',
category='sidebar',
is_reversed=False,
icon='fa fa-bar-chart',
position=1
)
MenuItem.objects.create(
parent=reports,
title='Sales Report',
url_name='report_sales',
category='sidebar',
is_reversed=True,
icon='fa fa-line-chart',
position=0
)
MenuItem.objects.create(
parent=reports,
title='User Report',
url_name='report_users',
category='sidebar',
is_reversed=True,
icon='fa fa-users',
position=1
)
# Footer items
MenuItem.objects.create(
title='Settings',
url_name='settings',
category='sidebarfooter',
is_reversed=True,
icon='fa fa-cog',
only_icon=True,
position=0
)
MenuItem.objects.create(
title='Logout',
url_name='/accounts/logout/',
category='sidebarfooter',
is_reversed=False,
icon='fa fa-power-off',
only_icon=True,
position=1
)
# Notification widget in top bar
MenuItem.objects.create(
title='Notifications',
url_name='djgentelella.notification.widgets.NotificationMenu',
category='main',
is_reversed=False,
reversed_args=reverse('notifications'),
is_widget=True,
icon='fa fa-bell',
position=0
)