diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index f3a353f5af7..f48e5aa804b 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -1,6 +1,5 @@ -import re - from mptt.forms import TreeNodeChoiceField +import re from django import forms from django.contrib.postgres.forms.array import SimpleArrayField @@ -21,9 +20,9 @@ DeviceBay, DeviceBayTemplate, CONNECTION_STATUS_CHOICES, CONNECTION_STATUS_PLANNED, CONNECTION_STATUS_CONNECTED, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceRole, DeviceType, Interface, IFACE_FF_CHOICES, IFACE_FF_LAG, IFACE_ORDERING_CHOICES, InterfaceConnection, InterfaceTemplate, - Manufacturer, InventoryItem, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, RACK_TYPE_CHOICES, - RACK_WIDTH_CHOICES, Rack, RackGroup, RackReservation, RackRole, Region, Site, STATUS_CHOICES, SUBDEVICE_ROLE_CHILD, - SUBDEVICE_ROLE_PARENT, VIRTUAL_IFACE_TYPES + Manufacturer, InventoryItem, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, + RACK_TYPE_CHOICES, RACK_WIDTH_CHOICES, Rack, RackGroup, RackReservation, RackRole, Region, Site, STATUS_CHOICES, + SUBDEVICE_ROLE_CHILD, SUBDEVICE_ROLE_PARENT, VIRTUAL_IFACE_TYPES, ) diff --git a/netbox/dcim/urls.py b/netbox/dcim/urls.py index 023d4e87dc5..8e666f406c2 100644 --- a/netbox/dcim/urls.py +++ b/netbox/dcim/urls.py @@ -47,6 +47,7 @@ # Racks url(r'^racks/$', views.RackListView.as_view(), name='rack_list'), + url(r'^rack-elevations/$', views.RackElevationListView.as_view(), name='rack_elevation_list'), url(r'^racks/add/$', views.RackEditView.as_view(), name='rack_add'), url(r'^racks/import/$', views.RackBulkImportView.as_view(), name='rack_import'), url(r'^racks/edit/$', views.RackBulkEditView.as_view(), name='rack_bulk_edit'), diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 9500b19141f..5eb80005d94 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -6,6 +6,7 @@ from django.contrib import messages from django.contrib.auth.decorators import permission_required from django.contrib.auth.mixins import PermissionRequiredMixin +from django.core.paginator import EmptyPage, PageNotAnInteger from django.db.models import Count from django.http import HttpResponseRedirect from django.shortcuts import get_object_or_404, redirect, render @@ -17,6 +18,7 @@ from circuits.models import Circuit from extras.models import Graph, TopologyMap, GRAPH_TYPE_INTERFACE, GRAPH_TYPE_SITE from utilities.forms import ConfirmationForm +from utilities.paginator import EnhancedPaginator from utilities.views import ( BulkDeleteView, BulkEditView, BulkImportView, ObjectDeleteView, ObjectEditView, ObjectListView, ) @@ -291,6 +293,46 @@ class RackListView(ObjectListView): template_name = 'dcim/rack_list.html' +class RackElevationListView(View): + """ + Display a set of rack elevations side-by-side. + """ + + def get(self, request): + + racks = Rack.objects.select_related( + 'site', 'group', 'tenant', 'role' + ).prefetch_related( + 'devices__device_type' + ) + racks = filters.RackFilter(request.GET, racks).qs + total_count = racks.count() + + # Pagination + paginator = EnhancedPaginator(racks, 25) + page_number = request.GET.get('page', 1) + try: + page = paginator.page(page_number) + except PageNotAnInteger: + page = paginator.page(1) + except EmptyPage: + page = paginator.page(paginator.num_pages) + + # Determine rack face + if request.GET.get('face') == '1': + face_id = 1 + else: + face_id = 0 + + return render(request, 'dcim/rack_elevation_list.html', { + 'paginator': paginator, + 'page': page, + 'total_count': total_count, + 'face_id': face_id, + 'filter_form': forms.RackFilterForm(request.GET), + }) + + def rack(request, pk): rack = get_object_or_404(Rack.objects.select_related('site__region', 'tenant__group', 'group', 'role'), pk=pk) diff --git a/netbox/templates/_base.html b/netbox/templates/_base.html index df760de2143..a7b0ceb5a35 100644 --- a/netbox/templates/_base.html +++ b/netbox/templates/_base.html @@ -58,6 +58,7 @@