From a5dc91c17514826d23ac5b278eabe558d0018d0b Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 29 Mar 2017 16:05:23 -0400 Subject: [PATCH] Introduced SearchTable for improved performance --- netbox/circuits/tables.py | 52 ++++++++++----- netbox/dcim/filters.py | 6 +- netbox/dcim/tables.py | 103 ++++++++++++++++++++++------- netbox/ipam/tables.py | 122 ++++++++++++++++++++++++----------- netbox/netbox/views.py | 69 ++++++++++---------- netbox/secrets/tables.py | 14 ++-- netbox/templates/search.html | 36 +++++------ netbox/templates/table.html | 4 +- netbox/tenancy/tables.py | 13 ++-- netbox/utilities/tables.py | 15 ++++- 10 files changed, 290 insertions(+), 144 deletions(-) diff --git a/netbox/circuits/tables.py b/netbox/circuits/tables.py index ab877a8ce..07e2c4477 100644 --- a/netbox/circuits/tables.py +++ b/netbox/circuits/tables.py @@ -1,7 +1,7 @@ import django_tables2 as tables from django_tables2.utils import Accessor -from utilities.tables import BaseTable, ToggleColumn +from utilities.tables import BaseTable, SearchTable, ToggleColumn from .models import Circuit, CircuitType, Provider @@ -19,9 +19,7 @@ CIRCUITTYPE_ACTIONS = """ class ProviderTable(BaseTable): pk = ToggleColumn() - name = tables.LinkColumn('circuits:provider', args=[Accessor('slug')], verbose_name='Name') - asn = tables.Column(verbose_name='ASN') - account = tables.Column(verbose_name='Account') + name = tables.LinkColumn() circuit_count = tables.Column(accessor=Accessor('count_circuits'), verbose_name='Circuits') class Meta(BaseTable.Meta): @@ -29,17 +27,25 @@ class ProviderTable(BaseTable): fields = ('pk', 'name', 'asn', 'account', 'circuit_count') +class ProviderSearchTable(SearchTable): + name = tables.LinkColumn() + + class Meta(SearchTable.Meta): + model = Provider + fields = ('name', 'asn', 'account') + + # # Circuit types # class CircuitTypeTable(BaseTable): pk = ToggleColumn() - name = tables.LinkColumn(verbose_name='Name') + name = tables.LinkColumn() circuit_count = tables.Column(verbose_name='Circuits') - slug = tables.Column(verbose_name='Slug') - actions = tables.TemplateColumn(template_code=CIRCUITTYPE_ACTIONS, attrs={'td': {'class': 'text-right'}}, - verbose_name='') + actions = tables.TemplateColumn( + template_code=CIRCUITTYPE_ACTIONS, attrs={'td': {'class': 'text-right'}}, verbose_name='' + ) class Meta(BaseTable.Meta): model = CircuitType @@ -52,16 +58,28 @@ class CircuitTypeTable(BaseTable): class CircuitTable(BaseTable): pk = ToggleColumn() - cid = tables.LinkColumn('circuits:circuit', args=[Accessor('pk')], verbose_name='ID') - type = tables.Column(verbose_name='Type') - provider = tables.LinkColumn('circuits:provider', args=[Accessor('provider.slug')], verbose_name='Provider') - tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')], verbose_name='Tenant') - a_side = tables.LinkColumn('dcim:site', accessor=Accessor('termination_a.site'), orderable=False, - args=[Accessor('termination_a.site.slug')]) - z_side = tables.LinkColumn('dcim:site', accessor=Accessor('termination_z.site'), orderable=False, - args=[Accessor('termination_z.site.slug')]) - description = tables.Column(verbose_name='Description') + cid = tables.LinkColumn(verbose_name='ID') + provider = tables.LinkColumn('circuits:provider', args=[Accessor('provider.slug')]) + tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')]) + a_side = tables.LinkColumn( + 'dcim:site', accessor=Accessor('termination_a.site'), orderable=False, + args=[Accessor('termination_a.site.slug')] + ) + z_side = tables.LinkColumn( + 'dcim:site', accessor=Accessor('termination_z.site'), orderable=False, + args=[Accessor('termination_z.site.slug')] + ) class Meta(BaseTable.Meta): model = Circuit fields = ('pk', 'cid', 'type', 'provider', 'tenant', 'a_side', 'z_side', 'description') + + +class CircuitSearchTable(SearchTable): + cid = tables.LinkColumn(verbose_name='ID') + provider = tables.LinkColumn('circuits:provider', args=[Accessor('provider.slug')]) + tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')]) + + class Meta(SearchTable.Meta): + model = Circuit + fields = ('cid', 'type', 'provider', 'tenant', 'description') diff --git a/netbox/dcim/filters.py b/netbox/dcim/filters.py index 9d9425f9c..6eab5ae34 100644 --- a/netbox/dcim/filters.py +++ b/netbox/dcim/filters.py @@ -8,9 +8,9 @@ from tenancy.models import Tenant from utilities.filters import NullableModelMultipleChoiceFilter, NumericInFilter from .models import ( ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay, - DeviceBayTemplate, DeviceRole, DeviceType, IFACE_FF_LAG, Interface, InterfaceConnection, InterfaceTemplate, - Manufacturer, InventoryItem, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack, RackGroup, - RackReservation, RackRole, Region, Site, VIRTUAL_IFACE_TYPES, + DeviceBayTemplate, DeviceRole, DeviceType, IFACE_FF_LAG, Interface, InterfaceTemplate, Manufacturer, InventoryItem, + Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack, RackGroup, RackReservation, + RackRole, Region, Site, VIRTUAL_IFACE_TYPES, ) diff --git a/netbox/dcim/tables.py b/netbox/dcim/tables.py index c4c7c5b73..b6ebb1be2 100644 --- a/netbox/dcim/tables.py +++ b/netbox/dcim/tables.py @@ -1,7 +1,7 @@ import django_tables2 as tables from django_tables2.utils import Accessor -from utilities.tables import BaseTable, ToggleColumn +from utilities.tables import BaseTable, SearchTable, ToggleColumn from .models import ( ConsolePort, ConsolePortTemplate, ConsoleServerPortTemplate, Device, DeviceBayTemplate, DeviceRole, DeviceType, @@ -136,11 +136,9 @@ class RegionTable(BaseTable): class SiteTable(BaseTable): pk = ToggleColumn() - name = tables.LinkColumn('dcim:site', args=[Accessor('slug')], verbose_name='Name') - facility = tables.Column(verbose_name='Facility') - region = tables.TemplateColumn(template_code=SITE_REGION_LINK, verbose_name='Region') - tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')], verbose_name='Tenant') - asn = tables.Column(verbose_name='ASN') + name = tables.LinkColumn() + region = tables.TemplateColumn(template_code=SITE_REGION_LINK) + tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')]) rack_count = tables.Column(accessor=Accessor('count_racks'), orderable=False, verbose_name='Racks') device_count = tables.Column(accessor=Accessor('count_devices'), orderable=False, verbose_name='Devices') prefix_count = tables.Column(accessor=Accessor('count_prefixes'), orderable=False, verbose_name='Prefixes') @@ -155,6 +153,16 @@ class SiteTable(BaseTable): ) +class SiteSearchTable(SearchTable): + name = tables.LinkColumn() + region = tables.TemplateColumn(template_code=SITE_REGION_LINK) + tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')]) + + class Meta(SearchTable.Meta): + model = Site + fields = ('name', 'facility', 'region', 'tenant', 'asn') + + # # Rack groups # @@ -197,20 +205,33 @@ class RackRoleTable(BaseTable): class RackTable(BaseTable): pk = ToggleColumn() - name = tables.LinkColumn('dcim:rack', args=[Accessor('pk')], verbose_name='Name') - site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')], verbose_name='Site') + name = tables.LinkColumn() + site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')]) group = tables.Column(accessor=Accessor('group.name'), verbose_name='Group') - facility_id = tables.Column(verbose_name='Facility ID') - tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')], verbose_name='Tenant') - role = tables.TemplateColumn(RACK_ROLE, verbose_name='Role') + tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')]) + role = tables.TemplateColumn(RACK_ROLE) u_height = tables.TemplateColumn("{{ record.u_height }}U", verbose_name='Height') - devices = tables.Column(accessor=Accessor('device_count'), verbose_name='Devices') + devices = tables.Column(accessor=Accessor('device_count')) get_utilization = tables.TemplateColumn(UTILIZATION_GRAPH, orderable=False, verbose_name='Utilization') class Meta(BaseTable.Meta): model = Rack - fields = ('pk', 'name', 'site', 'group', 'facility_id', 'tenant', 'role', 'u_height', 'devices', - 'get_utilization') + fields = ( + 'pk', 'name', 'site', 'group', 'facility_id', 'tenant', 'role', 'u_height', 'devices', 'get_utilization' + ) + + +class RackSearchTable(SearchTable): + name = tables.LinkColumn() + site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')]) + group = tables.Column(accessor=Accessor('group.name'), verbose_name='Group') + tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')]) + role = tables.TemplateColumn(RACK_ROLE) + u_height = tables.TemplateColumn("{{ record.u_height }}U", verbose_name='Height') + + class Meta(SearchTable.Meta): + model = Rack + fields = ('name', 'site', 'group', 'facility_id', 'tenant', 'role', 'u_height') class RackImportTable(BaseTable): @@ -249,9 +270,7 @@ class ManufacturerTable(BaseTable): class DeviceTypeTable(BaseTable): pk = ToggleColumn() - manufacturer = tables.Column(verbose_name='Manufacturer') model = tables.LinkColumn('dcim:devicetype', args=[Accessor('pk')], verbose_name='Device Type') - part_number = tables.Column(verbose_name='Part Number') is_full_depth = tables.BooleanColumn(verbose_name='Full Depth') is_console_server = tables.BooleanColumn(verbose_name='CS') is_pdu = tables.BooleanColumn(verbose_name='PDU') @@ -267,6 +286,22 @@ class DeviceTypeTable(BaseTable): ) +class DeviceTypeSearchTable(SearchTable): + model = tables.LinkColumn('dcim:devicetype', args=[Accessor('pk')], verbose_name='Device Type') + is_full_depth = tables.BooleanColumn(verbose_name='Full Depth') + is_console_server = tables.BooleanColumn(verbose_name='CS') + is_pdu = tables.BooleanColumn(verbose_name='PDU') + is_network_device = tables.BooleanColumn(verbose_name='Net') + subdevice_role = tables.TemplateColumn(SUBDEVICE_ROLE_TEMPLATE, verbose_name='Subdevice Role') + + class Meta(SearchTable.Meta): + model = DeviceType + fields = ( + 'model', 'manufacturer', 'part_number', 'u_height', 'is_full_depth', 'is_console_server', 'is_pdu', + 'is_network_device', 'subdevice_role', + ) + + # # Device type components # @@ -373,22 +408,42 @@ class PlatformTable(BaseTable): class DeviceTable(BaseTable): pk = ToggleColumn() + name = tables.TemplateColumn(template_code=DEVICE_LINK) status = tables.TemplateColumn(template_code=STATUS_ICON, verbose_name='') - name = tables.TemplateColumn(template_code=DEVICE_LINK, verbose_name='Name') - tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')], verbose_name='Tenant') - site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')], verbose_name='Site') - rack = tables.LinkColumn('dcim:rack', args=[Accessor('rack.pk')], verbose_name='Rack') + tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')]) + site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')]) + rack = tables.LinkColumn('dcim:rack', args=[Accessor('rack.pk')]) device_role = tables.TemplateColumn(DEVICE_ROLE, verbose_name='Role') - device_type = tables.LinkColumn('dcim:devicetype', args=[Accessor('device_type.pk')], verbose_name='Type', - text=lambda record: record.device_type.full_name) - primary_ip = tables.TemplateColumn(orderable=False, verbose_name='IP Address', - template_code=DEVICE_PRIMARY_IP) + device_type = tables.LinkColumn( + 'dcim:devicetype', args=[Accessor('device_type.pk')], verbose_name='Type', + text=lambda record: record.device_type.full_name + ) + primary_ip = tables.TemplateColumn( + orderable=False, verbose_name='IP Address', template_code=DEVICE_PRIMARY_IP + ) class Meta(BaseTable.Meta): model = Device fields = ('pk', 'name', 'status', 'tenant', 'site', 'rack', 'device_role', 'device_type', 'primary_ip') +class DeviceSearchTable(SearchTable): + name = tables.TemplateColumn(template_code=DEVICE_LINK) + status = tables.TemplateColumn(template_code=STATUS_ICON, verbose_name='') + tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')]) + site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')]) + rack = tables.LinkColumn('dcim:rack', args=[Accessor('rack.pk')]) + device_role = tables.TemplateColumn(DEVICE_ROLE, verbose_name='Role') + device_type = tables.LinkColumn( + 'dcim:devicetype', args=[Accessor('device_type.pk')], verbose_name='Type', + text=lambda record: record.device_type.full_name + ) + + class Meta(SearchTable.Meta): + model = Device + fields = ('name', 'status', 'tenant', 'site', 'rack', 'device_role', 'device_type') + + class DeviceImportTable(BaseTable): name = tables.TemplateColumn(template_code=DEVICE_LINK, verbose_name='Name') tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')], verbose_name='Tenant') diff --git a/netbox/ipam/tables.py b/netbox/ipam/tables.py index 6c99f7d9e..49f87d716 100644 --- a/netbox/ipam/tables.py +++ b/netbox/ipam/tables.py @@ -1,7 +1,7 @@ import django_tables2 as tables from django_tables2.utils import Accessor -from utilities.tables import BaseTable, ToggleColumn +from utilities.tables import BaseTable, SearchTable, ToggleColumn from .models import Aggregate, IPAddress, Prefix, RIR, Role, VLAN, VLANGroup, VRF @@ -133,16 +133,25 @@ TENANT_LINK = """ class VRFTable(BaseTable): pk = ToggleColumn() - name = tables.LinkColumn('ipam:vrf', args=[Accessor('pk')], verbose_name='Name') + name = tables.LinkColumn() rd = tables.Column(verbose_name='RD') - tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')], verbose_name='Tenant') - description = tables.Column(verbose_name='Description') + tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')]) class Meta(BaseTable.Meta): model = VRF fields = ('pk', 'name', 'rd', 'tenant', 'description') +class VRFSearchTable(SearchTable): + name = tables.LinkColumn() + rd = tables.Column(verbose_name='RD') + tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')]) + + class Meta(SearchTable.Meta): + model = VRF + fields = ('name', 'rd', 'tenant', 'description') + + # # RIRs # @@ -177,18 +186,25 @@ class RIRTable(BaseTable): class AggregateTable(BaseTable): pk = ToggleColumn() - prefix = tables.LinkColumn('ipam:aggregate', args=[Accessor('pk')], verbose_name='Aggregate') - rir = tables.Column(verbose_name='RIR') + prefix = tables.LinkColumn(verbose_name='Aggregate') child_count = tables.Column(verbose_name='Prefixes') get_utilization = tables.TemplateColumn(UTILIZATION_GRAPH, orderable=False, verbose_name='Utilization') date_added = tables.DateColumn(format="Y-m-d", verbose_name='Added') - description = tables.Column(verbose_name='Description') class Meta(BaseTable.Meta): model = Aggregate fields = ('pk', 'prefix', 'rir', 'child_count', 'get_utilization', 'date_added', 'description') +class AggregateSearchTable(SearchTable): + prefix = tables.LinkColumn(verbose_name='Aggregate') + date_added = tables.DateColumn(format="Y-m-d", verbose_name='Added') + + class Meta(SearchTable.Meta): + model = Aggregate + fields = ('prefix', 'rir', 'date_added', 'description') + + # # Roles # @@ -212,14 +228,13 @@ class RoleTable(BaseTable): class PrefixTable(BaseTable): pk = ToggleColumn() - status = tables.TemplateColumn(STATUS_LABEL, verbose_name='Status') - prefix = tables.TemplateColumn(PREFIX_LINK, verbose_name='Prefix', attrs={'th': {'style': 'padding-left: 17px'}}) + prefix = tables.TemplateColumn(PREFIX_LINK, attrs={'th': {'style': 'padding-left: 17px'}}) + status = tables.TemplateColumn(STATUS_LABEL) vrf = tables.TemplateColumn(VRF_LINK, verbose_name='VRF') - tenant = tables.TemplateColumn(TENANT_LINK, verbose_name='Tenant') - site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')], verbose_name='Site') + tenant = tables.TemplateColumn(TENANT_LINK) + site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')]) vlan = tables.LinkColumn('ipam:vlan', args=[Accessor('vlan.pk')], verbose_name='VLAN') - role = tables.TemplateColumn(PREFIX_ROLE_LINK, verbose_name='Role') - description = tables.Column(verbose_name='Description') + role = tables.TemplateColumn(PREFIX_ROLE_LINK) class Meta(BaseTable.Meta): model = Prefix @@ -230,12 +245,11 @@ class PrefixTable(BaseTable): class PrefixBriefTable(BaseTable): - prefix = tables.TemplateColumn(PREFIX_LINK_BRIEF, verbose_name='Prefix') - vrf = tables.LinkColumn('ipam:vrf', args=[Accessor('vrf.pk')], default='Global', verbose_name='VRF') - site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')], verbose_name='Site') - status = tables.TemplateColumn(STATUS_LABEL, verbose_name='Status') - vlan = tables.LinkColumn('ipam:vlan', args=[Accessor('vlan.pk')], verbose_name='VLAN') - role = tables.Column(verbose_name='Role') + prefix = tables.TemplateColumn(PREFIX_LINK_BRIEF) + vrf = tables.LinkColumn('ipam:vrf', args=[Accessor('vrf.pk')], default='Global') + site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')]) + status = tables.TemplateColumn(STATUS_LABEL) + vlan = tables.LinkColumn('ipam:vlan', args=[Accessor('vlan.pk')]) class Meta(BaseTable.Meta): model = Prefix @@ -243,6 +257,20 @@ class PrefixBriefTable(BaseTable): orderable = False +class PrefixSearchTable(SearchTable): + prefix = tables.TemplateColumn(PREFIX_LINK, attrs={'th': {'style': 'padding-left: 17px'}}) + status = tables.TemplateColumn(STATUS_LABEL) + vrf = tables.TemplateColumn(VRF_LINK, verbose_name='VRF') + tenant = tables.TemplateColumn(TENANT_LINK) + site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')]) + vlan = tables.LinkColumn('ipam:vlan', args=[Accessor('vlan.pk')], verbose_name='VLAN') + role = tables.TemplateColumn(PREFIX_ROLE_LINK) + + class Meta(SearchTable.Meta): + model = Prefix + fields = ('prefix', 'status', 'vrf', 'tenant', 'site', 'vlan', 'role', 'description') + + # # IPAddresses # @@ -250,13 +278,11 @@ class PrefixBriefTable(BaseTable): class IPAddressTable(BaseTable): pk = ToggleColumn() address = tables.TemplateColumn(IPADDRESS_LINK, verbose_name='IP Address') - status = tables.TemplateColumn(STATUS_LABEL, verbose_name='Status') + status = tables.TemplateColumn(STATUS_LABEL) vrf = tables.TemplateColumn(VRF_LINK, verbose_name='VRF') - tenant = tables.TemplateColumn(TENANT_LINK, verbose_name='Tenant') - device = tables.LinkColumn('dcim:device', args=[Accessor('interface.device.pk')], orderable=False, - verbose_name='Device') - interface = tables.Column(orderable=False, verbose_name='Interface') - description = tables.Column(verbose_name='Description') + tenant = tables.TemplateColumn(TENANT_LINK) + device = tables.LinkColumn('dcim:device', args=[Accessor('interface.device.pk')], orderable=False) + interface = tables.Column(orderable=False) class Meta(BaseTable.Meta): model = IPAddress @@ -268,17 +294,30 @@ class IPAddressTable(BaseTable): class IPAddressBriefTable(BaseTable): address = tables.LinkColumn('ipam:ipaddress', args=[Accessor('pk')], verbose_name='IP Address') - device = tables.LinkColumn('dcim:device', args=[Accessor('interface.device.pk')], orderable=False, - verbose_name='Device') - interface = tables.Column(orderable=False, verbose_name='Interface') - nat_inside = tables.LinkColumn('ipam:ipaddress', args=[Accessor('nat_inside.pk')], orderable=False, - verbose_name='NAT (Inside)') + device = tables.LinkColumn('dcim:device', args=[Accessor('interface.device.pk')], orderable=False) + interface = tables.Column(orderable=False) + nat_inside = tables.LinkColumn( + 'ipam:ipaddress', args=[Accessor('nat_inside.pk')], orderable=False, verbose_name='NAT (Inside)' + ) class Meta(BaseTable.Meta): model = IPAddress fields = ('address', 'device', 'interface', 'nat_inside') +class IPAddressSearchTable(SearchTable): + address = tables.TemplateColumn(IPADDRESS_LINK, verbose_name='IP Address') + status = tables.TemplateColumn(STATUS_LABEL) + vrf = tables.TemplateColumn(VRF_LINK, verbose_name='VRF') + tenant = tables.TemplateColumn(TENANT_LINK) + device = tables.LinkColumn('dcim:device', args=[Accessor('interface.device.pk')], orderable=False) + interface = tables.Column(orderable=False) + + class Meta(SearchTable.Meta): + model = IPAddress + fields = ('address', 'status', 'vrf', 'tenant', 'device', 'interface', 'description') + + # # VLAN groups # @@ -304,15 +343,26 @@ class VLANGroupTable(BaseTable): class VLANTable(BaseTable): pk = ToggleColumn() vid = tables.LinkColumn('ipam:vlan', args=[Accessor('pk')], verbose_name='ID') - site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')], verbose_name='Site') + site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')]) group = tables.Column(accessor=Accessor('group.name'), verbose_name='Group') - name = tables.Column(verbose_name='Name') prefixes = tables.TemplateColumn(VLAN_PREFIXES, orderable=False, verbose_name='Prefixes') - tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')], verbose_name='Tenant') - status = tables.TemplateColumn(STATUS_LABEL, verbose_name='Status') - role = tables.TemplateColumn(VLAN_ROLE_LINK, verbose_name='Role') - description = tables.Column(verbose_name='Description') + tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')]) + status = tables.TemplateColumn(STATUS_LABEL) + role = tables.TemplateColumn(VLAN_ROLE_LINK) class Meta(BaseTable.Meta): model = VLAN fields = ('pk', 'vid', 'site', 'group', 'name', 'prefixes', 'tenant', 'status', 'role', 'description') + + +class VLANSearchTable(SearchTable): + vid = tables.LinkColumn('ipam:vlan', args=[Accessor('pk')], verbose_name='ID') + site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')]) + group = tables.Column(accessor=Accessor('group.name'), verbose_name='Group') + tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')]) + status = tables.TemplateColumn(STATUS_LABEL) + role = tables.TemplateColumn(VLAN_ROLE_LINK) + + class Meta(SearchTable.Meta): + model = VLAN + fields = ('vid', 'site', 'group', 'name', 'tenant', 'status', 'role', 'description') diff --git a/netbox/netbox/views.py b/netbox/netbox/views.py index 12df44e6f..8b4c91d2f 100644 --- a/netbox/netbox/views.py +++ b/netbox/netbox/views.py @@ -4,26 +4,25 @@ from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.reverse import reverse -from django.db.models import Count from django.shortcuts import render from django.views.generic import View from circuits.filters import CircuitFilter, ProviderFilter from circuits.models import Circuit, Provider -from circuits.tables import CircuitTable, ProviderTable +from circuits.tables import CircuitSearchTable, ProviderSearchTable from dcim.filters import DeviceFilter, DeviceTypeFilter, RackFilter, SiteFilter from dcim.models import ConsolePort, Device, DeviceType, InterfaceConnection, PowerPort, Rack, Site -from dcim.tables import DeviceTable, DeviceTypeTable, RackTable, SiteTable +from dcim.tables import DeviceSearchTable, DeviceTypeSearchTable, RackSearchTable, SiteSearchTable from extras.models import UserAction from ipam.filters import AggregateFilter, IPAddressFilter, PrefixFilter, VLANFilter, VRFFilter from ipam.models import Aggregate, IPAddress, Prefix, VLAN, VRF -from ipam.tables import AggregateTable, IPAddressTable, PrefixTable, VLANTable, VRFTable +from ipam.tables import AggregateSearchTable, IPAddressSearchTable, PrefixSearchTable, VLANSearchTable, VRFSearchTable from secrets.filters import SecretFilter from secrets.models import Secret -from secrets.tables import SecretTable +from secrets.tables import SecretSearchTable from tenancy.filters import TenantFilter from tenancy.models import Tenant -from tenancy.tables import TenantTable +from tenancy.tables import TenantSearchTable from .forms import SearchForm @@ -31,89 +30,85 @@ SEARCH_MAX_RESULTS = 15 SEARCH_TYPES = { # Circuits 'provider': { - 'queryset': Provider.objects.annotate(count_circuits=Count('circuits')), + 'queryset': Provider.objects.all(), 'filter': ProviderFilter, - 'table': ProviderTable, + 'table': ProviderSearchTable, 'url': 'circuits:provider_list', }, 'circuit': { - 'queryset': Circuit.objects.select_related('provider', 'type', 'tenant').prefetch_related( - 'terminations__site' - ), + 'queryset': Circuit.objects.select_related('type', 'provider', 'tenant'), 'filter': CircuitFilter, - 'table': CircuitTable, + 'table': CircuitSearchTable, 'url': 'circuits:circuit_list', }, # DCIM 'site': { 'queryset': Site.objects.select_related('region', 'tenant'), 'filter': SiteFilter, - 'table': SiteTable, + 'table': SiteSearchTable, 'url': 'dcim:site_list', }, 'rack': { - 'queryset': Rack.objects.select_related('site', 'group', 'tenant', 'role').prefetch_related('devices__device_type').annotate(device_count=Count('devices', distinct=True)), + 'queryset': Rack.objects.select_related('site', 'group', 'tenant', 'role'), 'filter': RackFilter, - 'table': RackTable, + 'table': RackSearchTable, 'url': 'dcim:rack_list', }, 'devicetype': { - 'queryset': DeviceType.objects.select_related('manufacturer').annotate(instance_count=Count('instances')), + 'queryset': DeviceType.objects.select_related('manufacturer'), 'filter': DeviceTypeFilter, - 'table': DeviceTypeTable, + 'table': DeviceTypeSearchTable, 'url': 'dcim:devicetype_list', }, 'device': { - 'queryset': Device.objects.select_related( - 'device_type__manufacturer', 'device_role', 'tenant', 'site', 'rack', 'primary_ip4', 'primary_ip6' - ), + 'queryset': Device.objects.select_related('device_type__manufacturer', 'device_role', 'tenant', 'site', 'rack'), 'filter': DeviceFilter, - 'table': DeviceTable, + 'table': DeviceSearchTable, 'url': 'dcim:device_list', }, # IPAM 'vrf': { 'queryset': VRF.objects.select_related('tenant'), 'filter': VRFFilter, - 'table': VRFTable, + 'table': VRFSearchTable, 'url': 'ipam:vrf_list', }, 'aggregate': { 'queryset': Aggregate.objects.select_related('rir'), 'filter': AggregateFilter, - 'table': AggregateTable, + 'table': AggregateSearchTable, 'url': 'ipam:aggregate_list', }, 'prefix': { 'queryset': Prefix.objects.select_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role'), 'filter': PrefixFilter, - 'table': PrefixTable, + 'table': PrefixSearchTable, 'url': 'ipam:prefix_list', }, 'ipaddress': { 'queryset': IPAddress.objects.select_related('vrf__tenant', 'tenant', 'interface__device'), 'filter': IPAddressFilter, - 'table': IPAddressTable, + 'table': IPAddressSearchTable, 'url': 'ipam:ipaddress_list', }, 'vlan': { - 'queryset': VLAN.objects.select_related('site', 'group', 'tenant', 'role').prefetch_related('prefixes'), + 'queryset': VLAN.objects.select_related('site', 'group', 'tenant', 'role'), 'filter': VLANFilter, - 'table': VLANTable, + 'table': VLANSearchTable, 'url': 'ipam:vlan_list', }, # Secrets 'secret': { 'queryset': Secret.objects.select_related('role', 'device'), 'filter': SecretFilter, - 'table': SecretTable, + 'table': SecretSearchTable, 'url': 'secrets:secret_list', }, # Tenancy 'tenant': { 'queryset': Tenant.objects.select_related('group'), 'filter': TenantFilter, - 'table': TenantTable, + 'table': TenantSearchTable, 'url': 'tenancy:tenant_list', }, } @@ -180,17 +175,21 @@ class SearchView(View): obj_types = SEARCH_TYPES.keys() for obj_type in obj_types: + queryset = SEARCH_TYPES[obj_type]['queryset'] - filter = SEARCH_TYPES[obj_type]['filter'] + filter_cls = SEARCH_TYPES[obj_type]['filter'] table = SEARCH_TYPES[obj_type]['table'] url = SEARCH_TYPES[obj_type]['url'] - filtered_queryset = filter({'q': form.cleaned_data['q']}, queryset=queryset).qs - total_count = filtered_queryset.count() - if total_count: + + # Construct the results table for this object type + filtered_queryset = filter_cls({'q': form.cleaned_data['q']}, queryset=queryset).qs + table = table(filtered_queryset) + table.paginate(per_page=SEARCH_MAX_RESULTS) + + if table.page: results.append({ 'name': queryset.model._meta.verbose_name_plural, - 'table': table(filtered_queryset[:SEARCH_MAX_RESULTS]), - 'total': total_count, + 'table': table, 'url': '{}?q={}'.format(reverse(url), form.cleaned_data['q']) }) diff --git a/netbox/secrets/tables.py b/netbox/secrets/tables.py index 2fb6d9bbe..15e003d8f 100644 --- a/netbox/secrets/tables.py +++ b/netbox/secrets/tables.py @@ -1,7 +1,7 @@ import django_tables2 as tables from django_tables2.utils import Accessor -from utilities.tables import BaseTable, ToggleColumn +from utilities.tables import BaseTable, SearchTable, ToggleColumn from .models import SecretRole, Secret @@ -36,11 +36,15 @@ class SecretRoleTable(BaseTable): class SecretTable(BaseTable): pk = ToggleColumn() - device = tables.LinkColumn('secrets:secret', args=[Accessor('pk')], verbose_name='Device') - role = tables.Column(verbose_name='Role') - name = tables.Column(verbose_name='Name') - last_updated = tables.DateTimeColumn(verbose_name='Last updated') + device = tables.LinkColumn() class Meta(BaseTable.Meta): model = Secret fields = ('pk', 'device', 'role', 'name', 'last_updated') + + +class SecretSearchTable(SearchTable): + + class Meta(SearchTable.Meta): + model = Secret + fields = ('device', 'role', 'name', 'last_updated') diff --git a/netbox/templates/search.html b/netbox/templates/search.html index 5e72e3396..e0c60003a 100644 --- a/netbox/templates/search.html +++ b/netbox/templates/search.html @@ -1,6 +1,8 @@ {% extends '_base.html' %} {% load form_helpers %} +{% block title %}Search{% endblock %} + {% block content %} {% if request.GET.q %} {% include 'search_form.html' with search_form=form %} @@ -8,33 +10,31 @@
{% for obj_type in results %} -

