mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-16 04:02:52 -06:00
* Fixes #19264: Support table configs on child object list views * Clear assigned table config when resetting the configuration
This commit is contained in:
parent
dfd788c643
commit
a83dfff736
@ -7,7 +7,7 @@ from django.contrib.contenttypes.fields import GenericForeignKey, GenericRel
|
|||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist, ValidationError
|
from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist, ValidationError
|
||||||
from django.db import transaction, IntegrityError
|
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.db.models.fields.reverse_related import ManyToManyRel
|
||||||
from django.forms import ModelMultipleChoiceField, MultipleHiddenInput
|
from django.forms import ModelMultipleChoiceField, MultipleHiddenInput
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
@ -21,7 +21,7 @@ from mptt.models import MPTTModel
|
|||||||
from core.models import ObjectType
|
from core.models import ObjectType
|
||||||
from core.signals import clear_events
|
from core.signals import clear_events
|
||||||
from extras.choices import CustomFieldUIEditableChoices
|
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.error_handlers import handle_protectederror
|
||||||
from utilities.exceptions import AbortRequest, AbortTransaction, PermissionsViolation
|
from utilities.exceptions import AbortRequest, AbortTransaction, PermissionsViolation
|
||||||
from utilities.forms import BulkRenameForm, ConfirmationForm, restrict_form_fields
|
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.htmx import htmx_partial
|
||||||
from utilities.permissions import get_permission_for_model
|
from utilities.permissions import get_permission_for_model
|
||||||
from utilities.query import reapply_model_ordering
|
from utilities.query import reapply_model_ordering
|
||||||
|
from utilities.tables import get_table_configs
|
||||||
from utilities.views import GetReturnURLMixin, get_viewname
|
from utilities.views import GetReturnURLMixin, get_viewname
|
||||||
from .base import BaseMultiObjectView
|
from .base import BaseMultiObjectView
|
||||||
from .mixins import ActionsMixin, TableMixin
|
from .mixins import ActionsMixin, TableMixin
|
||||||
@ -140,15 +141,6 @@ class ObjectListView(BaseMultiObjectView, ActionsMixin, TableMixin):
|
|||||||
model = self.queryset.model
|
model = self.queryset.model
|
||||||
object_type = ObjectType.objects.get_for_model(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:
|
if self.filterset:
|
||||||
self.queryset = self.filterset(request.GET, self.queryset, request=request).qs
|
self.queryset = self.filterset(request.GET, self.queryset, request=request).qs
|
||||||
|
|
||||||
@ -184,14 +176,6 @@ class ObjectListView(BaseMultiObjectView, ActionsMixin, TableMixin):
|
|||||||
# Render the objects table
|
# Render the objects table
|
||||||
table = self.get_table(self.queryset, request, has_bulk_actions)
|
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 this is an HTMX request, return only the rendered table HTML
|
||||||
if htmx_partial(request):
|
if htmx_partial(request):
|
||||||
if request.GET.get('embedded', False):
|
if request.GET.get('embedded', False):
|
||||||
@ -208,7 +192,7 @@ class ObjectListView(BaseMultiObjectView, ActionsMixin, TableMixin):
|
|||||||
context = {
|
context = {
|
||||||
'model': model,
|
'model': model,
|
||||||
'table': table,
|
'table': table,
|
||||||
'table_configs': table_configs,
|
'table_configs': get_table_configs(table, request.user),
|
||||||
'actions': actions,
|
'actions': actions,
|
||||||
'filter_form': self.filterset_form(request.GET) if self.filterset_form else None,
|
'filter_form': self.filterset_form(request.GET) if self.filterset_form else None,
|
||||||
'prerequisite_model': get_prerequisite_model(self.queryset),
|
'prerequisite_model': get_prerequisite_model(self.queryset),
|
||||||
|
@ -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 netbox.constants import DEFAULT_ACTION_PERMISSIONS
|
||||||
from utilities.permissions import get_permission_for_model
|
from utilities.permissions import get_permission_for_model
|
||||||
|
|
||||||
@ -47,6 +50,15 @@ class TableMixin:
|
|||||||
request: The current request
|
request: The current request
|
||||||
bulk_actions: Render checkboxes for object selection
|
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)
|
table = self.table(data, user=request.user)
|
||||||
if 'pk' in table.base_columns and bulk_actions:
|
if 'pk' in table.base_columns and bulk_actions:
|
||||||
table.columns.show('pk')
|
table.columns.show('pk')
|
||||||
|
@ -20,6 +20,7 @@ from utilities.forms import ConfirmationForm, restrict_form_fields
|
|||||||
from utilities.htmx import htmx_partial
|
from utilities.htmx import htmx_partial
|
||||||
from utilities.permissions import get_permission_for_model
|
from utilities.permissions import get_permission_for_model
|
||||||
from utilities.querydict import normalize_querydict, prepare_cloned_fields
|
from utilities.querydict import normalize_querydict, prepare_cloned_fields
|
||||||
|
from utilities.tables import get_table_configs
|
||||||
from utilities.views import GetReturnURLMixin, get_viewname
|
from utilities.views import GetReturnURLMixin, get_viewname
|
||||||
from .base import BaseObjectView
|
from .base import BaseObjectView
|
||||||
from .mixins import ActionsMixin, TableMixin
|
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',
|
'base_template': f'{instance._meta.app_label}/{instance._meta.model_name}.html',
|
||||||
'table': table,
|
'table': table,
|
||||||
'table_config': f'{table.name}_config',
|
'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,
|
'filter_form': self.filterset_form(request.GET) if self.filterset_form else None,
|
||||||
'actions': actions,
|
'actions': actions,
|
||||||
'tab': self.tab,
|
'tab': self.tab,
|
||||||
|
BIN
netbox/project-static/dist/netbox.js
vendored
BIN
netbox/project-static/dist/netbox.js
vendored
Binary file not shown.
BIN
netbox/project-static/dist/netbox.js.map
vendored
BIN
netbox/project-static/dist/netbox.js.map
vendored
Binary file not shown.
@ -80,7 +80,8 @@ function handleSubmit(event: Event): void {
|
|||||||
const toast = createToast('danger', 'Error Resetting Table Configuration', res.error);
|
const toast = createToast('danger', 'Error Resetting Table Configuration', res.error);
|
||||||
toast.show();
|
toast.show();
|
||||||
} else {
|
} else {
|
||||||
location.reload();
|
// Strip any URL query parameters & reload the page
|
||||||
|
window.location.href = window.location.origin + window.location.pathname;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
@ -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.module_loading import import_string
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from core.models import ObjectType
|
||||||
from netbox.registry import registry
|
from netbox.registry import registry
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
|
'get_table_configs',
|
||||||
'get_table_for_model',
|
'get_table_for_model',
|
||||||
'get_table_ordering',
|
'get_table_ordering',
|
||||||
'linkify_phone',
|
'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):
|
def get_table_for_model(model, name=None):
|
||||||
name = name or f'{model.__name__}Table'
|
name = name or f'{model.__name__}Table'
|
||||||
try:
|
try:
|
||||||
|
Loading…
Reference in New Issue
Block a user