فهرست منبع

Add support for custom fields in tables (#5460)

* Add support for custom fields in tables

* Fix empty list displays as none

Co-authored-by: TomGrozev <TomGrozev@users.noreply.github.com>
Co-authored-by: Jeremy Stretch <jeremy@netverity.dev>
Tom Grozev 4 سال پیش
والد
کامیت
be3d33eebd
1فایلهای تغییر یافته به همراه45 افزوده شده و 0 حذف شده
  1. 45 0
      netbox/utilities/tables.py

+ 45 - 0
netbox/utilities/tables.py

@@ -1,7 +1,9 @@
 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
@@ -9,6 +11,7 @@ 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
 
 
@@ -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
@@ -73,6 +89,12 @@ class BaseTable(tables.Table):
 
         # 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:
@@ -316,6 +338,28 @@ class TagColumn(tables.TemplateColumn):
         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.
@@ -362,3 +406,4 @@ def paginate_table(table, request):
         'per_page': get_paginate_count(request)
     }
     RequestConfig(request, paginate).configure(table)
+