diff --git a/netbox/netbox/views/generic/bulk_views.py b/netbox/netbox/views/generic/bulk_views.py index 539e257c0..5a5d060e9 100644 --- a/netbox/netbox/views/generic/bulk_views.py +++ b/netbox/netbox/views/generic/bulk_views.py @@ -7,7 +7,7 @@ from django.contrib.contenttypes.fields import GenericForeignKey, GenericRel from django.contrib.contenttypes.models import ContentType from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist, ValidationError from django.db import transaction, IntegrityError -from django.db.models import ManyToManyField, ProtectedError, Q, RestrictedError +from django.db.models import ManyToManyField, ProtectedError, RestrictedError from django.db.models.fields.reverse_related import ManyToManyRel from django.forms import ModelMultipleChoiceField, MultipleHiddenInput from django.http import HttpResponse @@ -21,7 +21,7 @@ from mptt.models import MPTTModel from core.models import ObjectType from core.signals import clear_events from extras.choices import CustomFieldUIEditableChoices -from extras.models import CustomField, ExportTemplate, TableConfig +from extras.models import CustomField, ExportTemplate from utilities.error_handlers import handle_protectederror from utilities.exceptions import AbortRequest, AbortTransaction, PermissionsViolation from utilities.forms import BulkRenameForm, ConfirmationForm, restrict_form_fields @@ -29,6 +29,7 @@ from utilities.forms.bulk_import import BulkImportForm from utilities.htmx import htmx_partial from utilities.permissions import get_permission_for_model from utilities.query import reapply_model_ordering +from utilities.tables import get_table_configs from utilities.views import GetReturnURLMixin, get_viewname from .base import BaseMultiObjectView from .mixins import ActionsMixin, TableMixin @@ -140,15 +141,6 @@ class ObjectListView(BaseMultiObjectView, ActionsMixin, TableMixin): model = self.queryset.model object_type = ObjectType.objects.get_for_model(model) - # If a TableConfig has been specified, apply it & update the user's saved preference - if tableconfig_id := request.GET.get('tableconfig_id'): - tableconfig = get_object_or_404(TableConfig, pk=tableconfig_id) - if request.user.is_authenticated: - table = self.table.__name__ - request.user.config.set(f'tables.{table}.columns', tableconfig.columns) - request.user.config.set(f'tables.{table}.ordering', tableconfig.ordering, commit=True) - return redirect(request.path) - if self.filterset: self.queryset = self.filterset(request.GET, self.queryset, request=request).qs @@ -184,14 +176,6 @@ class ObjectListView(BaseMultiObjectView, ActionsMixin, TableMixin): # Render the objects table table = self.get_table(self.queryset, request, has_bulk_actions) - # Retrieve available configurations for the table - table_configs = TableConfig.objects.filter( - Q(shared=True) | Q(user=request.user if request.user.is_authenticated else None), - object_type=object_type, - table=table.name, - enabled=True, - ) - # If this is an HTMX request, return only the rendered table HTML if htmx_partial(request): if request.GET.get('embedded', False): @@ -208,7 +192,7 @@ class ObjectListView(BaseMultiObjectView, ActionsMixin, TableMixin): context = { 'model': model, 'table': table, - 'table_configs': table_configs, + 'table_configs': get_table_configs(table, request.user), 'actions': actions, 'filter_form': self.filterset_form(request.GET) if self.filterset_form else None, 'prerequisite_model': get_prerequisite_model(self.queryset), diff --git a/netbox/netbox/views/generic/mixins.py b/netbox/netbox/views/generic/mixins.py index 25621b7bd..5f9f62120 100644 --- a/netbox/netbox/views/generic/mixins.py +++ b/netbox/netbox/views/generic/mixins.py @@ -1,3 +1,6 @@ +from django.shortcuts import get_object_or_404 + +from extras.models import TableConfig from netbox.constants import DEFAULT_ACTION_PERMISSIONS from utilities.permissions import get_permission_for_model @@ -47,6 +50,15 @@ class TableMixin: request: The current request bulk_actions: Render checkboxes for object selection """ + + # If a TableConfig has been specified, apply it & update the user's saved preference + if tableconfig_id := request.GET.get('tableconfig_id'): + tableconfig = get_object_or_404(TableConfig, pk=tableconfig_id) + if request.user.is_authenticated: + table = self.table.__name__ + request.user.config.set(f'tables.{table}.columns', tableconfig.columns) + request.user.config.set(f'tables.{table}.ordering', tableconfig.ordering, commit=True) + table = self.table(data, user=request.user) if 'pk' in table.base_columns and bulk_actions: table.columns.show('pk') diff --git a/netbox/netbox/views/generic/object_views.py b/netbox/netbox/views/generic/object_views.py index fb554ca4f..11038aa97 100644 --- a/netbox/netbox/views/generic/object_views.py +++ b/netbox/netbox/views/generic/object_views.py @@ -20,6 +20,7 @@ from utilities.forms import ConfirmationForm, restrict_form_fields from utilities.htmx import htmx_partial from utilities.permissions import get_permission_for_model from utilities.querydict import normalize_querydict, prepare_cloned_fields +from utilities.tables import get_table_configs from utilities.views import GetReturnURLMixin, get_viewname from .base import BaseObjectView from .mixins import ActionsMixin, TableMixin @@ -156,6 +157,7 @@ class ObjectChildrenView(ObjectView, ActionsMixin, TableMixin): 'base_template': f'{instance._meta.app_label}/{instance._meta.model_name}.html', 'table': table, 'table_config': f'{table.name}_config', + 'table_configs': get_table_configs(table, request.user), 'filter_form': self.filterset_form(request.GET) if self.filterset_form else None, 'actions': actions, 'tab': self.tab, diff --git a/netbox/project-static/dist/netbox.js b/netbox/project-static/dist/netbox.js index 5171e9dc5..d51c3ebcc 100644 Binary files a/netbox/project-static/dist/netbox.js and b/netbox/project-static/dist/netbox.js differ diff --git a/netbox/project-static/dist/netbox.js.map b/netbox/project-static/dist/netbox.js.map index 4a48c9cf8..1c17e5841 100644 Binary files a/netbox/project-static/dist/netbox.js.map and b/netbox/project-static/dist/netbox.js.map differ diff --git a/netbox/project-static/src/tableConfig.ts b/netbox/project-static/src/tableConfig.ts index f1dbedbae..e39dc3bfd 100644 --- a/netbox/project-static/src/tableConfig.ts +++ b/netbox/project-static/src/tableConfig.ts @@ -80,7 +80,8 @@ function handleSubmit(event: Event): void { const toast = createToast('danger', 'Error Resetting Table Configuration', res.error); toast.show(); } else { - location.reload(); + // Strip any URL query parameters & reload the page + window.location.href = window.location.origin + window.location.pathname; } }); return; diff --git a/netbox/utilities/tables.py b/netbox/utilities/tables.py index b90685c9f..50f46e8a0 100644 --- a/netbox/utilities/tables.py +++ b/netbox/utilities/tables.py @@ -1,9 +1,13 @@ +from django.apps import apps +from django.db.models import Q from django.utils.module_loading import import_string from django.utils.translation import gettext_lazy as _ +from core.models import ObjectType from netbox.registry import registry __all__ = ( + 'get_table_configs', 'get_table_for_model', 'get_table_ordering', 'linkify_phone', @@ -11,6 +15,19 @@ __all__ = ( ) +def get_table_configs(table, user): + """ + Return any available TableConfigs applicable to the given table & user. + """ + TableConfig = apps.get_model('extras', 'TableConfig') + return TableConfig.objects.filter( + Q(shared=True) | Q(user=user if user.is_authenticated else None), + object_type=ObjectType.objects.get_for_model(table.Meta.model), + table=table.name, + enabled=True, + ) + + def get_table_for_model(model, name=None): name = name or f'{model.__name__}Table' try: