mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-14 09:51:22 -06:00
Docs and test for #9072
This commit is contained in:
parent
1fc8de85a3
commit
053c97b7a8
@ -148,6 +148,32 @@ These views are provided to enable or enhance certain NetBox model features, suc
|
|||||||
|
|
||||||
## Extending Core Views
|
## Extending Core Views
|
||||||
|
|
||||||
|
### Additional Tabs
|
||||||
|
|
||||||
|
Plugins can "attach" a custom view to a core NetBox model by registering it with `register_model_view()`. To include a tab for this view within the NetBox UI, declare a TabView instance named `tab`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from dcim.models import Site
|
||||||
|
from myplugin.models import Stuff
|
||||||
|
from netbox.views import generic
|
||||||
|
from utilities.views import ViewTab, register_model_view
|
||||||
|
|
||||||
|
@register_model_view(Site, 'mview', path='some-other-stuff')
|
||||||
|
class MyView(generic.ObjectView):
|
||||||
|
...
|
||||||
|
tab = ViewTab(
|
||||||
|
label='Other Stuff',
|
||||||
|
badge=lambda obj: Stuff.objects.filter(site=obj).count(),
|
||||||
|
permission='myplugin.view_stuff'
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
::: utilities.views.register_model_view
|
||||||
|
|
||||||
|
::: utilities.views.ViewTab
|
||||||
|
|
||||||
|
### Extra Template Content
|
||||||
|
|
||||||
Plugins can inject custom content into certain areas of the detail views of applicable models. This is accomplished by subclassing `PluginTemplateExtension`, designating a particular NetBox model, and defining the desired methods to render custom content. Four methods are available:
|
Plugins can inject custom content into certain areas of the detail views of applicable models. This is accomplished by subclassing `PluginTemplateExtension`, designating a particular NetBox model, and defining the desired methods to render custom content. Four methods are available:
|
||||||
|
|
||||||
* `left_page()` - Inject content on the left side of the page
|
* `left_page()` - Inject content on the left side of the page
|
||||||
|
@ -26,6 +26,7 @@ A new `PluginMenu` class has been introduced, which enables a plugin to inject a
|
|||||||
### Plugins API
|
### Plugins API
|
||||||
|
|
||||||
* [#9071](https://github.com/netbox-community/netbox/issues/9071) - Introduce `PluginMenu` for top-level plugin navigation menus
|
* [#9071](https://github.com/netbox-community/netbox/issues/9071) - Introduce `PluginMenu` for top-level plugin navigation menus
|
||||||
|
* [#9072](https://github.com/netbox-community/netbox/issues/9072) - Enable registration of tabbed plugin views for core NetBox models
|
||||||
* [#9880](https://github.com/netbox-community/netbox/issues/9880) - Introduce `django_apps` plugin configuration parameter
|
* [#9880](https://github.com/netbox-community/netbox/issues/9880) - Introduce `django_apps` plugin configuration parameter
|
||||||
* [#10314](https://github.com/netbox-community/netbox/issues/10314) - Move `clone()` method from NetBoxModel to CloningMixin
|
* [#10314](https://github.com/netbox-community/netbox/issues/10314) - Move `clone()` method from NetBoxModel to CloningMixin
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.views.generic import View
|
from django.views.generic import View
|
||||||
|
|
||||||
|
from dcim.models import Site
|
||||||
|
from utilities.views import register_model_view
|
||||||
from .models import DummyModel
|
from .models import DummyModel
|
||||||
|
|
||||||
|
|
||||||
@ -9,3 +11,10 @@ class DummyModelsView(View):
|
|||||||
def get(self, request):
|
def get(self, request):
|
||||||
instance_count = DummyModel.objects.count()
|
instance_count = DummyModel.objects.count()
|
||||||
return HttpResponse(f"Instances: {instance_count}")
|
return HttpResponse(f"Instances: {instance_count}")
|
||||||
|
|
||||||
|
|
||||||
|
@register_model_view(Site, 'extra', path='other-stuff')
|
||||||
|
class ExtraCoreModelView(View):
|
||||||
|
|
||||||
|
def get(self, request, pk):
|
||||||
|
return HttpResponse("Success!")
|
||||||
|
@ -59,6 +59,17 @@ class PluginTest(TestCase):
|
|||||||
response = client.get(url)
|
response = client.get(url)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_registered_views(self):
|
||||||
|
|
||||||
|
# Test URL resolution
|
||||||
|
url = reverse('dcim:site_extra', kwargs={'pk': 1})
|
||||||
|
self.assertEqual(url, '/dcim/sites/1/other-stuff/')
|
||||||
|
|
||||||
|
# Test GET request
|
||||||
|
client = Client()
|
||||||
|
response = client.get(url)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
def test_menu(self):
|
def test_menu(self):
|
||||||
"""
|
"""
|
||||||
Check menu registration.
|
Check menu registration.
|
||||||
|
@ -14,7 +14,6 @@ class BaseObjectView(ObjectPermissionRequiredMixin, View):
|
|||||||
"""
|
"""
|
||||||
queryset = None
|
queryset = None
|
||||||
template_name = None
|
template_name = None
|
||||||
tab = None
|
|
||||||
|
|
||||||
def get_object(self, **kwargs):
|
def get_object(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -37,7 +37,12 @@ class ObjectView(BaseObjectView):
|
|||||||
Retrieve a single object for display.
|
Retrieve a single object for display.
|
||||||
|
|
||||||
Note: If `template_name` is not specified, it will be determined automatically based on the queryset model.
|
Note: If `template_name` is not specified, it will be determined automatically based on the queryset model.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
tab: A ViewTab instance for the view
|
||||||
"""
|
"""
|
||||||
|
tab = None
|
||||||
|
|
||||||
def get_required_permission(self):
|
def get_required_permission(self):
|
||||||
return get_permission_for_model(self.queryset.model, 'view')
|
return get_permission_for_model(self.queryset.model, 'view')
|
||||||
|
|
||||||
|
@ -137,6 +137,12 @@ class ViewTab:
|
|||||||
"""
|
"""
|
||||||
ViewTabs are used for navigation among multiple object-specific views, such as the changelog or journal for
|
ViewTabs are used for navigation among multiple object-specific views, such as the changelog or journal for
|
||||||
a particular object.
|
a particular object.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
label: Human-friendly text
|
||||||
|
badge: A static value or callable to display alongside the label (optional). If a callable is used, it must accept a single
|
||||||
|
argument representing the object being viewed.
|
||||||
|
permission: The permission required to display the tab (optional).
|
||||||
"""
|
"""
|
||||||
def __init__(self, label, badge=None, permission=None):
|
def __init__(self, label, badge=None, permission=None):
|
||||||
self.label = label
|
self.label = label
|
||||||
@ -178,7 +184,7 @@ def register_model_view(model, name, path=None, kwargs=None):
|
|||||||
name: The string used to form the view's name for URL resolution (e.g. via `reverse()`). This will be appended
|
name: The string used to form the view's name for URL resolution (e.g. via `reverse()`). This will be appended
|
||||||
to the name of the base view for the model using an underscore.
|
to the name of the base view for the model using an underscore.
|
||||||
path: The URL path by which the view can be reached (optional). If not provided, `name` will be used.
|
path: The URL path by which the view can be reached (optional). If not provided, `name` will be used.
|
||||||
kwargs: A dictionary of keyword arguments for the view to include when registering its URL path (optional)
|
kwargs: A dictionary of keyword arguments for the view to include when registering its URL path (optional).
|
||||||
"""
|
"""
|
||||||
def _wrapper(cls):
|
def _wrapper(cls):
|
||||||
app_label = model._meta.app_label
|
app_label = model._meta.app_label
|
||||||
|
Loading…
Reference in New Issue
Block a user