1
0
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 (#14265)

* Closes #14173: Enable plugins to register columns on core tables

* Support translation for column name

* Document new registry store
This commit is contained in:
Jeremy Stretch
2023-11-16 12:16:35 -05:00
committed by GitHub
parent e15647a2ce
commit e767fec5cc
8 changed files with 81 additions and 1 deletions

View File

@ -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()`.

View File

@ -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

View File

@ -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(),
}) })

View File

@ -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
]) ])

View 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)

View File

@ -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):

View File

@ -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.

View File

@ -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