Browse Source

Introduced SearchTable for improved performance

Jeremy Stretch 8 years ago
parent
commit
a5dc91c175

+ 35 - 17
netbox/circuits/tables.py

@@ -1,7 +1,7 @@
 import django_tables2 as tables
 import django_tables2 as tables
 from django_tables2.utils import Accessor
 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
 from .models import Circuit, CircuitType, Provider
 
 
@@ -19,9 +19,7 @@ CIRCUITTYPE_ACTIONS = """
 
 
 class ProviderTable(BaseTable):
 class ProviderTable(BaseTable):
     pk = ToggleColumn()
     pk = ToggleColumn()
-    name = tables.LinkColumn('circuits:provider', args=[Accessor('slug')], verbose_name='Name')
+    name = tables.LinkColumn()
-    asn = tables.Column(verbose_name='ASN')
-    account = tables.Column(verbose_name='Account')
     circuit_count = tables.Column(accessor=Accessor('count_circuits'), verbose_name='Circuits')
     circuit_count = tables.Column(accessor=Accessor('count_circuits'), verbose_name='Circuits')
 
 
     class Meta(BaseTable.Meta):
     class Meta(BaseTable.Meta):
@@ -29,17 +27,25 @@ class ProviderTable(BaseTable):
         fields = ('pk', 'name', 'asn', 'account', 'circuit_count')
         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
 # Circuit types
 #
 #
 
 
 class CircuitTypeTable(BaseTable):
 class CircuitTypeTable(BaseTable):
     pk = ToggleColumn()
     pk = ToggleColumn()
-    name = tables.LinkColumn(verbose_name='Name')
+    name = tables.LinkColumn()
     circuit_count = tables.Column(verbose_name='Circuits')
     circuit_count = tables.Column(verbose_name='Circuits')
-    slug = tables.Column(verbose_name='Slug')
+    actions = tables.TemplateColumn(
-    actions = tables.TemplateColumn(template_code=CIRCUITTYPE_ACTIONS, attrs={'td': {'class': 'text-right'}},
+        template_code=CIRCUITTYPE_ACTIONS, attrs={'td': {'class': 'text-right'}}, verbose_name=''
-                                    verbose_name='')
+    )
 
 
     class Meta(BaseTable.Meta):
     class Meta(BaseTable.Meta):
         model = CircuitType
         model = CircuitType
@@ -52,16 +58,28 @@ class CircuitTypeTable(BaseTable):
 
 
 class CircuitTable(BaseTable):
 class CircuitTable(BaseTable):
     pk = ToggleColumn()
     pk = ToggleColumn()
-    cid = tables.LinkColumn('circuits:circuit', args=[Accessor('pk')], verbose_name='ID')
+    cid = tables.LinkColumn(verbose_name='ID')
-    type = tables.Column(verbose_name='Type')
+    provider = tables.LinkColumn('circuits:provider', args=[Accessor('provider.slug')])
-    provider = tables.LinkColumn('circuits:provider', args=[Accessor('provider.slug')], verbose_name='Provider')
+    tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')])
-    tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')], verbose_name='Tenant')
+    a_side = tables.LinkColumn(
-    a_side = tables.LinkColumn('dcim:site', accessor=Accessor('termination_a.site'), orderable=False,
+        'dcim:site', accessor=Accessor('termination_a.site'), orderable=False,
-                               args=[Accessor('termination_a.site.slug')])
+        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')])
+    z_side = tables.LinkColumn(
-    description = tables.Column(verbose_name='Description')
+        'dcim:site', accessor=Accessor('termination_z.site'), orderable=False,
+        args=[Accessor('termination_z.site.slug')]
+    )
 
 
     class Meta(BaseTable.Meta):
     class Meta(BaseTable.Meta):
         model = Circuit
         model = Circuit
         fields = ('pk', 'cid', 'type', 'provider', 'tenant', 'a_side', 'z_side', 'description')
         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')

+ 3 - 3
netbox/dcim/filters.py

@@ -8,9 +8,9 @@ from tenancy.models import Tenant
 from utilities.filters import NullableModelMultipleChoiceFilter, NumericInFilter
 from utilities.filters import NullableModelMultipleChoiceFilter, NumericInFilter
 from .models import (
 from .models import (
     ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
     ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
-    DeviceBayTemplate, DeviceRole, DeviceType, IFACE_FF_LAG, Interface, InterfaceConnection, InterfaceTemplate,
+    DeviceBayTemplate, DeviceRole, DeviceType, IFACE_FF_LAG, Interface, InterfaceTemplate, Manufacturer, InventoryItem,
-    Manufacturer, InventoryItem, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack, RackGroup,
+    Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack, RackGroup, RackReservation,
-    RackReservation, RackRole, Region, Site, VIRTUAL_IFACE_TYPES,
+    RackRole, Region, Site, VIRTUAL_IFACE_TYPES,
 )
 )
 
 
 
 

+ 79 - 24
netbox/dcim/tables.py

@@ -1,7 +1,7 @@
 import django_tables2 as tables
 import django_tables2 as tables
 from django_tables2.utils import Accessor
 from django_tables2.utils import Accessor
 
 
-from utilities.tables import BaseTable, ToggleColumn
+from utilities.tables import BaseTable, SearchTable, ToggleColumn
 
 
 from .models import (
 from .models import (
     ConsolePort, ConsolePortTemplate, ConsoleServerPortTemplate, Device, DeviceBayTemplate, DeviceRole, DeviceType,
     ConsolePort, ConsolePortTemplate, ConsoleServerPortTemplate, Device, DeviceBayTemplate, DeviceRole, DeviceType,
@@ -136,11 +136,9 @@ class RegionTable(BaseTable):
 
 
 class SiteTable(BaseTable):
 class SiteTable(BaseTable):
     pk = ToggleColumn()
     pk = ToggleColumn()
-    name = tables.LinkColumn('dcim:site', args=[Accessor('slug')], verbose_name='Name')
+    name = tables.LinkColumn()
-    facility = tables.Column(verbose_name='Facility')
+    region = tables.TemplateColumn(template_code=SITE_REGION_LINK)
-    region = tables.TemplateColumn(template_code=SITE_REGION_LINK, verbose_name='Region')
+    tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')])
-    tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')], verbose_name='Tenant')
-    asn = tables.Column(verbose_name='ASN')
     rack_count = tables.Column(accessor=Accessor('count_racks'), orderable=False, verbose_name='Racks')
     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')
     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')
     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
 # Rack groups
 #
 #
@@ -197,20 +205,33 @@ class RackRoleTable(BaseTable):
 
 
 class RackTable(BaseTable):
 class RackTable(BaseTable):
     pk = ToggleColumn()
     pk = ToggleColumn()
-    name = tables.LinkColumn('dcim:rack', args=[Accessor('pk')], verbose_name='Name')
+    name = tables.LinkColumn()
-    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')
     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')])
-    tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')], verbose_name='Tenant')
+    role = tables.TemplateColumn(RACK_ROLE)
-    role = tables.TemplateColumn(RACK_ROLE, verbose_name='Role')
     u_height = tables.TemplateColumn("{{ record.u_height }}U", verbose_name='Height')
     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')
     get_utilization = tables.TemplateColumn(UTILIZATION_GRAPH, orderable=False, verbose_name='Utilization')
 
 
     class Meta(BaseTable.Meta):
     class Meta(BaseTable.Meta):
         model = Rack
         model = Rack
-        fields = ('pk', 'name', 'site', 'group', 'facility_id', 'tenant', 'role', 'u_height', 'devices',
+        fields = (
-                  'get_utilization')
+            '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):
 class RackImportTable(BaseTable):
@@ -249,9 +270,7 @@ class ManufacturerTable(BaseTable):
 
 
 class DeviceTypeTable(BaseTable):
 class DeviceTypeTable(BaseTable):
     pk = ToggleColumn()
     pk = ToggleColumn()
-    manufacturer = tables.Column(verbose_name='Manufacturer')
     model = tables.LinkColumn('dcim:devicetype', args=[Accessor('pk')], verbose_name='Device Type')
     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_full_depth = tables.BooleanColumn(verbose_name='Full Depth')
     is_console_server = tables.BooleanColumn(verbose_name='CS')
     is_console_server = tables.BooleanColumn(verbose_name='CS')
     is_pdu = tables.BooleanColumn(verbose_name='PDU')
     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
 # Device type components
 #
 #
@@ -373,22 +408,42 @@ class PlatformTable(BaseTable):
 
 
 class DeviceTable(BaseTable):
 class DeviceTable(BaseTable):
     pk = ToggleColumn()
     pk = ToggleColumn()
+    name = tables.TemplateColumn(template_code=DEVICE_LINK)
     status = tables.TemplateColumn(template_code=STATUS_ICON, verbose_name='')
     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')])
-    tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')], verbose_name='Tenant')
+    site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])
-    site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')], verbose_name='Site')
+    rack = tables.LinkColumn('dcim:rack', args=[Accessor('rack.pk')])
-    rack = tables.LinkColumn('dcim:rack', args=[Accessor('rack.pk')], verbose_name='Rack')
     device_role = tables.TemplateColumn(DEVICE_ROLE, verbose_name='Role')
     device_role = tables.TemplateColumn(DEVICE_ROLE, verbose_name='Role')
-    device_type = tables.LinkColumn('dcim:devicetype', args=[Accessor('device_type.pk')], verbose_name='Type',
+    device_type = tables.LinkColumn(
-                                    text=lambda record: record.device_type.full_name)
+        'dcim:devicetype', args=[Accessor('device_type.pk')], verbose_name='Type',
-    primary_ip = tables.TemplateColumn(orderable=False, verbose_name='IP Address',
+        text=lambda record: record.device_type.full_name
-                                       template_code=DEVICE_PRIMARY_IP)
+    )
+    primary_ip = tables.TemplateColumn(
+        orderable=False, verbose_name='IP Address', template_code=DEVICE_PRIMARY_IP
+    )
 
 
     class Meta(BaseTable.Meta):
     class Meta(BaseTable.Meta):
         model = Device
         model = Device
         fields = ('pk', 'name', 'status', 'tenant', 'site', 'rack', 'device_role', 'device_type', 'primary_ip')
         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):
 class DeviceImportTable(BaseTable):
     name = tables.TemplateColumn(template_code=DEVICE_LINK, verbose_name='Name')
     name = tables.TemplateColumn(template_code=DEVICE_LINK, verbose_name='Name')
     tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')], verbose_name='Tenant')
     tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')], verbose_name='Tenant')

+ 86 - 36
netbox/ipam/tables.py

@@ -1,7 +1,7 @@
 import django_tables2 as tables
 import django_tables2 as tables
 from django_tables2.utils import Accessor
 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
 from .models import Aggregate, IPAddress, Prefix, RIR, Role, VLAN, VLANGroup, VRF
 
 
@@ -133,16 +133,25 @@ TENANT_LINK = """
 
 
 class VRFTable(BaseTable):
 class VRFTable(BaseTable):
     pk = ToggleColumn()
     pk = ToggleColumn()
