Skip to content

Instantly share code, notes, and snippets.

@sujeetkv
Last active July 19, 2019 11:27
Show Gist options
  • Save sujeetkv/b36a1be5776a6c6229229a9faf9453e8 to your computer and use it in GitHub Desktop.
Save sujeetkv/b36a1be5776a6c6229229a9faf9453e8 to your computer and use it in GitHub Desktop.
Generate menu dynamically for flask application
from collections import OrderedDict
from flask import g
class FlaskMenu(object):
"""Generate menu for flask application.
Example::
# create object
app_menu = FlaskMenu(children=[
dict(
menu_id='dashboard',
title='Dashboard',
icon='dashboard',
endpoint='main.index',
is_visible=lambda menu_instance:True,
),
dict(
menu_id='order_management',
title='Order Management',
icon='list',
is_visible=lambda menu_instance:True,
children=[
dict(
menu_id='order_list',
title='Orders',
endpoint='orders.index',
),
dict(
menu_id='order_create',
title='Create Order',
endpoint='orders.create',
is_visible=lambda menu_instance:False,
),
],
),
])
# load jinja utility extension
app.jinja_env.add_extension('jinja2.ext.do')
# make this object globally available in template context
@app.context_processor
def process_template_context():
return dict(app_menu=app_menu)
# render menu in template
{% for item in app_menu.children recursive %}
<li {% if item.is_active() or item.has_active_child() %} class="active"{% endif %}>
{% if item.children %}
{% if item.is_visible() and item.has_visible_child() %}
<a href="{% if item.endpoint %}{{ url_for(item.endpoint) }}{% else %}javascript:;{% endif %}" data-target="#collapse-{{ item.menu_id }}">
{% if item.icon %}<i class="fa fa-fw fa-{{ item.icon }}"></i> {% endif %}{{ item.title }}
</a>
<ul id="collapse-{{ item.menu_id }}">
{{ loop(item.children) }}
</ul>
{% endif %}
{% else %}
{% if item.is_visible() %}
<a href="{% if item.endpoint %}{{ url_for(item.endpoint) }}{% else %}javascript:;{% endif %}"{% if item.is_active() %} class="active"{% endif %}>
{% if item.icon %}<i class="fa fa-fw fa-{{ item.icon }}"></i> {% endif %}{{ item.title }}
</a>
{% endif %}
{% endif %}
</li>
{% endfor %}
# set active menu in current template outside any block
{% do app_menu.set_active('order_list') %}
"""
def __init__(self, menu_id=None, title=None, icon=None, endpoint=None, is_visible=None, children=None):
self.menu_id = menu_id
self.title = title
self.icon = icon
self.endpoint = endpoint
self.is_visible_callback = is_visible or (lambda menu_instance:True)
self.child_entries = OrderedDict()
if children and isinstance(children, (list, tuple)):
for child in children:
if isinstance(child, dict) and child.get('menu_id') and child.get('title'):
self.child_entries[child.get('menu_id')] = self.__class__(
menu_id=child.get('menu_id'),
title=child.get('title'),
icon=child.get('icon'),
endpoint=child.get('endpoint'),
is_visible=child.get('is_visible'),
children=child.get('children'),
)
@property
def children(self):
return self.child_entries.values()
@staticmethod
def set_active(menu_id):
setattr(g, 'menu_id', menu_id)
@staticmethod
def get_active():
return getattr(g, 'menu_id', None)
def is_active(self):
return self.menu_id == self.get_active()
def has_active_child(self, recursive=True):
for child in self.child_entries.values():
if child.is_active():
return True
if recursive:
for child in self.child_entries.values():
if child.has_active_child(recursive=recursive):
return True
return False
def is_visible(self):
return self.is_visible_callback(self)
def has_visible_child(self, recursive=True):
for child in self.child_entries.values():
if child.is_visible():
return True
if recursive:
for child in self.child_entries.values():
if child.has_visible_child(recursive=recursive):
return True
return False
from flask_menu import FlaskMenu
import check_menu # any function which returns boolean to set menu visibility of particular menu item
# create object
app_menu = FlaskMenu(children=[
dict(
menu_id='dashboard',
title='Dashboard',
icon='dashboard',
endpoint='main.index',
is_visible=lambda menu_instance:check_menu(menu_instance),
),
dict(
menu_id='order_management',
title='Order Management',
icon='list',
is_visible=lambda menu_instance:check_menu(menu_instance),
children=[
dict(
menu_id='order_list',
title='Orders',
endpoint='orders.index',
),
dict(
menu_id='order_create',
title='Create Order',
endpoint='orders.create',
is_visible=lambda menu_instance:check_menu(menu_instance),
),
],
),
])
# load jinja utility extension
app.jinja_env.add_extension('jinja2.ext.do')
# make this object globally available in template context
@app.context_processor
def process_template_context():
return dict(app_menu=app_menu)
<!-- render menu in template -->
{% for item in app_menu.children recursive %}
<li {% if item.is_active() or item.has_active_child() %} class="active"{% endif %}>
{% if item.children %}
{% if item.is_visible() and item.has_visible_child() %}
<a href="{% if item.endpoint %}{{ url_for(item.endpoint) }}{% else %}javascript:;{% endif %}" data-target="#collapse-{{ item.menu_id }}">
{% if item.icon %}<i class="fa fa-fw fa-{{ item.icon }}"></i> {% endif %}{{ item.title }}
</a>
<ul id="collapse-{{ item.menu_id }}">
{{ loop(item.children) }}
</ul>
{% endif %}
{% else %}
{% if item.is_visible() %}
<a href="{% if item.endpoint %}{{ url_for(item.endpoint) }}{% else %}javascript:;{% endif %}"{% if item.is_active() %} class="active"{% endif %}>
{% if item.icon %}<i class="fa fa-fw fa-{{ item.icon }}"></i> {% endif %}{{ item.title }}
</a>
{% endif %}
{% endif %}
</li>
{% endfor %}
<!-- set active menu in current template outside any block -->
{% do app_menu.set_active('order_list') %}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment