From d4569df30598c96f47c8661493906c1d35e9e5db Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 1 Apr 2026 10:29:49 -0400 Subject: [PATCH 1/7] Closes #21770: Enable including/excluding columns on ObjectsTablePanel --- netbox/netbox/ui/panels.py | 10 +++++++++- netbox/netbox/views/generic/mixins.py | 12 ++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/netbox/netbox/ui/panels.py b/netbox/netbox/ui/panels.py index f58e98449..7aaf0e874 100644 --- a/netbox/netbox/ui/panels.py +++ b/netbox/netbox/ui/panels.py @@ -282,11 +282,13 @@ class ObjectsTablePanel(Panel): model (str): The dotted label of the model to be added (e.g. "dcim.site") filters (dict): A dictionary of arbitrary URL parameters to append to the table's URL. If the value of a key is a callable, it will be passed the current template context. + include_columns (list): A list of column names to display exclusively (overrides user preferences) + exclude_columns (list): A list of column names to hide from the table (overrides user preferences) """ template_name = 'ui/panels/objects_table.html' title = None - def __init__(self, model, filters=None, **kwargs): + def __init__(self, model, filters=None, include_columns=None, exclude_columns=None, **kwargs): super().__init__(**kwargs) # Resolve the model class from its app.name label @@ -297,6 +299,8 @@ class ObjectsTablePanel(Panel): raise ValueError(f"Invalid model label: {model}") self.filters = filters or {} + self.include_columns = include_columns or [] + self.exclude_columns = exclude_columns or [] # If no title is specified, derive one from the model name if self.title is None: @@ -308,6 +312,10 @@ class ObjectsTablePanel(Panel): } if 'return_url' not in url_params and 'object' in context: url_params['return_url'] = context['object'].get_absolute_url() + if self.include_columns: + url_params['include_columns'] = ','.join(self.include_columns) + if self.exclude_columns: + url_params['exclude_columns'] = ','.join(self.exclude_columns) return { **super().get_context(context), 'viewname': get_viewname(self.model, 'list'), diff --git a/netbox/netbox/views/generic/mixins.py b/netbox/netbox/views/generic/mixins.py index 6e40e4175..031dddb2d 100644 --- a/netbox/netbox/views/generic/mixins.py +++ b/netbox/netbox/views/generic/mixins.py @@ -97,4 +97,16 @@ class TableMixin: table.columns.show('pk') table.configure(request) + # Apply column inclusion/exclusion (overrides user preferences) + if include_columns := request.GET.get('include_columns'): + include_columns = include_columns.split(',') + for column in table.columns: + if column.name not in table.exempt_columns and column.name not in include_columns: + table.columns.hide(column.name) + elif exclude_columns := request.GET.get('exclude_columns'): + exclude_columns = exclude_columns.split(',') + for column_name in exclude_columns: + if column_name in table.columns.names(): + table.columns.hide(column_name) + return table From 364868a207b51f225a43a47c649c7d345f049361 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 1 Apr 2026 10:54:34 -0400 Subject: [PATCH 2/7] Implement exclude_columns on embedded tables --- netbox/circuits/views.py | 5 +++++ netbox/core/views.py | 1 + netbox/dcim/views.py | 13 +++++++++++++ netbox/ipam/views.py | 2 ++ netbox/tenancy/views.py | 3 +++ netbox/users/views.py | 19 ++++++++++++++++--- netbox/virtualization/views.py | 4 ++++ netbox/vpn/views.py | 3 +++ netbox/wireless/views.py | 1 + 9 files changed, 48 insertions(+), 3 deletions(-) diff --git a/netbox/circuits/views.py b/netbox/circuits/views.py index ddb4ef358..d6dad2e46 100644 --- a/netbox/circuits/views.py +++ b/netbox/circuits/views.py @@ -53,6 +53,7 @@ class ProviderView(GetRelatedModelsMixin, generic.ObjectView): ObjectsTablePanel( model='circuits.ProviderAccount', filters={'provider_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['provider'], actions=[ actions.AddObject( 'circuits.ProviderAccount', url_params={'provider': lambda ctx: ctx['object'].pk} @@ -62,6 +63,7 @@ class ProviderView(GetRelatedModelsMixin, generic.ObjectView): ObjectsTablePanel( model='circuits.Circuit', filters={'provider_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['provider'], actions=[ actions.AddObject('circuits.Circuit', url_params={'provider': lambda ctx: ctx['object'].pk}), ], @@ -161,6 +163,7 @@ class ProviderAccountView(GetRelatedModelsMixin, generic.ObjectView): ObjectsTablePanel( model='circuits.Circuit', filters={'provider_account_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['provider_account'], actions=[ actions.AddObject( 'circuits.Circuit', @@ -257,6 +260,7 @@ class ProviderNetworkView(GetRelatedModelsMixin, generic.ObjectView): ObjectsTablePanel( model='circuits.VirtualCircuit', filters={'provider_network_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['provider_network'], actions=[ actions.AddObject( 'circuits.VirtualCircuit', url_params={'provider_network': lambda ctx: ctx['object'].pk} @@ -801,6 +805,7 @@ class VirtualCircuitView(generic.ObjectView): model='circuits.VirtualCircuitTermination', title=_('Terminations'), filters={'virtual_circuit_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['virtual_circuit'], actions=[ actions.AddObject( 'circuits.VirtualCircuitTermination', diff --git a/netbox/core/views.py b/netbox/core/views.py index 6531fb7b2..328679fa9 100644 --- a/netbox/core/views.py +++ b/netbox/core/views.py @@ -94,6 +94,7 @@ class DataSourceView(GetRelatedModelsMixin, generic.ObjectView): ObjectsTablePanel( model='core.DataFile', filters={'source_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['source'], ), ], ) diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index acf335b69..a28562385 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -258,6 +258,7 @@ class RegionView(GetRelatedModelsMixin, generic.ObjectView): model='dcim.Region', title=_('Child Regions'), filters={'parent_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['parent'], actions=[ actions.AddObject('dcim.Region', url_params={'parent': lambda ctx: ctx['object'].pk}), ], @@ -390,6 +391,7 @@ class SiteGroupView(GetRelatedModelsMixin, generic.ObjectView): model='dcim.SiteGroup', title=_('Child Groups'), filters={'parent_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['parent'], actions=[ actions.AddObject('dcim.SiteGroup', url_params={'parent': lambda ctx: ctx['object'].pk}), ], @@ -540,6 +542,7 @@ class SiteView(GetRelatedModelsMixin, generic.ObjectView): ObjectsTablePanel( model='dcim.Location', filters={'site_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['site'], actions=[ actions.AddObject('dcim.Location', url_params={'site': lambda ctx: ctx['object'].pk}), ], @@ -552,6 +555,7 @@ class SiteView(GetRelatedModelsMixin, generic.ObjectView): 'rack_id': settings.FILTERS_NULL_CHOICE_VALUE, 'parent_bay_id': settings.FILTERS_NULL_CHOICE_VALUE, }, + exclude_columns=['site'], actions=[ actions.AddObject('dcim.Device', url_params={'site': lambda ctx: ctx['object'].pk}), ], @@ -674,6 +678,7 @@ class LocationView(GetRelatedModelsMixin, generic.ObjectView): model='dcim.Location', title=_('Child Locations'), filters={'parent_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['parent'], actions=[ actions.AddObject( 'dcim.Location', @@ -692,6 +697,7 @@ class LocationView(GetRelatedModelsMixin, generic.ObjectView): 'rack_id': settings.FILTERS_NULL_CHOICE_VALUE, 'parent_bay_id': settings.FILTERS_NULL_CHOICE_VALUE, }, + exclude_columns=['location'], actions=[ actions.AddObject( 'dcim.Device', @@ -1686,6 +1692,7 @@ class ModuleTypeProfileView(generic.ObjectView): filters={ 'profile_id': lambda ctx: ctx['object'].pk, }, + exclude_columns=['profile'], actions=[ actions.AddObject( 'dcim.ModuleType', @@ -2427,6 +2434,7 @@ class DeviceRoleView(GetRelatedModelsMixin, generic.ObjectView): model='dcim.DeviceRole', title=_('Child Device Roles'), filters={'parent_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['parent'], actions=[ actions.AddObject('dcim.DeviceRole', url_params={'parent': lambda ctx: ctx['object'].pk}), ], @@ -2527,6 +2535,7 @@ class PlatformView(GetRelatedModelsMixin, generic.ObjectView): model='dcim.Platform', title=_('Child Platforms'), filters={'parent_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['parent'], actions=[ actions.AddObject('dcim.Platform', url_params={'parent': lambda ctx: ctx['object'].pk}), ], @@ -2605,6 +2614,7 @@ class DeviceView(generic.ObjectView): ObjectsTablePanel( model='dcim.VirtualDeviceContext', filters={'device_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['device'], actions=[ actions.AddObject('dcim.VirtualDeviceContext', url_params={'device': lambda ctx: ctx['object'].pk}), ], @@ -2617,6 +2627,7 @@ class DeviceView(generic.ObjectView): model='ipam.Service', title=_('Application Services'), filters={'device_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['device'], actions=[ actions.AddObject( 'ipam.Service', @@ -3376,11 +3387,13 @@ class InterfaceView(generic.ObjectView): model='ipam.IPAddress', filters={'interface_id': lambda ctx: ctx['object'].pk}, title=_('IP Addresses'), + exclude_columns=['assigned'], ), ObjectsTablePanel( model='dcim.MACAddress', filters={'interface_id': lambda ctx: ctx['object'].pk}, title=_('MAC Addresses'), + exclude_columns=['assigned_object', 'assigned_object_parent'], ), ObjectsTablePanel( model='ipam.VLAN', diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index 3f8f9eba2..4fc30342b 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -1331,6 +1331,7 @@ class VLANTranslationPolicyView(generic.ObjectView): 'ipam.vlantranslationrule', filters={'policy_id': lambda ctx: ctx['object'].pk}, title=_('VLAN translation rules'), + exclude_columns=['policy'], actions=[ actions.AddObject( 'ipam.vlantranslationrule', @@ -1628,6 +1629,7 @@ class VLANView(generic.ObjectView): 'ipam.prefix', filters={'vlan_id': lambda ctx: ctx['object'].pk}, title=_('Prefixes'), + exclude_columns=['vlan'], actions=[ actions.AddObject( 'ipam.prefix', diff --git a/netbox/tenancy/views.py b/netbox/tenancy/views.py index 26b0ac5ab..7ebc1ab82 100644 --- a/netbox/tenancy/views.py +++ b/netbox/tenancy/views.py @@ -57,6 +57,7 @@ class TenantGroupView(GetRelatedModelsMixin, generic.ObjectView): 'tenancy.tenantgroup', filters={'parent_id': lambda ctx: ctx['object'].pk}, title=_('Child Groups'), + exclude_columns=['parent'], actions=[ actions.AddObject( 'tenancy.tenantgroup', @@ -235,6 +236,7 @@ class ContactGroupView(GetRelatedModelsMixin, generic.ObjectView): 'tenancy.contactgroup', filters={'parent_id': lambda ctx: ctx['object'].pk}, title=_('Child Groups'), + exclude_columns=['parent'], actions=[ actions.AddObject( 'tenancy.contactgroup', @@ -414,6 +416,7 @@ class ContactView(generic.ObjectView): 'tenancy.contactassignment', filters={'contact_id': lambda ctx: ctx['object'].pk}, title=_('Assignments'), + exclude_columns=['contact'], ), ], ) diff --git a/netbox/users/views.py b/netbox/users/views.py index 59ebe518f..bf50919a0 100644 --- a/netbox/users/views.py +++ b/netbox/users/views.py @@ -200,7 +200,11 @@ class GroupView(generic.ObjectView): OrganizationalObjectPanel(), ], right_panels=[ - ObjectsTablePanel('users.User', filters={'group_id': lambda ctx: ctx['object'].pk}), + ObjectsTablePanel( + 'users.User', + filters={'group_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['group'], + ), ObjectsTablePanel( 'users.ObjectPermission', title=_('Assigned Permissions'), @@ -345,6 +349,7 @@ class OwnerGroupView(generic.ObjectView): 'users.Owner', filters={'group_id': lambda ctx: ctx['object'].pk}, title=_('Members'), + exclude_columns=['group'], actions=[ actions.AddObject( 'users.Owner', @@ -412,8 +417,16 @@ class OwnerView(GetRelatedModelsMixin, generic.ObjectView): layout = layout.SimpleLayout( left_panels=[ panels.OwnerPanel(), - ObjectsTablePanel('users.Group', filters={'owner_id': lambda ctx: ctx['object'].pk}), - ObjectsTablePanel('users.User', filters={'owner_id': lambda ctx: ctx['object'].pk}), + ObjectsTablePanel( + 'users.Group', + filters={'owner_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['owner'], + ), + ObjectsTablePanel( + 'users.User', + filters={'owner_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['owner'], + ), ], right_panels=[ RelatedObjectsPanel(), diff --git a/netbox/virtualization/views.py b/netbox/virtualization/views.py index 27cd0cb98..fbff67432 100644 --- a/netbox/virtualization/views.py +++ b/netbox/virtualization/views.py @@ -492,6 +492,7 @@ class VirtualMachineView(generic.ObjectView): model='ipam.Service', title=_('Application Services'), filters={'virtual_machine_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['parent'], actions=[ actions.AddObject( 'ipam.Service', @@ -508,6 +509,7 @@ class VirtualMachineView(generic.ObjectView): ObjectsTablePanel( model='virtualization.VirtualDisk', filters={'virtual_machine_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['virtual_machine'], actions=[ actions.AddObject( 'virtualization.VirtualDisk', url_params={'virtual_machine': lambda ctx: ctx['object'].pk} @@ -649,6 +651,7 @@ class VMInterfaceView(generic.ObjectView): ObjectsTablePanel( model='ipam.IPaddress', filters={'vminterface_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['assigned'], actions=[ actions.AddObject( 'ipam.IPaddress', @@ -662,6 +665,7 @@ class VMInterfaceView(generic.ObjectView): ObjectsTablePanel( model='dcim.MACAddress', filters={'vminterface_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['assigned_object', 'assigned_object_parent'], actions=[ actions.AddObject( 'dcim.MACAddress', url_params={'vminterface': lambda ctx: ctx['object'].pk} diff --git a/netbox/vpn/views.py b/netbox/vpn/views.py index 7da05e007..e74c53b64 100644 --- a/netbox/vpn/views.py +++ b/netbox/vpn/views.py @@ -129,6 +129,7 @@ class TunnelView(generic.ObjectView): ObjectsTablePanel( 'vpn.tunneltermination', filters={'tunnel_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['tunnel'], actions=[ actions.AddObject( 'vpn.tunneltermination', @@ -223,6 +224,7 @@ class TunnelTerminationView(generic.ObjectView): 'tunnel_id': lambda ctx: ctx['object'].tunnel.pk, 'id__n': lambda ctx: ctx['object'].pk, }, + exclude_columns=['tunnel'], title=_('Peer Terminations'), ), ], @@ -675,6 +677,7 @@ class L2VPNView(generic.ObjectView): ObjectsTablePanel( 'vpn.l2vpntermination', filters={'l2vpn_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['l2vpn'], actions=[ actions.AddObject( 'vpn.l2vpntermination', diff --git a/netbox/wireless/views.py b/netbox/wireless/views.py index fecec1ef9..422b46cc5 100644 --- a/netbox/wireless/views.py +++ b/netbox/wireless/views.py @@ -53,6 +53,7 @@ class WirelessLANGroupView(GetRelatedModelsMixin, generic.ObjectView): model='wireless.WirelessLANGroup', title=_('Child Groups'), filters={'parent_id': lambda ctx: ctx['object'].pk}, + exclude_columns=['parent'], actions=[ actions.AddObject( 'wireless.WirelessLANGroup', From bd35afe3201327627ce4863e3c7b412c9fa07b35 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 1 Apr 2026 14:14:13 -0400 Subject: [PATCH 3/7] Apply column hiding before prefetching --- netbox/netbox/tables/tables.py | 13 +++++++++++++ netbox/netbox/views/generic/mixins.py | 12 ------------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/netbox/netbox/tables/tables.py b/netbox/netbox/tables/tables.py index d016cbf28..9fd31e196 100644 --- a/netbox/netbox/tables/tables.py +++ b/netbox/netbox/tables/tables.py @@ -185,6 +185,19 @@ class BaseTable(tables.Table): columns = getattr(self.Meta, 'default_columns', self.Meta.fields) self._set_columns(columns) + + # Apply column inclusion/exclusion (overrides user preferences) + if include_columns := request.GET.get('include_columns'): + include_columns = include_columns.split(',') + for column in self.columns: + if column.name not in self.exempt_columns and column.name not in include_columns: + self.columns.hide(column.name) + elif exclude_columns := request.GET.get('exclude_columns'): + exclude_columns = exclude_columns.split(',') + for column_name in exclude_columns: + if column_name in self.columns.names(): + self.columns.hide(column_name) + self._apply_prefetching() if ordering is not None: self.order_by = ordering diff --git a/netbox/netbox/views/generic/mixins.py b/netbox/netbox/views/generic/mixins.py index 031dddb2d..6e40e4175 100644 --- a/netbox/netbox/views/generic/mixins.py +++ b/netbox/netbox/views/generic/mixins.py @@ -97,16 +97,4 @@ class TableMixin: table.columns.show('pk') table.configure(request) - # Apply column inclusion/exclusion (overrides user preferences) - if include_columns := request.GET.get('include_columns'): - include_columns = include_columns.split(',') - for column in table.columns: - if column.name not in table.exempt_columns and column.name not in include_columns: - table.columns.hide(column.name) - elif exclude_columns := request.GET.get('exclude_columns'): - exclude_columns = exclude_columns.split(',') - for column_name in exclude_columns: - if column_name in table.columns.names(): - table.columns.hide(column_name) - return table From a45ec6620a5f85a1b6ff9137aadf2546cc1487d7 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 1 Apr 2026 14:17:57 -0400 Subject: [PATCH 4/7] Protect exempt columns from exclusion --- netbox/netbox/tables/tables.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netbox/netbox/tables/tables.py b/netbox/netbox/tables/tables.py index 9fd31e196..e57b3642b 100644 --- a/netbox/netbox/tables/tables.py +++ b/netbox/netbox/tables/tables.py @@ -195,7 +195,7 @@ class BaseTable(tables.Table): elif exclude_columns := request.GET.get('exclude_columns'): exclude_columns = exclude_columns.split(',') for column_name in exclude_columns: - if column_name in self.columns.names(): + if column_name in self.columns.names() and column_name not in self.exempt_columns: self.columns.hide(column_name) self._apply_prefetching() From f06f8f3f1d2a554090c346d89d98122cc065e8f7 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 1 Apr 2026 14:25:31 -0400 Subject: [PATCH 5/7] Exclude assigned object columns from IP addresses table on interface views --- netbox/dcim/views.py | 2 +- netbox/virtualization/views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index a28562385..c7488891e 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -3387,7 +3387,7 @@ class InterfaceView(generic.ObjectView): model='ipam.IPAddress', filters={'interface_id': lambda ctx: ctx['object'].pk}, title=_('IP Addresses'), - exclude_columns=['assigned'], + exclude_columns=['assigned', 'assigned_object', 'assigned_object_parent'], ), ObjectsTablePanel( model='dcim.MACAddress', diff --git a/netbox/virtualization/views.py b/netbox/virtualization/views.py index fbff67432..d6a758dad 100644 --- a/netbox/virtualization/views.py +++ b/netbox/virtualization/views.py @@ -651,7 +651,7 @@ class VMInterfaceView(generic.ObjectView): ObjectsTablePanel( model='ipam.IPaddress', filters={'vminterface_id': lambda ctx: ctx['object'].pk}, - exclude_columns=['assigned'], + exclude_columns=['assigned', 'assigned_object', 'assigned_object_parent'], actions=[ actions.AddObject( 'ipam.IPaddress', From be1a29d7ee9b294500e38e736620bd4f0f887d02 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 1 Apr 2026 14:46:53 -0400 Subject: [PATCH 6/7] Misc cleanup --- netbox/dcim/views.py | 2 +- netbox/users/views.py | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index c7488891e..5ebb22c6b 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -2627,7 +2627,7 @@ class DeviceView(generic.ObjectView): model='ipam.Service', title=_('Application Services'), filters={'device_id': lambda ctx: ctx['object'].pk}, - exclude_columns=['device'], + exclude_columns=['parent'], actions=[ actions.AddObject( 'ipam.Service', diff --git a/netbox/users/views.py b/netbox/users/views.py index bf50919a0..928775101 100644 --- a/netbox/users/views.py +++ b/netbox/users/views.py @@ -203,7 +203,6 @@ class GroupView(generic.ObjectView): ObjectsTablePanel( 'users.User', filters={'group_id': lambda ctx: ctx['object'].pk}, - exclude_columns=['group'], ), ObjectsTablePanel( 'users.ObjectPermission', @@ -420,12 +419,10 @@ class OwnerView(GetRelatedModelsMixin, generic.ObjectView): ObjectsTablePanel( 'users.Group', filters={'owner_id': lambda ctx: ctx['object'].pk}, - exclude_columns=['owner'], ), ObjectsTablePanel( 'users.User', filters={'owner_id': lambda ctx: ctx['object'].pk}, - exclude_columns=['owner'], ), ], right_panels=[ From 6c08941542466d9bae25e82d3e0350a44ad18530 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 1 Apr 2026 14:58:41 -0400 Subject: [PATCH 7/7] Tweak behavior of include_columns --- netbox/netbox/tables/tables.py | 11 +++++------ netbox/netbox/ui/panels.py | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/netbox/netbox/tables/tables.py b/netbox/netbox/tables/tables.py index e57b3642b..931da4f77 100644 --- a/netbox/netbox/tables/tables.py +++ b/netbox/netbox/tables/tables.py @@ -187,12 +187,11 @@ class BaseTable(tables.Table): self._set_columns(columns) # Apply column inclusion/exclusion (overrides user preferences) - if include_columns := request.GET.get('include_columns'): - include_columns = include_columns.split(',') - for column in self.columns: - if column.name not in self.exempt_columns and column.name not in include_columns: - self.columns.hide(column.name) - elif exclude_columns := request.GET.get('exclude_columns'): + if columns_param := request.GET.get('include_columns'): + for column_name in columns_param.split(','): + if column_name in self.columns.names(): + self.columns.show(column_name) + if exclude_columns := request.GET.get('exclude_columns'): exclude_columns = exclude_columns.split(',') for column_name in exclude_columns: if column_name in self.columns.names() and column_name not in self.exempt_columns: diff --git a/netbox/netbox/ui/panels.py b/netbox/netbox/ui/panels.py index 7aaf0e874..fa26cf754 100644 --- a/netbox/netbox/ui/panels.py +++ b/netbox/netbox/ui/panels.py @@ -282,7 +282,7 @@ class ObjectsTablePanel(Panel): model (str): The dotted label of the model to be added (e.g. "dcim.site") filters (dict): A dictionary of arbitrary URL parameters to append to the table's URL. If the value of a key is a callable, it will be passed the current template context. - include_columns (list): A list of column names to display exclusively (overrides user preferences) + include_columns (list): A list of column names to always display (overrides user preferences) exclude_columns (list): A list of column names to hide from the table (overrides user preferences) """ template_name = 'ui/panels/objects_table.html'