-    name = tables.LinkColumn('ipam:vrf', args=[Accessor('pk')], verbose_name='Name')
+    name = tables.LinkColumn()
     rd = tables.Column(verbose_name='RD')
     rd = tables.Column(verbose_name='RD')
-    tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')], verbose_name='Tenant')
+    tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')])
-    description = tables.Column(verbose_name='Description')
 
 
     class Meta(BaseTable.Meta):
     class Meta(BaseTable.Meta):
         model = VRF
         model = VRF
         fields = ('pk', 'name', 'rd', 'tenant', 'description')
         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
 # RIRs
 #
 #
@@ -177,18 +186,25 @@ class RIRTable(BaseTable):
 
 
 class AggregateTable(BaseTable):
 class AggregateTable(BaseTable):
     pk = ToggleColumn()
     pk = ToggleColumn()
-    prefix = tables.LinkColumn('ipam:aggregate', args=[Accessor('pk')], verbose_name='Aggregate')
+    prefix = tables.LinkColumn(verbose_name='Aggregate')
-    rir = tables.Column(verbose_name='RIR')
     child_count = tables.Column(verbose_name='Prefixes')
     child_count = tables.Column(verbose_name='Prefixes')
     get_utilization = tables.TemplateColumn(UTILIZATION_GRAPH, orderable=False, verbose_name='Utilization')
     get_utilization = tables.TemplateColumn(UTILIZATION_GRAPH, orderable=False, verbose_name='Utilization')
     date_added = tables.DateColumn(format="Y-m-d", verbose_name='Added')
     date_added = tables.DateColumn(format="Y-m-d", verbose_name='Added')
-    description = tables.Column(verbose_name='Description')
 
 
     class Meta(BaseTable.Meta):
     class Meta(BaseTable.Meta):
         model = Aggregate
         model = Aggregate
         fields = ('pk', 'prefix', 'rir', 'child_count', 'get_utilization', 'date_added', 'description')
         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
 # Roles
 #
 #
@@ -212,14 +228,13 @@ class RoleTable(BaseTable):
 
 
 class PrefixTable(BaseTable):
 class PrefixTable(BaseTable):
     pk = ToggleColumn()
     pk = ToggleColumn()
-    status = tables.TemplateColumn(STATUS_LABEL, verbose_name='Status')
+    prefix = tables.TemplateColumn(PREFIX_LINK, attrs={'th': {'style': 'padding-left: 17px'}})
-    prefix = tables.TemplateColumn(PREFIX_LINK, verbose_name='Prefix', attrs={'th': {'style': 'padding-left: 17px'}})
+    status = tables.TemplateColumn(STATUS_LABEL)
     vrf = tables.TemplateColumn(VRF_LINK, verbose_name='VRF')
     vrf = tables.TemplateColumn(VRF_LINK, verbose_name='VRF')
-    tenant = tables.TemplateColumn(TENANT_LINK, verbose_name='Tenant')
+    tenant = tables.TemplateColumn(TENANT_LINK)
-    site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')], verbose_name='Site')
+    site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])
     vlan = tables.LinkColumn('ipam:vlan', args=[Accessor('vlan.pk')], verbose_name='VLAN')
     vlan = tables.LinkColumn('ipam:vlan', args=[Accessor('vlan.pk')], verbose_name='VLAN')
-    role = tables.TemplateColumn(PREFIX_ROLE_LINK, verbose_name='Role')
+    role = tables.TemplateColumn(PREFIX_ROLE_LINK)
-    description = tables.Column(verbose_name='Description')
 
 
     class Meta(BaseTable.Meta):
     class Meta(BaseTable.Meta):
         model = Prefix
         model = Prefix
@@ -230,12 +245,11 @@ class PrefixTable(BaseTable):
 
 
 
 
 class PrefixBriefTable(BaseTable):
 class PrefixBriefTable(BaseTable):
-    prefix = tables.TemplateColumn(PREFIX_LINK_BRIEF, verbose_name='Prefix')
+    prefix = tables.TemplateColumn(PREFIX_LINK_BRIEF)
-    vrf = tables.LinkColumn('ipam:vrf', args=[Accessor('vrf.pk')], default='Global', verbose_name='VRF')
+    vrf = tables.LinkColumn('ipam:vrf', args=[Accessor('vrf.pk')], default='Global')
-    site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')], verbose_name='Site')
+    site = tables.LinkColumn('dcim:site', args=[Accessor('site.slug')])
-    status = tables.TemplateColumn(STATUS_LABEL, verbose_name='Status')
+    status = tables.TemplateColumn(STATUS_LABEL)
-    vlan = tables.LinkColumn('ipam:vlan', args=[Accessor('vlan.pk')], verbose_name='VLAN')
+    vlan = tables.LinkColumn('ipam:vlan', args=[Accessor('vlan.pk')])
-    role = tables.Column(verbose_name='Role')
 
 
     class Meta(BaseTable.Meta):
     class Meta(BaseTable.Meta):
         model = Prefix
         model = Prefix
@@ -243,6 +257,20 @@ class PrefixBriefTable(BaseTable):
         orderable = False
         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
 # IPAddresses
 #
 #
@@ -250,13 +278,11 @@ class PrefixBriefTable(BaseTable):
 class IPAddressTable(BaseTable):
 class IPAddressTable(BaseTable):
     pk = ToggleColumn()
     pk = ToggleColumn()
     address = tables.TemplateColumn(IPADDRESS_LINK, verbose_name='IP Address')
     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')
     vrf = tables.TemplateColumn(VRF_LINK, verbose_name='VRF')
-    tenant = tables.TemplateColumn(TENANT_LINK, verbose_name='Tenant')
+    tenant = tables.TemplateColumn(TENANT_LINK)
-    device = tables.LinkColumn('dcim:device', args=[Accessor('interface.device.pk')], orderable=False,
+    device = tables.LinkColumn('dcim:device', args=[Accessor('interface.device.pk')], orderable=False)
-                               verbose_name='Device')
+    interface = tables.Column(orderable=False)
-    interface = tables.Column(orderable=False, verbose_name='Interface')
-    description = tables.Column(verbose_name='Description')
 
 
     class Meta(BaseTable.Meta):
     class Meta(BaseTable.Meta):
         model = IPAddress
         model = IPAddress
@@ -268,17 +294,30 @@ class IPAddressTable(BaseTable):
 
 
 class IPAddressBriefTable(BaseTable):
 class IPAddressBriefTable(BaseTable):
     address = tables.LinkColumn('ipam:ipaddress', args=[Accessor('pk')], verbose_name='IP Address')
     address = tables.LinkColumn('ipam:ipaddress', args=[Accessor('pk')], verbose_name='IP Address')
-    device = tables.LinkColumn('dcim:device', args=[Accessor('interface.device.pk')], orderable=False,
+    device = tables.LinkColumn('dcim:device', args=[Accessor('interface.device.pk')], orderable=False)
-                               verbose_name='Device')
+    interface = tables.Column(orderable=False)
-    interface = tables.Column(orderable=False, verbose_name='Interface')
+    nat_inside = tables.LinkColumn(
-    nat_inside = tables.LinkColumn('ipam:ipaddress', args=[Accessor('nat_inside.pk')], orderable=False,
+        'ipam:ipaddress', args=[Accessor('nat_inside.pk')], orderable=False, verbose_name='NAT (Inside)'
-                                   verbose_name='NAT (Inside)')
+    )
 
 
     class Meta(BaseTable.Meta):
     class Meta(BaseTable.Meta):
         model = IPAddress
         model = IPAddress
         fields = ('address', 'device', 'interface', 'nat_inside')
         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
 # VLAN groups
 #
 #
@@ -304,15 +343,26 @@ class VLANGroupTable(BaseTable):
 class VLANTable(BaseTable):
 class VLANTable(BaseTable):
     pk = ToggleColumn()
     pk = ToggleColumn()
     vid = tables.LinkColumn('ipam:vlan', args=[Accessor('pk')], verbose_name='ID')
     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')
     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')
     prefixes = tables.TemplateColumn(VLAN_PREFIXES, orderable=False, verbose_name='Prefixes')
-    tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')], verbose_name='Tenant')
+    tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')])
-    status = tables.TemplateColumn(STATUS_LABEL, verbose_name='Status')
+    status = tables.TemplateColumn(STATUS_LABEL)
-    role = tables.TemplateColumn(VLAN_ROLE_LINK, verbose_name='Role')
+    role = tables.TemplateColumn(VLAN_ROLE_LINK)
-    description = tables.Column(verbose_name='Description')
 
 
     class Meta(BaseTable.Meta):
     class Meta(BaseTable.Meta):
         model = VLAN
         model = VLAN
         fields = ('pk', 'vid', 'site', 'group', 'name', 'prefixes', 'tenant', 'status', 'role', 'description')
         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')

+ 34 - 35
netbox/netbox/views.py

@@ -4,26 +4,25 @@ from rest_framework.views import APIView
 from rest_framework.response import Response
 from rest_framework.response import Response
 from rest_framework.reverse import reverse
 from rest_framework.reverse import reverse
 
 
-from django.db.models import Count
 from django.shortcuts import render
 from django.shortcuts import render
 from django.views.generic import View
 from django.views.generic import View
 
 
 from circuits.filters import CircuitFilter, ProviderFilter
 from circuits.filters import CircuitFilter, ProviderFilter
 from circuits.models import Circuit, Provider
 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.filters import DeviceFilter, DeviceTypeFilter, RackFilter, SiteFilter
 from dcim.models import ConsolePort, Device, DeviceType, InterfaceConnection, PowerPort, Rack, Site
 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 extras.models import UserAction
 from ipam.filters import AggregateFilter, IPAddressFilter, PrefixFilter, VLANFilter, VRFFilter
 from ipam.filters import AggregateFilter, IPAddressFilter, PrefixFilter, VLANFilter, VRFFilter
 from ipam.models import Aggregate, IPAddress, Prefix, VLAN, VRF
 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.filters import SecretFilter
 from secrets.models import Secret
 from secrets.models import Secret
-from secrets.tables import SecretTable
+from secrets.tables import SecretSearchTable
 from tenancy.filters import TenantFilter
 from tenancy.filters import TenantFilter
 from tenancy.models import Tenant
 from tenancy.models import Tenant
-from tenancy.tables import TenantTable
+from tenancy.tables import TenantSearchTable
 from .forms import SearchForm
 from .forms import SearchForm
 
 
 
 
