-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Description
Proposed Changes
I'd like a to adopt a simpler mechanism than what currently exists in the 3351-plugins branch for plugins to register navigation menu items. We have a few options, which I'll outline here.
Current approach: Signals
The current approach requires three components. First, we declare the menu items in a file:
navigation.py
class NavLink1(PluginNavMenuLink):
link = 'plugins:my_plugin:view1'
link_text = 'Link A'
class NavLink2(PluginNavMenuLink):
link = 'plugins:my_plugin:view2'
link_text = 'Link B'Next, we create a function to return these classes, and register it as a signal receiver:
signals.py
@receiver(register_nav_menu_link_classes)
def nav_menu_link_classes(**kwargs):
return [NavLink1, NavLink2]Finally, we extend our PluginApp to import this function on ready(). (We could probably build this part into the PluginApp class.)
init.py
class MyPluginConfig(PluginConfig):
...
def ready(self):
from . import signalsProposal: Import of a known variable
The first option I propose is to direct plugin authors to define a single variable within a standard file, i.e. navigation.py, which contains all navigation menu items for their plugin. (This is very similar in concept to the way a plugin currently declares URL patterns in urls.py.)
navigation.py
class NavLink1(PluginNavMenuLink):
link = 'plugins:my_plugin:view1'
link_text = 'Link A'
class NavLink2(PluginNavMenuLink):
link = 'plugins:my_plugin:view2'
link_text = 'Link B'
menu_items = (NavLink1, NavLink2)NetBox would automatically import and register the classes in menu_items (if defined by the plugin). This could be handled by the PluginConfig's ready() method, like how we're currently handling signals.
With a minor tweak to the PluginNavMenuLink class to accept attributes on initialization, the above example could be further simplified to skip naming the individual instances:
navigation.py
menu_items = (
PluginNavMenuLink(
link='plugins:my_plugin:view1',
link_text='Link A'
),
PluginNavMenuLink(
link='plugins:my_plugin:view2',
link_text='Link B'
),
)Justification
I prefer this approach for four reasons:
- It minimizes the amount of code needed from the plugin author.
- It is much easier to understand what's happening, especially for someone unfamiliar with Django signals.
- It provides the simplest possible integration point: A single variable.
- This approach is already well-established by Django's URL patterns mechanism (wherein each app provides a
urlpatternsvariable).