mirror of
https://github.com/netbox-community/netbox.git
synced 2026-04-21 16:31:38 +02:00
Closes #21459: Avoid prefetching data for hidden table columns
This commit is contained in:
@@ -52,43 +52,14 @@ class BaseTable(tables.Table):
|
|||||||
'class': 'table table-hover object-list',
|
'class': 'table table-hover object-list',
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, *args, user=None, **kwargs):
|
# TODO: Remove user kwarg in NetBox v4.7
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
# Set default empty_text if none was provided
|
# Set default empty_text if none was provided
|
||||||
if self.empty_text is None:
|
if self.empty_text is None:
|
||||||
self.empty_text = _("No {model_name} found").format(model_name=self._meta.model._meta.verbose_name_plural)
|
self.empty_text = _("No {model_name} found").format(model_name=self._meta.model._meta.verbose_name_plural)
|
||||||
|
|
||||||
# Dynamically update the table's QuerySet to ensure related fields are pre-fetched
|
|
||||||
if isinstance(self.data, TableQuerysetData):
|
|
||||||
|
|
||||||
prefetch_fields = []
|
|
||||||
for column in self.columns:
|
|
||||||
if column.visible:
|
|
||||||
model = getattr(self.Meta, 'model')
|
|
||||||
accessor = column.accessor
|
|
||||||
if accessor.startswith('custom_field_data__'):
|
|
||||||
# Ignore custom field references
|
|
||||||
continue
|
|
||||||
prefetch_path = []
|
|
||||||
for field_name in accessor.split(accessor.SEPARATOR):
|
|
||||||
try:
|
|
||||||
field = model._meta.get_field(field_name)
|
|
||||||
except FieldDoesNotExist:
|
|
||||||
break
|
|
||||||
if isinstance(field, (RelatedField, ManyToOneRel)):
|
|
||||||
# Follow ForeignKeys to the related model
|
|
||||||
prefetch_path.append(field_name)
|
|
||||||
model = field.remote_field.model
|
|
||||||
elif isinstance(field, GenericForeignKey):
|
|
||||||
# Can't prefetch beyond a GenericForeignKey
|
|
||||||
prefetch_path.append(field_name)
|
|
||||||
break
|
|
||||||
if prefetch_path:
|
|
||||||
prefetch_fields.append('__'.join(prefetch_path))
|
|
||||||
self.data.data = self.data.data.prefetch_related(*prefetch_fields)
|
|
||||||
|
|
||||||
def _get_columns(self, visible=True):
|
def _get_columns(self, visible=True):
|
||||||
columns = []
|
columns = []
|
||||||
for name, column in self.columns.items():
|
for name, column in self.columns.items():
|
||||||
@@ -144,6 +115,41 @@ class BaseTable(tables.Table):
|
|||||||
self.sequence.remove('actions')
|
self.sequence.remove('actions')
|
||||||
self.sequence.append('actions')
|
self.sequence.append('actions')
|
||||||
|
|
||||||
|
def _apply_prefetching(self):
|
||||||
|
"""
|
||||||
|
Dynamically update the table's QuerySet to ensure related fields are pre-fetched
|
||||||
|
"""
|
||||||
|
if not isinstance(self.data, TableQuerysetData):
|
||||||
|
return
|
||||||
|
|
||||||
|
prefetch_fields = []
|
||||||
|
for column in self.columns:
|
||||||
|
if not column.visible:
|
||||||
|
# Skip hidden columns
|
||||||
|
continue
|
||||||
|
model = getattr(self.Meta, 'model') # Must be called *after* resolving columns
|
||||||
|
accessor = column.accessor
|
||||||
|
if accessor.startswith('custom_field_data__'):
|
||||||
|
# Ignore custom field references
|
||||||
|
continue
|
||||||
|
prefetch_path = []
|
||||||
|
for field_name in accessor.split(accessor.SEPARATOR):
|
||||||
|
try:
|
||||||
|
field = model._meta.get_field(field_name)
|
||||||
|
except FieldDoesNotExist:
|
||||||
|
break
|
||||||
|
if isinstance(field, (RelatedField, ManyToOneRel)):
|
||||||
|
# Follow ForeignKeys to the related model
|
||||||
|
prefetch_path.append(field_name)
|
||||||
|
model = field.remote_field.model
|
||||||
|
elif isinstance(field, GenericForeignKey):
|
||||||
|
# Can't prefetch beyond a GenericForeignKey
|
||||||
|
prefetch_path.append(field_name)
|
||||||
|
break
|
||||||
|
if prefetch_path:
|
||||||
|
prefetch_fields.append('__'.join(prefetch_path))
|
||||||
|
self.data.data = self.data.data.prefetch_related(*prefetch_fields)
|
||||||
|
|
||||||
def configure(self, request):
|
def configure(self, request):
|
||||||
"""
|
"""
|
||||||
Configure the table for a specific request context. This performs pagination and records
|
Configure the table for a specific request context. This performs pagination and records
|
||||||
@@ -178,6 +184,7 @@ class BaseTable(tables.Table):
|
|||||||
columns = getattr(self.Meta, 'default_columns', self.Meta.fields)
|
columns = getattr(self.Meta, 'default_columns', self.Meta.fields)
|
||||||
|
|
||||||
self._set_columns(columns)
|
self._set_columns(columns)
|
||||||
|
self._apply_prefetching()
|
||||||
if ordering is not None:
|
if ordering is not None:
|
||||||
self.order_by = ordering
|
self.order_by = ordering
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,6 @@ class ObjectChangeLogView(ConditionalLoginRequiredMixin, View):
|
|||||||
objectchanges_table = ObjectChangeTable(
|
objectchanges_table = ObjectChangeTable(
|
||||||
data=objectchanges,
|
data=objectchanges,
|
||||||
orderable=False,
|
orderable=False,
|
||||||
user=request.user
|
|
||||||
)
|
)
|
||||||
objectchanges_table.configure(request)
|
objectchanges_table.configure(request)
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ class TableMixin:
|
|||||||
request.user.config.set(f'tables.{table}.columns', tableconfig.columns)
|
request.user.config.set(f'tables.{table}.columns', tableconfig.columns)
|
||||||
request.user.config.set(f'tables.{table}.ordering', tableconfig.ordering, commit=True)
|
request.user.config.set(f'tables.{table}.ordering', tableconfig.ordering, commit=True)
|
||||||
|
|
||||||
table = self.table(data, user=request.user)
|
table = self.table(data)
|
||||||
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')
|
||||||
table.configure(request)
|
table.configure(request)
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ class WirelessLANView(generic.ObjectView):
|
|||||||
attached_interfaces = Interface.objects.restrict(request.user, 'view').filter(
|
attached_interfaces = Interface.objects.restrict(request.user, 'view').filter(
|
||||||
wireless_lans=instance
|
wireless_lans=instance
|
||||||
)
|
)
|
||||||
interfaces_table = tables.WirelessLANInterfacesTable(attached_interfaces, user=request.user)
|
interfaces_table = tables.WirelessLANInterfacesTable(attached_interfaces)
|
||||||
interfaces_table.configure(request)
|
interfaces_table.configure(request)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user