@@ -31,89 +30,85 @@ SEARCH_MAX_RESULTS = 15
 SEARCH_TYPES = {
 SEARCH_TYPES = {
     # Circuits
     # Circuits
     'provider': {
     'provider': {
-        'queryset': Provider.objects.annotate(count_circuits=Count('circuits')),
+        'queryset': Provider.objects.all(),
         'filter': ProviderFilter,
         'filter': ProviderFilter,
-        'table': ProviderTable,
+        'table': ProviderSearchTable,
         'url': 'circuits:provider_list',
         'url': 'circuits:provider_list',
     },
     },
     'circuit': {
     'circuit': {
-        'queryset': Circuit.objects.select_related('provider', 'type', 'tenant').prefetch_related(
+        'queryset': Circuit.objects.select_related('type', 'provider', 'tenant'),
-            'terminations__site'
-        ),
         'filter': CircuitFilter,
         'filter': CircuitFilter,
-        'table': CircuitTable,
+        'table': CircuitSearchTable,
         'url': 'circuits:circuit_list',
         'url': 'circuits:circuit_list',
     },
     },
     # DCIM
     # DCIM
     'site': {
     'site': {
         'queryset': Site.objects.select_related('region', 'tenant'),
         'queryset': Site.objects.select_related('region', 'tenant'),
         'filter': SiteFilter,
         'filter': SiteFilter,
-        'table': SiteTable,
+        'table': SiteSearchTable,
         'url': 'dcim:site_list',
         'url': 'dcim:site_list',
     },
     },
     'rack': {
     '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,
         'filter': RackFilter,
-        'table': RackTable,
+        'table': RackSearchTable,
         'url': 'dcim:rack_list',
         'url': 'dcim:rack_list',
     },
     },
     'devicetype': {
     'devicetype': {
-        'queryset': DeviceType.objects.select_related('manufacturer').annotate(instance_count=Count('instances')),
+        'queryset': DeviceType.objects.select_related('manufacturer'),
         'filter': DeviceTypeFilter,
         'filter': DeviceTypeFilter,
-        'table': DeviceTypeTable,
+        'table': DeviceTypeSearchTable,
         'url': 'dcim:devicetype_list',
         'url': 'dcim:devicetype_list',
     },
     },
     'device': {
     'device': {
-        'queryset': Device.objects.select_related(
+        'queryset': Device.objects.select_related('device_type__manufacturer', 'device_role', 'tenant', 'site', 'rack'),
-            'device_type__manufacturer', 'device_role', 'tenant', 'site', 'rack', 'primary_ip4', 'primary_ip6'
-        ),
         'filter': DeviceFilter,
         'filter': DeviceFilter,
-        'table': DeviceTable,
+        'table': DeviceSearchTable,
         'url': 'dcim:device_list',
         'url': 'dcim:device_list',
     },
     },
     # IPAM
     # IPAM
     'vrf': {
     'vrf': {
         'queryset': VRF.objects.select_related('tenant'),
         'queryset': VRF.objects.select_related('tenant'),
         'filter': VRFFilter,
         'filter': VRFFilter,
-        'table': VRFTable,
+        'table': VRFSearchTable,
         'url': 'ipam:vrf_list',
         'url': 'ipam:vrf_list',
     },
     },
     'aggregate': {
     'aggregate': {
         'queryset': Aggregate.objects.select_related('rir'),
         'queryset': Aggregate.objects.select_related('rir'),
         'filter': AggregateFilter,
         'filter': AggregateFilter,
-        'table': AggregateTable,
+        'table': AggregateSearchTable,
         'url': 'ipam:aggregate_list',
         'url': 'ipam:aggregate_list',
     },
     },
     'prefix': {
     'prefix': {
         'queryset': Prefix.objects.select_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role'),
         'queryset': Prefix.objects.select_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role'),
         'filter': PrefixFilter,
         'filter': PrefixFilter,
-        'table': PrefixTable,
+        'table': PrefixSearchTable,
         'url': 'ipam:prefix_list',
         'url': 'ipam:prefix_list',
     },
     },
     'ipaddress': {
     'ipaddress': {
         'queryset': IPAddress.objects.select_related('vrf__tenant', 'tenant', 'interface__device'),
         'queryset': IPAddress.objects.select_related('vrf__tenant', 'tenant', 'interface__device'),
         'filter': IPAddressFilter,
         'filter': IPAddressFilter,
-        'table': IPAddressTable,
+        'table': IPAddressSearchTable,
         'url': 'ipam:ipaddress_list',
         'url': 'ipam:ipaddress_list',
     },
     },
     'vlan': {
     'vlan': {
-        'queryset': VLAN.objects.select_related('site', 'group', 'tenant', 'role').prefetch_related('prefixes'),
+        'queryset': VLAN.objects.select_related('site', 'group', 'tenant', 'role'),
         'filter': VLANFilter,
         'filter': VLANFilter,
-        'table': VLANTable,
+        'table': VLANSearchTable,
         'url': 'ipam:vlan_list',
         'url': 'ipam:vlan_list',
     },
     },
     # Secrets
     # Secrets
     'secret': {
     'secret': {
         'queryset': Secret.objects.select_related('role', 'device'),
         'queryset': Secret.objects.select_related('role', 'device'),
         'filter': SecretFilter,
         'filter': SecretFilter,
-        'table': SecretTable,
+        'table': SecretSearchTable,
         'url': 'secrets:secret_list',
         'url': 'secrets:secret_list',
     },
     },
     # Tenancy
     # Tenancy
     'tenant': {
     'tenant': {
         'queryset': Tenant.objects.select_related('group'),
         'queryset': Tenant.objects.select_related('group'),
         'filter': TenantFilter,
         'filter': TenantFilter,
-        'table': TenantTable,
+        'table': TenantSearchTable,
         'url': 'tenancy:tenant_list',
         'url': 'tenancy:tenant_list',
     },
     },
 }
 }
