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:
@ -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 %}—{% endif %}
|
{% if record.length %}{{ record.length }} {{ record.get_length_unit_display }}{% else %}—{% 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(
|
||||||
|
@ -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 %}—{% endif %}
|
{% if record.pk %}{% utilization_graph record.get_utilization %}{% else %}—{% 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(
|
||||||
|
@ -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')
|
||||||
|
@ -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')
|
||||||
|
@ -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.
|
||||||
|
@ -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(
|
||||||
|
Reference in New Issue
Block a user