Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions netbox/utilities/tables.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import django_tables2 as tables
from django.contrib.auth.models import AnonymousUser
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import FieldDoesNotExist
from django.db.models import Func, F, Value
from django.db.models.fields.related import RelatedField
from django.urls import reverse
from django.utils.html import strip_tags
from django.utils.safestring import mark_safe
from django_tables2 import RequestConfig
from django_tables2.data import TableQuerysetData

from .models import CustomField
from .paginator import EnhancedPaginator, get_paginate_count


Expand All @@ -30,12 +33,25 @@ class BaseTable(tables.Table):

:param user: Personalize table display for the given user (optional). Has no effect if AnonymousUser is passed.
"""

class Meta:
attrs = {
'class': 'table table-hover table-headings',
}

def __init__(self, *args, user=None, **kwargs):
# Add custom field columns
obj_type = ContentType.objects.get_for_model(self._meta.model)
custom_fields = {}

for cf in CustomField.objects.filter(content_types=obj_type):
name = 'cf_{}'.format(cf.name)
label = cf.label if cf.label != '' else cf.name
self.base_columns[name] = CustomFieldColumn(verbose_name=label)
custom_fields[name] = cf
self._meta.fields += tuple(custom_fields.keys())

# Init table
super().__init__(*args, **kwargs)

# Set default empty_text if none was provided
Expand Down Expand Up @@ -73,6 +89,12 @@ def __init__(self, *args, user=None, **kwargs):

# Dynamically update the table's QuerySet to ensure related fields are pre-fetched
if isinstance(self.data, TableQuerysetData):
# Extract custom field values
cf_fields = {}
for key, cf in custom_fields.items():
cf_fields[key] = Func(F('custom_field_data'), Value(cf.name), function='jsonb_extract_path_text')
self.data.data = self.data.data.annotate(**cf_fields)

prefetch_fields = []
for column in self.columns:
if column.visible:
Expand Down Expand Up @@ -316,6 +338,28 @@ def value(self, value):
return ",".join([tag.name for tag in value.all()])


class CustomFieldColumn(tables.Column):
"""
Display custom fields in the appropriate format.
"""
def render(self, record, bound_column, value):
if isinstance(value, list):
if len(value):
template = ''
for v in value:
template += f'<span class="label label-default">{v}</span> '
else:
template = '<span class="text-muted">&mdash;</span>'
elif value:
template = value
else:
return self.default
return mark_safe(template)

def value(self, value):
return value


class MPTTColumn(tables.TemplateColumn):
"""
Display a nested hierarchy for MPTT-enabled models.
Expand Down Expand Up @@ -362,3 +406,4 @@ def paginate_table(table, request):
'per_page': get_paginate_count(request)
}
RequestConfig(request, paginate).configure(table)