فهرست منبع

Introduce LinkedCountColumn to standardize approach to counting related items in tables

Jeremy Stretch 5 سال پیش
والد
کامیت
28f0da0bc1
6فایلهای تغییر یافته به همراه82 افزوده شده و 81 حذف شده
  1. 26 49
      netbox/dcim/tables.py
  2. 15 15
      netbox/ipam/tables.py
  3. 4 2
      netbox/secrets/tables.py
  4. 4 2
      netbox/tenancy/tables.py
  5. 24 0
      netbox/utilities/tables.py
  6. 9 13
      netbox/virtualization/tables.py

+ 26 - 49
netbox/dcim/tables.py

@@ -3,8 +3,8 @@ from django_tables2.utils import Accessor
 
 from tenancy.tables import COL_TENANT
 from utilities.tables import (
-    BaseTable, BooleanColumn, ButtonsColumn, ChoiceFieldColumn, ColorColumn, ColoredLabelColumn, TagColumn,
-    ToggleColumn,
+    BaseTable, BooleanColumn, ButtonsColumn, ChoiceFieldColumn, ColorColumn, ColoredLabelColumn, LinkedCountColumn,
+    TagColumn, ToggleColumn,
 )
 from .models import (
     Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
@@ -49,14 +49,6 @@ RACKGROUP_ELEVATIONS = """
 </a>
 """
 
-RACK_DEVICE_COUNT = """
-<a href="{% url 'dcim:device_list' %}?rack_id={{ record.pk }}">{{ value }}</a>
-"""
-
-DEVICE_COUNT = """
-<a href="{% url 'dcim:device_list' %}?role={{ record.slug }}">{{ value|default:0 }}</a>
-"""
-
 RACKRESERVATION_ACTIONS = """
 <a href="{% url 'dcim:rackreservation_changelog' pk=record.pk %}" class="btn btn-default btn-xs" title="Change log">
     <i class="fa fa-history"></i>
@@ -75,14 +67,6 @@ MANUFACTURER_ACTIONS = """
 {% endif %}
 """
 
-DEVICEROLE_DEVICE_COUNT = """
-<a href="{% url 'dcim:device_list' %}?role={{ record.slug }}">{{ value|default:0 }}</a>
-"""
-
-DEVICEROLE_VM_COUNT = """
-<a href="{% url 'virtualization:virtualmachine_list' %}?role={{ record.slug }}">{{ value|default:0 }}</a>
-"""
-
 DEVICEROLE_ACTIONS = """
 <a href="{% url 'dcim:devicerole_changelog' slug=record.slug %}" class="btn btn-default btn-xs" title="Change log">
     <i class="fa fa-history"></i>
@@ -92,24 +76,12 @@ DEVICEROLE_ACTIONS = """
 {% endif %}
 """
 
-PLATFORM_DEVICE_COUNT = """
-<a href="{% url 'dcim:device_list' %}?platform={{ record.slug }}">{{ value|default:0 }}</a>
-"""
-
-PLATFORM_VM_COUNT = """
-<a href="{% url 'virtualization:virtualmachine_list' %}?platform={{ record.slug }}">{{ value|default:0 }}</a>
-"""
-
 DEVICE_PRIMARY_IP = """
 {{ record.primary_ip6.address.ip|default:"" }}
 {% if record.primary_ip6 and record.primary_ip4 %}<br />{% endif %}
 {{ record.primary_ip4.address.ip|default:"" }}
 """
 
-DEVICETYPE_INSTANCES_TEMPLATE = """
-<a href="{% url 'dcim:device_list' %}?manufacturer_id={{ record.manufacturer_id }}&device_type_id={{ record.pk }}">{{ record.instance_count }}</a>
-"""
-
 UTILIZATION_GRAPH = """
 {% load helpers %}
 {% utilization_graph value %}
@@ -129,10 +101,6 @@ CABLE_LENGTH = """
 {% if record.length %}{{ record.length }} {{ record.get_length_unit_display }}{% else %}&mdash;{% endif %}
 """
 
-POWERPANEL_POWERFEED_COUNT = """
-<a href="{% url 'dcim:powerfeed_list' %}?power_panel_id={{ record.pk }}">{{ value }}</a>
-"""
-
 INTERFACE_IPADDRESSES = """
 {% for ip in record.ip_addresses.unrestricted %}
     <a href="{{ ip.get_absolute_url }}">{{ ip }}</a><br />
@@ -280,8 +248,9 @@ class RackTable(BaseTable):
 
 
 class RackDetailTable(RackTable):
-    device_count = tables.TemplateColumn(
-        template_code=RACK_DEVICE_COUNT,
+    device_count = LinkedCountColumn(
+        viewname='dcim:device_list',
+        url_params={'rack_id': 'pk'},
         verbose_name='Devices'
     )
     get_utilization = tables.TemplateColumn(
@@ -388,8 +357,9 @@ class DeviceTypeTable(BaseTable):
     is_full_depth = BooleanColumn(
         verbose_name='Full Depth'
     )
-    instance_count = tables.TemplateColumn(
-        template_code=DEVICETYPE_INSTANCES_TEMPLATE,
+    instance_count = LinkedCountColumn(
+        viewname='dcim:device_list',
+        url_params={'device_type_id': 'pk'},
         verbose_name='Instances'
     )
     tags = TagColumn(
@@ -526,12 +496,14 @@ class DeviceBayTemplateTable(ComponentTemplateTable):
 
 class DeviceRoleTable(BaseTable):
     pk = ToggleColumn()
-    device_count = tables.TemplateColumn(
-        template_code=DEVICEROLE_DEVICE_COUNT,
+    device_count = LinkedCountColumn(
+        viewname='dcim:device_list',
+        url_params={'role': 'slug'},
         verbose_name='Devices'
     )
-    vm_count = tables.TemplateColumn(
-        template_code=DEVICEROLE_VM_COUNT,
+    vm_count = LinkedCountColumn(
+        viewname='virtualization:virtualmachine_list',
+        url_params={'role': 'slug'},
         verbose_name='VMs'
     )
     color = tables.TemplateColumn(
@@ -553,12 +525,14 @@ class DeviceRoleTable(BaseTable):
 
 class PlatformTable(BaseTable):
     pk = ToggleColumn()
-    device_count = tables.TemplateColumn(
-        template_code=PLATFORM_DEVICE_COUNT,
+    device_count = LinkedCountColumn(
+        viewname='dcim:device_list',
+        url_params={'platform': 'slug'},
         verbose_name='Devices'
     )
-    vm_count = tables.TemplateColumn(
-        template_code=PLATFORM_VM_COUNT,
+    vm_count = LinkedCountColumn(
+        viewname='virtualization:virtualmachine_list',
+        url_params={'platform': 'slug'},
         verbose_name='VMs'
     )
     actions = ButtonsColumn(Platform, pk_field='slug')
@@ -994,7 +968,9 @@ class VirtualChassisTable(BaseTable):
     master = tables.Column(
         linkify=True
     )
-    member_count = tables.Column(
+    member_count = LinkedCountColumn(
+        viewname='dcim:device_list',
+        url_params={'virtual_chassis_id': 'pk'},
         verbose_name='Members'
     )
     tags = TagColumn(
@@ -1018,8 +994,9 @@ class PowerPanelTable(BaseTable):
         viewname='dcim:site',
         args=[Accessor('site__slug')]
     )
-    powerfeed_count = tables.TemplateColumn(
-        template_code=POWERPANEL_POWERFEED_COUNT,
+    powerfeed_count = LinkedCountColumn(
+        viewname='dcim:powerfeed_list',
+        url_params={'power_panel_id': 'pk'},
         verbose_name='Feeds'
     )
     tags = TagColumn(

+ 15 - 15
netbox/ipam/tables.py

@@ -4,7 +4,9 @@ from django_tables2.utils import Accessor
 
 from dcim.models import Interface
 from tenancy.tables import COL_TENANT
-from utilities.tables import BaseTable, BooleanColumn, ButtonsColumn, ChoiceFieldColumn, TagColumn, ToggleColumn
+from utilities.tables import (
+    BaseTable, BooleanColumn, ButtonsColumn, ChoiceFieldColumn, LinkedCountColumn, TagColumn, ToggleColumn,
+)
 from virtualization.models import VMInterface
 from .models import Aggregate, IPAddress, Prefix, RIR, Role, RouteTarget, Service, VLAN, VLANGroup, VRF
 
@@ -34,14 +36,6 @@ UTILIZATION_GRAPH = """
 {% if record.pk %}{% utilization_graph record.get_utilization %}{% else %}&mdash;{% endif %}
 """
 
-ROLE_PREFIX_COUNT = """
-<a href="{% url 'ipam:prefix_list' %}?role={{ record.slug }}">{{ value|default:0 }}</a>
-"""
-
-ROLE_VLAN_COUNT = """
-<a href="{% url 'ipam:vlan_list' %}?role={{ record.slug }}">{{ value|default:0 }}</a>
-"""
-
 PREFIX_LINK = """
 {% if record.children %}
     <span class="text-nowrap" style="padding-left: {{ record.parents }}0px "><i class="fa fa-caret-right"></i></a>
@@ -209,7 +203,9 @@ class RIRTable(BaseTable):
     is_private = BooleanColumn(
         verbose_name='Private'
     )
-    aggregate_count = tables.Column(
+    aggregate_count = LinkedCountColumn(
+        viewname='ipam:aggregate_list',
+        url_params={'rir': 'slug'},
         verbose_name='Aggregates'
     )
     actions = ButtonsColumn(RIR, pk_field='slug')
@@ -304,12 +300,14 @@ class AggregateDetailTable(AggregateTable):
 
 class RoleTable(BaseTable):
     pk = ToggleColumn()
-    prefix_count = tables.TemplateColumn(
-        template_code=ROLE_PREFIX_COUNT,
+    prefix_count = LinkedCountColumn(
+        viewname='ipam:prefix_list',
+        url_params={'role': 'slug'},
         verbose_name='Prefixes'
     )
-    vlan_count = tables.TemplateColumn(
-        template_code=ROLE_VLAN_COUNT,
+    vlan_count = LinkedCountColumn(
+        viewname='ipam:vlan_list',
+        url_params={'role': 'slug'},
         verbose_name='VLANs'
     )
     actions = ButtonsColumn(Role, pk_field='slug')
@@ -508,7 +506,9 @@ class VLANGroupTable(BaseTable):
         viewname='dcim:site',
         args=[Accessor('site__slug')]
     )
-    vlan_count = tables.Column(
+    vlan_count = LinkedCountColumn(
+        viewname='ipam:vlan_list',
+        url_params={'group': 'slug'},
         verbose_name='VLANs'
     )
     actions = ButtonsColumn(

+ 4 - 2
netbox/secrets/tables.py

@@ -1,6 +1,6 @@
 import django_tables2 as tables
 
-from utilities.tables import BaseTable, ButtonsColumn, TagColumn, ToggleColumn
+from utilities.tables import BaseTable, ButtonsColumn, LinkedCountColumn, TagColumn, ToggleColumn
 from .models import SecretRole, Secret
 
 
@@ -11,7 +11,9 @@ from .models import SecretRole, Secret
 class SecretRoleTable(BaseTable):
     pk = ToggleColumn()
     name = tables.LinkColumn()
-    secret_count = tables.Column(
+    secret_count = LinkedCountColumn(
+        viewname='secrets:secret_list',
+        url_params={'role': 'slug'},
         verbose_name='Secrets'
     )
     actions = ButtonsColumn(SecretRole, pk_field='slug')

+ 4 - 2
netbox/tenancy/tables.py

@@ -1,6 +1,6 @@
 import django_tables2 as tables
 
-from utilities.tables import BaseTable, ButtonsColumn, TagColumn, ToggleColumn
+from utilities.tables import BaseTable, ButtonsColumn, LinkedCountColumn, TagColumn, ToggleColumn
 from .models import Tenant, TenantGroup
 
 MPTT_LINK = """
@@ -32,7 +32,9 @@ class TenantGroupTable(BaseTable):
         template_code=MPTT_LINK,
         orderable=False
     )
-    tenant_count = tables.Column(
+    tenant_count = LinkedCountColumn(
+        viewname='tenancy:tenant_list',
+        url_params={'group': 'slug'},
         verbose_name='Tenants'
     )
     actions = ButtonsColumn(TenantGroup, pk_field='slug')

+ 24 - 0
netbox/utilities/tables.py

@@ -1,6 +1,7 @@
 import django_tables2 as tables
 from django.core.exceptions import FieldDoesNotExist
 from django.db.models.fields.related import RelatedField
+from django.urls import reverse
 from django.utils.safestring import mark_safe
 from django_tables2.data import TableQuerysetData
 
@@ -213,6 +214,29 @@ class ColoredLabelColumn(tables.TemplateColumn):
         super().__init__(template_code=self.template_code, *args, **kwargs)
 
 
+class LinkedCountColumn(tables.Column):
+    """
+    Render a count of related objects linked to a filtered URL.
+
+    :param viewname: The view name to use for URL resolution
+    :param view_kwargs: Additional kwargs to pass for URL resolution (optional)
+    :param url_params: A dict of query parameters to append to the URL (e.g. ?foo=bar) (optional)
+    """
+    def __init__(self, viewname, *args, view_kwargs=None, url_params=None, default=0, **kwargs):
+        self.viewname = viewname
+        self.view_kwargs = view_kwargs or {}
+        self.url_params = url_params
+        super().__init__(*args, default=default, **kwargs)
+
+    def render(self, record, value):
+        if value:
+            url = reverse(self.viewname, kwargs=self.view_kwargs)
+            if self.url_params:
+                url += '?' + '&'.join([f'{k}={getattr(record, v)}' for k, v in self.url_params.items()])
+            return mark_safe(f'<a href="{url}">{value}</a>')
+        return value
+
+
 class TagColumn(tables.TemplateColumn):
     """
     Display a list of tags assigned to the object.

+ 9 - 13
netbox/virtualization/tables.py

@@ -2,7 +2,9 @@ import django_tables2 as tables
 
 from dcim.tables import BaseInterfaceTable
 from tenancy.tables import COL_TENANT
-from utilities.tables import BaseTable, ButtonsColumn, ChoiceFieldColumn, ColoredLabelColumn, TagColumn, ToggleColumn
+from utilities.tables import (
+    BaseTable, ButtonsColumn, ChoiceFieldColumn, ColoredLabelColumn, LinkedCountColumn, TagColumn, ToggleColumn,
+)
 from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface
 
 VIRTUALMACHINE_PRIMARY_IP = """
@@ -11,14 +13,6 @@ VIRTUALMACHINE_PRIMARY_IP = """
 {{ record.primary_ip4.address.ip|default:"" }}
 """
 
-DEVICE_COUNT = """
-<a href="{% url 'dcim:device_list' %}?cluster_id={{ record.pk }}">{{ value|default:0 }}</a>
-"""
-
-VM_COUNT = """
-<a href="{% url 'virtualization:virtualmachine_list' %}?cluster_id={{ record.pk }}">{{ value|default:0 }}</a>
-"""
-
 
 #
 # Cluster types
@@ -69,12 +63,14 @@ class ClusterTable(BaseTable):
     site = tables.Column(
         linkify=True
     )
-    device_count = tables.TemplateColumn(
-        template_code=DEVICE_COUNT,
+    device_count = LinkedCountColumn(
+        viewname='dcim:device_list',
+        url_params={'cluster_id': 'pk'},
         verbose_name='Devices'
     )
-    vm_count = tables.TemplateColumn(
-        template_code=VM_COUNT,
+    vm_count = LinkedCountColumn(
+        viewname='virtualization:virtualmachine_list',
+        url_params={'cluster_id': 'pk'},
         verbose_name='VMs'
     )
     tags = TagColumn(