@@ -180,17 +175,21 @@ class SearchView(View):
                 obj_types = SEARCH_TYPES.keys()
                 obj_types = SEARCH_TYPES.keys()
 
 
             for obj_type in obj_types:
             for obj_type in obj_types:
+
                 queryset = SEARCH_TYPES[obj_type]['queryset']
                 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']
                 table = SEARCH_TYPES[obj_type]['table']
                 url = SEARCH_TYPES[obj_type]['url']
                 url = SEARCH_TYPES[obj_type]['url']
-                filtered_queryset = filter({'q': form.cleaned_data['q']}, queryset=queryset).qs
+
-                total_count = filtered_queryset.count()
+                # Construct the results table for this object type
-                if total_count:
+                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({
                     results.append({
                         'name': queryset.model._meta.verbose_name_plural,
                         'name': queryset.model._meta.verbose_name_plural,
-                        'table': table(filtered_queryset[:SEARCH_MAX_RESULTS]),
+                        'table': table,
-                        'total': total_count,
                         'url': '{}?q={}'.format(reverse(url), form.cleaned_data['q'])
                         'url': '{}?q={}'.format(reverse(url), form.cleaned_data['q'])
                     })
                     })
 
 

+ 9 - 5
netbox/secrets/tables.py

@@ -1,7 +1,7 @@
 import django_tables2 as tables
 import django_tables2 as tables
 from django_tables2.utils import Accessor
 from django_tables2.utils import Accessor
 
 
-from utilities.tables import BaseTable, ToggleColumn
+from utilities.tables import BaseTable, SearchTable, ToggleColumn
 
 
 from .models import SecretRole, Secret
 from .models import SecretRole, Secret
 
 
@@ -36,11 +36,15 @@ class SecretRoleTable(BaseTable):
 
 
 class SecretTable(BaseTable):
 class SecretTable(BaseTable):
     pk = ToggleColumn()
     pk = ToggleColumn()
-    device = tables.LinkColumn('secrets:secret', args=[Accessor('pk')], verbose_name='Device')
+    device = tables.LinkColumn()
-    role = tables.Column(verbose_name='Role')
-    name = tables.Column(verbose_name='Name')
-    last_updated = tables.DateTimeColumn(verbose_name='Last updated')
 
 
     class Meta(BaseTable.Meta):
     class Meta(BaseTable.Meta):
         model = Secret
         model = Secret
         fields = ('pk', 'device', 'role', 'name', 'last_updated')
         fields = ('pk', 'device', 'role', 'name', 'last_updated')
+
+
+class SecretSearchTable(SearchTable):
+
+    class Meta(SearchTable.Meta):
+        model = Secret
+        fields = ('device', 'role', 'name', 'last_updated')

+ 18 - 18
netbox/templates/search.html

@@ -1,6 +1,8 @@
 {% extends '_base.html' %}
 {% extends '_base.html' %}
 {% load form_helpers %}
 {% load form_helpers %}
 
 
+{% block title %}Search{% endblock %}
+
 {% block content %}
 {% block content %}
     {% if request.GET.q %}
     {% if request.GET.q %}
         {% include 'search_form.html' with search_form=form %}
         {% include 'search_form.html' with search_form=form %}
@@ -8,33 +10,31 @@
             <div class="row">
             <div class="row">
                 <div class="col-md-10">
                 <div class="col-md-10">
                     {% for obj_type in results %}
                     {% for obj_type in results %}