{{ obj_type.name|title }}

- {% include 'table.html' with table=obj_type.table %} - {% if obj_type.total > obj_type.table.rows|length %} +

{{ obj_type.name }}

+ {% include 'table.html' with table=obj_type.table hide_paginator=True %} + {% if obj_type.table.page.has_next %} - All {{ obj_type.total }} results + All {{ obj_type.table.page.paginator.count }} results {% endif %}
{% endfor %}
- {% if results %} -
-
- Search Results -
-
- {% for obj_type in results %} - - {{ obj_type.name|title }} - {{ obj_type.total }} - - {% endfor %} -
+
+
+ Search Results
- {% endif %} +
+ {% for obj_type in results %} + + {{ obj_type.name }} + {{ obj_type.table.page.paginator.count }} + + {% endfor %} +
+
{% else %} diff --git a/netbox/templates/table.html b/netbox/templates/table.html index 8782f0796..4792f8e68 100644 --- a/netbox/templates/table.html +++ b/netbox/templates/table.html @@ -4,5 +4,7 @@ {# Extends the stock django_tables2 template to provide custom formatting of the pagination controls #} {% block pagination %} - {% include 'paginator.html' %} + {% if not hide_paginator %} + {% include 'paginator.html' %} + {% endif %} {% endblock pagination %} diff --git a/netbox/tenancy/tables.py b/netbox/tenancy/tables.py index 0ce0d577c..bacb4c12f 100644 --- a/netbox/tenancy/tables.py +++ b/netbox/tenancy/tables.py @@ -1,7 +1,7 @@ import django_tables2 as tables from django_tables2.utils import Accessor -from utilities.tables import BaseTable, ToggleColumn +from utilities.tables import BaseTable, SearchTable, ToggleColumn from .models import Tenant, TenantGroup @@ -36,10 +36,15 @@ class TenantGroupTable(BaseTable): class TenantTable(BaseTable): pk = ToggleColumn() - name = tables.LinkColumn('tenancy:tenant', args=[Accessor('slug')], verbose_name='Name') - group = tables.Column(verbose_name='Group') - description = tables.Column(verbose_name='Description') + name = tables.LinkColumn() class Meta(BaseTable.Meta): model = Tenant fields = ('pk', 'name', 'group', 'description') + + +class TenantSearchTable(SearchTable): + + class Meta(SearchTable.Meta): + model = Tenant + fields = ('name', 'group', 'description') diff --git a/netbox/utilities/tables.py b/netbox/utilities/tables.py index a53227937..279a7310b 100644 --- a/netbox/utilities/tables.py +++ b/netbox/utilities/tables.py @@ -4,7 +4,9 @@ from django.utils.safestring import mark_safe class BaseTable(tables.Table): - + """ + Default table for object lists + """ def __init__(self, *args, **kwargs): super(BaseTable, self).__init__(*args, **kwargs) @@ -18,6 +20,17 @@ class BaseTable(tables.Table): } +class SearchTable(tables.Table): + """ + Default table for search results + """ + class Meta: + attrs = { + 'class': 'table table-hover', + } + orderable = False + + class ToggleColumn(tables.CheckBoxColumn): def __init__(self, *args, **kwargs):