1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Introduce LinkedCountColumn to standardize approach to counting related items in tables

This commit is contained in:
Jeremy Stretch
2020-09-25 12:42:17 -04:00
parent 18a8a91d57
commit 28f0da0bc1
6 changed files with 82 additions and 81 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,7 @@
import django_tables2 as tables import django_tables2 as tables
from django.core.exceptions import FieldDoesNotExist from django.core.exceptions import FieldDoesNotExist
from django.db.models.fields.related import RelatedField from django.db.models.fields.related import RelatedField
from django.urls import reverse
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django_tables2.data import TableQuerysetData from django_tables2.data import TableQuerysetData
@ -213,6 +214,29 @@ class ColoredLabelColumn(tables.TemplateColumn):
super().__init__(template_code=self.template_code, *args, **kwargs) 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): class TagColumn(tables.TemplateColumn):
""" """
Display a list of tags assigned to the object. Display a list of tags assigned to the object.

View File

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