-                        <h3 id="{{ obj_type.name }}">{{ obj_type.name|title }}</h3>
+                        <h3 id="{{ obj_type.name }}">{{ obj_type.name }}</h3>
-                        {% include 'table.html' with table=obj_type.table %}
+                        {% include 'table.html' with table=obj_type.table hide_paginator=True %}
-                        {% if obj_type.total > obj_type.table.rows|length %}
+                        {% if obj_type.table.page.has_next %}
                             <a href="{{ obj_type.url }}" class="btn btn-primary pull-right">
                             <a href="{{ obj_type.url }}" class="btn btn-primary pull-right">
                                 <span class="fa fa-search" aria-hidden="true"></span>
                                 <span class="fa fa-search" aria-hidden="true"></span>
-                                All {{ obj_type.total }} results
+                                All {{ obj_type.table.page.paginator.count }} results
                             </a>
                             </a>
                         {% endif %}
                         {% endif %}
                     <div class="clearfix"></div>
                     <div class="clearfix"></div>
                     {% endfor %}
                     {% endfor %}
                 </div>
                 </div>
                 <div class="col-md-2" style="padding-top: 20px;">
                 <div class="col-md-2" style="padding-top: 20px;">
-                    {% if results %}
+                    <div class="panel panel-default">
-                        <div class="panel panel-default">
+                        <div class="panel-heading">
-                            <div class="panel-heading">
+                            <strong>Search Results</strong>
-                                <strong>Search Results</strong>
-                            </div>
-                            <div class="list-group">
-                                {% for obj_type in results %}
-                                    <a href="#{{ obj_type.name }}" class="list-group-item">
-                                        {{ obj_type.name|title }}
-                                        <span class="badge">{{ obj_type.total }}</span>
-                                    </a>
-                                {% endfor %}
-                            </div>
                         </div>
                         </div>
-                    {% endif %}
+                        <div class="list-group">
+                            {% for obj_type in results %}
+                                <a href="#{{ obj_type.name }}" class="list-group-item">
+                                    {{ obj_type.name }}
+                                    <span class="badge">{{ obj_type.table.page.paginator.count }}</span>
+                                </a>
+                            {% endfor %}
+                        </div>
+                    </div>
                 </div>
                 </div>
             </div>
             </div>
         {% else %}
         {% else %}

+ 3 - 1
netbox/templates/table.html

@@ -4,5 +4,7 @@
 {# Extends the stock django_tables2 template to provide custom formatting of the pagination controls #}
 {# Extends the stock django_tables2 template to provide custom formatting of the pagination controls #}
 
 
 {% block pagination %}
 {% block pagination %}
-    {% include 'paginator.html' %}
+    {% if not hide_paginator %}
+        {% include 'paginator.html' %}
+    {% endif %}
 {% endblock pagination %}
 {% endblock pagination %}

+ 9 - 4
netbox/tenancy/tables.py

@@ -1,7 +1,7 @@
 import django_tables2 as tables
 import django_tables2 as tables
 from django_tables2.utils import Accessor
 from django_tables2.utils import Accessor
 
 
-from utilities.tables import BaseTable, ToggleColumn
+from utilities.tables import BaseTable, SearchTable, ToggleColumn
 
 
 from .models import Tenant, TenantGroup
 from .models import Tenant, TenantGroup
 
 
@@ -36,10 +36,15 @@ class TenantGroupTable(BaseTable):
 
 
 class TenantTable(BaseTable):
 class TenantTable(BaseTable):
     pk = ToggleColumn()
     pk = ToggleColumn()
-    name = tables.LinkColumn('tenancy:tenant', args=[Accessor('slug')], verbose_name='Name')
+    name = tables.LinkColumn()
-    group = tables.Column(verbose_name='Group')
-    description = tables.Column(verbose_name='Description')
 
 
     class Meta(BaseTable.Meta):
     class Meta(BaseTable.Meta):
         model = Tenant
         model = Tenant
         fields = ('pk', 'name', 'group', 'description')
         fields = ('pk', 'name', 'group', 'description')
+
+
+class TenantSearchTable(SearchTable):
+
+    class Meta(SearchTable.Meta):
+        model = Tenant
+        fields = ('name', 'group', 'description')

+ 14 - 1
netbox/utilities/tables.py

@@ -4,7 +4,9 @@ from django.utils.safestring import mark_safe
 
 
 
 
 class BaseTable(tables.Table):
 class BaseTable(tables.Table):
-
+    """
+    Default table for object lists
+    """
     def __init__(self, *args, **kwargs):
     def __init__(self, *args, **kwargs):
         super(BaseTable, self).__init__(*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):
 class ToggleColumn(tables.CheckBoxColumn):
 
 
     def __init__(self, *args, **kwargs):
     def __init__(self, *args, **kwargs):