mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
* Closes #14173: Enable plugins to register columns on core tables * Support translation for column name * Document new registry store
This commit is contained in:
@ -53,6 +53,10 @@ This store maintains all registered items for plugins, such as navigation menus,
|
|||||||
|
|
||||||
A dictionary mapping each model (identified by its app and label) to its search index class, if one has been registered for it.
|
A dictionary mapping each model (identified by its app and label) to its search index class, if one has been registered for it.
|
||||||
|
|
||||||
|
### `tables`
|
||||||
|
|
||||||
|
A dictionary mapping table classes to lists of extra columns that have been registered by plugins using the `register_table_column()` utility function. Each column is defined as a tuple of name and column instance.
|
||||||
|
|
||||||
### `views`
|
### `views`
|
||||||
|
|
||||||
A hierarchical mapping of registered views for each model. Mappings are added using the `register_model_view()` decorator, and URLs paths can be generated from these using `get_model_urls()`.
|
A hierarchical mapping of registered views for each model. Mappings are added using the `register_model_view()` decorator, and URLs paths can be generated from these using `get_model_urls()`.
|
||||||
|
@ -87,3 +87,28 @@ The table column classes listed below are supported for use in plugins. These cl
|
|||||||
options:
|
options:
|
||||||
members:
|
members:
|
||||||
- __init__
|
- __init__
|
||||||
|
|
||||||
|
## Extending Core Tables
|
||||||
|
|
||||||
|
!!! info "This feature was introduced in NetBox v3.7."
|
||||||
|
|
||||||
|
Plugins can register their own custom columns on core tables using the `register_table_column()` utility function. This allows a plugin to attach additional information, such as relationships to its own models, to built-in object lists.
|
||||||
|
|
||||||
|
```python
|
||||||
|
import django_tables2
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from dcim.tables import SiteTable
|
||||||
|
from utilities.tables import register_table_column
|
||||||
|
|
||||||
|
mycol = django_tables2.Column(
|
||||||
|
verbose_name=_('My Column'),
|
||||||
|
accessor=django_tables2.A('description')
|
||||||
|
)
|
||||||
|
|
||||||
|
register_table_column(mycol, 'foo', SiteTable)
|
||||||
|
```
|
||||||
|
|
||||||
|
You'll typically want to define an accessor identifying the desired model field or relationship when defining a custom column. See the [django-tables2 documentation](https://django-tables2.readthedocs.io/) for more information on creating custom columns.
|
||||||
|
|
||||||
|
::: utilities.tables.register_table_column
|
||||||
|
@ -28,6 +28,7 @@ registry = Registry({
|
|||||||
'models': collections.defaultdict(set),
|
'models': collections.defaultdict(set),
|
||||||
'plugins': dict(),
|
'plugins': dict(),
|
||||||
'search': dict(),
|
'search': dict(),
|
||||||
|
'tables': collections.defaultdict(dict),
|
||||||
'views': collections.defaultdict(dict),
|
'views': collections.defaultdict(dict),
|
||||||
'widgets': dict(),
|
'widgets': dict(),
|
||||||
})
|
})
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
from copy import deepcopy
|
||||||
|
|
||||||
import django_tables2 as tables
|
import django_tables2 as tables
|
||||||
from django.contrib.auth.models import AnonymousUser
|
from django.contrib.auth.models import AnonymousUser
|
||||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||||
@ -12,6 +14,7 @@ from django_tables2.data import TableQuerysetData
|
|||||||
|
|
||||||
from extras.models import CustomField, CustomLink
|
from extras.models import CustomField, CustomLink
|
||||||
from extras.choices import CustomFieldVisibilityChoices
|
from extras.choices import CustomFieldVisibilityChoices
|
||||||
|
from netbox.registry import registry
|
||||||
from netbox.tables import columns
|
from netbox.tables import columns
|
||||||
from utilities.paginator import EnhancedPaginator, get_paginate_count
|
from utilities.paginator import EnhancedPaginator, get_paginate_count
|
||||||
from utilities.utils import get_viewname, highlight_string, title
|
from utilities.utils import get_viewname, highlight_string, title
|
||||||
@ -191,12 +194,17 @@ class NetBoxTable(BaseTable):
|
|||||||
if extra_columns is None:
|
if extra_columns is None:
|
||||||
extra_columns = []
|
extra_columns = []
|
||||||
|
|
||||||
|
if registered_columns := registry['tables'].get(self.__class__):
|
||||||
|
extra_columns.extend([
|
||||||
|
# Create a copy to avoid modifying the original Column
|
||||||
|
(name, deepcopy(column)) for name, column in registered_columns.items()
|
||||||
|
])
|
||||||
|
|
||||||
# Add custom field & custom link columns
|
# Add custom field & custom link columns
|
||||||
content_type = ContentType.objects.get_for_model(self._meta.model)
|
content_type = ContentType.objects.get_for_model(self._meta.model)
|
||||||
custom_fields = CustomField.objects.filter(
|
custom_fields = CustomField.objects.filter(
|
||||||
content_types=content_type
|
content_types=content_type
|
||||||
).exclude(ui_visibility=CustomFieldVisibilityChoices.VISIBILITY_HIDDEN)
|
).exclude(ui_visibility=CustomFieldVisibilityChoices.VISIBILITY_HIDDEN)
|
||||||
|
|
||||||
extra_columns.extend([
|
extra_columns.extend([
|
||||||
(f'cf_{cf.name}', columns.CustomFieldColumn(cf)) for cf in custom_fields
|
(f'cf_{cf.name}', columns.CustomFieldColumn(cf)) for cf in custom_fields
|
||||||
])
|
])
|
||||||
|
11
netbox/netbox/tests/dummy_plugin/tables.py
Normal file
11
netbox/netbox/tests/dummy_plugin/tables.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import django_tables2 as tables
|
||||||
|
|
||||||
|
from dcim.tables import SiteTable
|
||||||
|
from utilities.tables import register_table_column
|
||||||
|
|
||||||
|
mycol = tables.Column(
|
||||||
|
verbose_name='My column',
|
||||||
|
accessor=tables.A('description')
|
||||||
|
)
|
||||||
|
|
||||||
|
register_table_column(mycol, 'foo', SiteTable)
|
@ -4,6 +4,8 @@ from django.views.generic import View
|
|||||||
from dcim.models import Site
|
from dcim.models import Site
|
||||||
from utilities.views import register_model_view
|
from utilities.views import register_model_view
|
||||||
from .models import DummyModel
|
from .models import DummyModel
|
||||||
|
# Trigger registration of custom column
|
||||||
|
from .tables import mycol
|
||||||
|
|
||||||
|
|
||||||
class DummyModelsView(View):
|
class DummyModelsView(View):
|
||||||
|
@ -97,6 +97,16 @@ class PluginTest(TestCase):
|
|||||||
|
|
||||||
self.assertIn(SiteContent, registry['plugins']['template_extensions']['dcim.site'])
|
self.assertIn(SiteContent, registry['plugins']['template_extensions']['dcim.site'])
|
||||||
|
|
||||||
|
def test_registered_columns(self):
|
||||||
|
"""
|
||||||
|
Check that a plugin can register a custom column on a core model table.
|
||||||
|
"""
|
||||||
|
from dcim.models import Site
|
||||||
|
from dcim.tables import SiteTable
|
||||||
|
|
||||||
|
table = SiteTable(Site.objects.all())
|
||||||
|
self.assertIn('foo', table.columns.names())
|
||||||
|
|
||||||
def test_user_preferences(self):
|
def test_user_preferences(self):
|
||||||
"""
|
"""
|
||||||
Check that plugin UserPreferences are registered.
|
Check that plugin UserPreferences are registered.
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
|
from netbox.registry import registry
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'get_table_ordering',
|
'get_table_ordering',
|
||||||
'linkify_phone',
|
'linkify_phone',
|
||||||
|
'register_table_column'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -26,3 +29,19 @@ def linkify_phone(value):
|
|||||||
if value is None:
|
if value is None:
|
||||||
return None
|
return None
|
||||||
return f"tel:{value}"
|
return f"tel:{value}"
|
||||||
|
|
||||||
|
|
||||||
|
def register_table_column(column, name, *tables):
|
||||||
|
"""
|
||||||
|
Register a custom column for use on one or more tables.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
column: The column instance to register
|
||||||
|
name: The name of the table column
|
||||||
|
tables: One or more table classes
|
||||||
|
"""
|
||||||
|
for table in tables:
|
||||||
|
reg = registry['tables'][table]
|
||||||
|
if name in reg:
|
||||||
|
raise ValueError(f"A column named {name} is already defined for table {table.__name__}")
|
||||||
|
reg[name] = column
|
||||||
|
Reference in New Issue
Block a user