mirror of
https://github.com/netbox-community/netbox.git
synced 2026-02-18 06:47:46 +01:00
Compare commits
3 Commits
v4.5.3
...
21459-tabl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64735d587c | ||
|
|
2900429769 | ||
|
|
278c82dd88 |
@@ -52,43 +52,14 @@ class BaseTable(tables.Table):
|
||||
'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)
|
||||
|
||||
# Set default empty_text if none was provided
|
||||
if self.empty_text is None:
|
||||
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):
|
||||
columns = []
|
||||
for name, column in self.columns.items():
|
||||
@@ -144,6 +115,41 @@ class BaseTable(tables.Table):
|
||||
self.sequence.remove('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):
|
||||
"""
|
||||
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)
|
||||
|
||||
self._set_columns(columns)
|
||||
self._apply_prefetching()
|
||||
if ordering is not None:
|
||||
self.order_by = ordering
|
||||
|
||||
|
||||
@@ -68,7 +68,6 @@ class ObjectChangeLogView(ConditionalLoginRequiredMixin, View):
|
||||
objectchanges_table = ObjectChangeTable(
|
||||
data=objectchanges,
|
||||
orderable=False,
|
||||
user=request.user
|
||||
)
|
||||
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}.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:
|
||||
table.columns.show('pk')
|
||||
table.configure(request)
|
||||
|
||||
@@ -109,7 +109,7 @@ class WirelessLANView(generic.ObjectView):
|
||||
attached_interfaces = Interface.objects.restrict(request.user, 'view').filter(
|
||||
wireless_lans=instance
|
||||
)
|
||||
interfaces_table = tables.WirelessLANInterfacesTable(attached_interfaces, user=request.user)
|
||||
interfaces_table = tables.WirelessLANInterfacesTable(attached_interfaces)
|
||||
interfaces_table.configure(request)
|
||||
|
||||
return {
|
||||
|
||||
48
ruff.toml
48
ruff.toml
@@ -1,16 +1,58 @@
|
||||
# Ruff configuration
|
||||
####################
|
||||
|
||||
exclude = [
|
||||
"netbox/project-static/**"
|
||||
".eggs",
|
||||
".git",
|
||||
".pyenv",
|
||||
".pytest_cache",
|
||||
".ruff_cache",
|
||||
".venv",
|
||||
".vscode",
|
||||
"__pypackages__",
|
||||
"_build",
|
||||
"build",
|
||||
"dist",
|
||||
"netbox/project-static/**",
|
||||
"node_modules",
|
||||
"site-packages",
|
||||
"venv",
|
||||
]
|
||||
|
||||
# Enforce line length and indent-width
|
||||
line-length = 120
|
||||
indent-width = 4
|
||||
|
||||
# Ignores anything in .gitignore
|
||||
respect-gitignore = true
|
||||
|
||||
# Always generate Python 3.12-compatible code
|
||||
target-version = "py312"
|
||||
|
||||
[lint]
|
||||
extend-select = ["E1", "E2", "E3", "E501", "W"]
|
||||
ignore = ["F403", "F405"]
|
||||
extend-select = [
|
||||
"E1", # pycodestyle errors: indentation-related (e.g., unexpected/missing indent)
|
||||
"E2", # pycodestyle errors: whitespace-related (e.g., missing whitespace, extra spaces)
|
||||
"E3", # pycodestyle errors: blank lines / spacing around definitions
|
||||
"E501", # pycodestyle: line too long (enforced with `line-length` above)
|
||||
"W", # pycodestyle warnings (various style warnings, often whitespace/newlines)
|
||||
]
|
||||
ignore = [
|
||||
"F403", # pyflakes: `from ... import *` used; unable to detect undefined names
|
||||
"F405", # pyflakes: name may be undefined or defined from star imports
|
||||
"UP032", # pyupgrade: prefer f-strings over `str.format(...)`
|
||||
]
|
||||
preview = true
|
||||
|
||||
[lint.per-file-ignores]
|
||||
"template_code.py" = ["E501"]
|
||||
|
||||
[format]
|
||||
# Use single quotes for strings.
|
||||
quote-style = "single"
|
||||
|
||||
# Indent with spaces, rather than tabs.
|
||||
indent-style = "space"
|
||||
|
||||
# Enforce UNIX line ending
|
||||
line-ending = "lf"
|
||||
|
||||
Reference in New Issue
Block a user