.. _cookbook-main-menu:
Main menu
---------
How do I control which menu items are shown for a user?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. uses M.include
Using `include` you can control which menu items are shown for a given user. This also controls access, so you can know that your menu and your access control are always in sync.
.. code-block:: python
menu = MainMenu(
items=dict(
albums=M(
view=albums_view,
),
artists=M(
view=artists_view,
include=lambda user, **_: user.is_staff,
),
),
)
.. raw:: html
▼ Hide result
Toggle structure
.. raw:: html
▼ Hide result
Toggle structure
How do I control the display name of a menu item?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. uses M.display_name
By default the display name is derived from the `name` of the `M` object. So given:
.. code-block:: python
menu = MainMenu(
items=dict(
albums=M(view=albums_view),
),
)
.. raw:: html
▼ Hide result
Toggle structure
The `name` would be "albums", and the display name is automatically derived as "Albums". The translation from `name` to `display_name` replaces `_` with space, runs `gettext_lazy()` on the result, and then capitalizes that.
If you want to do something else, pass the `display_name` parameter:
.. code-block:: python
menu = MainMenu(
items=dict(
albums=M(
view=albums_view,
display_name=gettext_lazy('Discography'),
),
),
)
.. raw:: html
▼ Hide result
Toggle structure
Note that `display_name` can be a function too.
How do I add sub-paths for a menu item?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. uses M.paths
Since the menu system can control access, it is useful to nest path mappings under a specific menu item without showing them in the menu. This is done with the `paths` argument:
.. code-block:: python
menu = MainMenu(
items=dict(
albums=M(
view=albums_view,
paths=[
path('/edit/', edit_album_view),
],
),
),
)
.. raw:: html
► Show result
Toggle structure
How do I add external links in the menu?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. uses M.url
.. uses EXTERNAL
Use the special value `EXTERNAL` for the `view` argument, and use `url` argument:
.. code-block:: python
menu = MainMenu(
items=dict(
albums=M(
view=EXTERNAL,
url='https://docs.iommi.rocks',
),
),
)
.. raw:: html
▼ Hide result
Toggle structure
Note the icon added by default for an external link. This is configurable via the `icon_formatter` on your `Style`.
How do I nest menu items?
~~~~~~~~~~~~~~~~~~~~~~~~~
.. uses M.items
.. uses M.open
You can create a menu hierarchy with the `items` argument, which produces expandable sections. iommi will open the menu items that match the current URL by default. You can also force a submenu to be open with the `open` argument:
.. code-block:: python
menu = MainMenu(
items=dict(
things=M(
view=things_view,
open=True, # force open
items=dict(
albums=M(
view=albums_view,
),
artists=M(
view=artists_view,
)
),
),
),
)
.. raw:: html
▼ Hide result
Toggle structure
The `open` argument can be a callable.
How do I put arbitrary html in the menu?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. uses M.template
With the `template` argument you can put arbitrary html into menu items:
.. code-block:: python
menu = MainMenu(
items=dict(
albums=M(
view=EXTERNAL,
template=Template('''
''')
),
),
)
.. raw:: html
▼ Hide result
Toggle structure
Note that you want to include the `` tag.
You can also override the base template via your `Style`.
How do I show which specific object I am on in the menu?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. uses M.path
.. uses M.url
.. uses M.params
If you have a list of objects and you click into one of them, you might want to show that item in the menu, and potentially also sub-pages for that item. You do that by using iommi path decoders, and mapping everything together with `display_name`, `path`, `params` and `url`:
.. code-block:: python
menu = MainMenu(
items=dict(
albums=M(
view=albums_view,
items=dict(
album=M(
view=album_view,
display_name=lambda album, **_: str(album),
path='/',
params={'album'},
url=lambda album, **_: f'/albums/{album.pk}/',
),
)
),
),
)
.. raw:: html
▼ Hide result
Toggle structure
How do I make a data driven dynamic submenu?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. uses M.items
There can be cases where you want a dynamic submenu where the items come from the database. To do this, specify `items` as a callable. Note that urls here can't be mapped to paths, as paths need to be known at startup.
.. code-block:: python
menu = MainMenu(
items=dict(
albums=M(
view=albums_view,
items=lambda **_: {
f'album_{album.pk}': M(
view=EXTERNAL,
display_name=str(album),
url=album.get_absolute_url(),
)
for album in Album.objects.all()
}
)
),
)
.. raw:: html
▼ Hide result
Toggle structure
How do I use the menu for access control and path mapping without rendering into the menu?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. uses M.render
The access control and path mapping of `M` items is very handy, but sometimes you don't want
to show some URL in the sidebar menu. Use `render` to accomplish this:
.. code-block:: python
menu = MainMenu(
items=dict(
albums=M(
view=albums_view,
render=False,
)
),
)