content: from django.contrib import messages
  from django.contrib.contenttypes.models import ContentType
  from django.core.paginator import EmptyPage, PageNotAnInteger
  from django.db import router, transaction
  from django.db.models import Prefetch
  from django.forms import ModelMultipleChoiceField, MultipleHiddenInput, modelformset_factory
  from django.shortcuts import get_object_or_404, redirect, render
  from django.urls import reverse
  from django.utils.html import escape
  from django.utils.safestring import mark_safe
  from django.utils.translation import gettext_lazy as _
  from django.views.generic import View
  
  from circuits.models import Circuit, CircuitTermination
  from extras.views import ObjectConfigContextView, ObjectRenderConfigView
  from ipam.models import ASN, IPAddress, Prefix, VLANGroup, VLAN
  from ipam.tables import InterfaceVLANTable, VLANTranslationRuleTable
  from netbox.object_actions import *
  from netbox.views import generic
  from utilities.forms import ConfirmationForm
  from utilities.paginator import EnhancedPaginator, get_paginate_count
  from utilities.permissions import get_permission_for_model
  from utilities.query import count_related
  from utilities.query_functions import CollateAsChar
  from utilities.request import safe_for_redirect
  from utilities.views import (
      GetRelatedModelsMixin, GetReturnURLMixin, ObjectPermissionRequiredMixin, ViewTab, register_model_view
  )
  from virtualization.filtersets import VirtualMachineFilterSet
  from virtualization.forms import VirtualMachineFilterForm
  from virtualization.models import Cluster, VirtualMachine
  from virtualization.tables import VirtualMachineTable
  from wireless.models import WirelessLAN
  from . import filtersets, forms, tables
  from .choices import DeviceFaceChoices, InterfaceModeChoices
  from .models import *
  from .object_actions import BulkAddComponents, BulkDisconnect
  
  CABLE_TERMINATION_TYPES = {
      'dcim.consoleport': ConsolePort,
      'dcim.consoleserverport': ConsoleServerPort,
      'dcim.powerport': PowerPort,
      'dcim.poweroutlet': PowerOutlet,
      'dcim.interface': Interface,
      'dcim.frontport': FrontPort,
      'dcim.rearport': RearPort,
      'dcim.powerfeed': PowerFeed,
      'circuits.circuittermination': CircuitTermination,
  }
  
  
  class DeviceComponentsView(generic.ObjectChildrenView):
      queryset = Device.objects.all()
  
      def get_children(self, request, parent):
          return self.child_model.objects.restrict(request.user, 'view').filter(device=parent)
  
  
  class DeviceTypeComponentsView(generic.ObjectChildrenView):
      actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDelete)
      queryset = DeviceType.objects.all()
      viewname = None  # Used for return_url resolution
  
      def get_children(self, request, parent):
          return self.child_model.objects.restrict(request.user, 'view').filter(device_type=parent)
  
      def get_extra_context(self, request, instance):
          return {
              'return_url': reverse(self.viewname, kwargs={'pk': instance.pk}),
          }
  
  
  class ModuleTypeComponentsView(generic.ObjectChildrenView):
      queryset = ModuleType.objects.all()
      actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDelete)
      viewname = None  # Used for return_url resolution
  
      def get_children(self, request, parent):
          return self.child_model.objects.restrict(request.user, 'view').filter(module_type=parent)
  
      def get_extra_context(self, request, instance):
          return {
              'return_url': reverse(self.viewname, kwargs={'pk': instance.pk}),
          }
  
  
  class BulkDisconnectView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
      """
      An extendable view for disconnection console/power/interface components in bulk.
      """
      queryset = None
      template_name = 'dcim/bulk_disconnect.html'
  
      def __init__(self, *args, **kwargs):
          super().__init__(*args, **kwargs)
  
          # Create a new Form class from ConfirmationForm
          class _Form(ConfirmationForm):
              pk = ModelMultipleChoiceField(
                  queryset=self.queryset,
                  widget=MultipleHiddenInput()
              )
  
          self.form = _Form
  
      def get_required_permission(self):
          return get_permission_for_model(self.queryset.model, 'change')
  
      def post(self, request):
  
          selected_objects = []
          return_url = self.get_return_url(request)
  
          if '_confirm' in request.POST:
              form = self.form(request.POST)
  
              if form.is_valid():
  
                  with transaction.atomic(using=router.db_for_write(Cable)):
                      count = 0
                      cable_ids = set()
                      for obj in self.queryset.filter(pk__in=form.cleaned_data['pk']):
                          if obj.cable:
                              cable_ids.add(obj.cable.pk)
                              count += 1
                      for cable in Cable.objects.filter(pk__in=cable_ids):
                          cable.delete()
  
                  messages.success(request, _("Disconnected {count} {type}").format(
                      count=count,
                      type=self.queryset.model._meta.verbose_name_plural
                  ))
  
                  return redirect(return_url)
  
          else:
              form = self.form(initial={'pk': request.POST.getlist('pk')})
              selected_objects = self.queryset.filter(pk__in=form.initial['pk'])
  
          return render(request, self.template_name, {
              'form': form,
              'obj_type_plural': self.queryset.model._meta.verbose_name_plural,
              'selected_objects': selected_objects,
              'return_url': return_url,
          })
  
  
  class PathTraceView(generic.ObjectView):
      """
      Trace a cable path beginning from the given path endpoint (origin).
      """
      additional_permissions = ['dcim.view_cable']
      template_name = 'dcim/cable_trace.html'
  
      def dispatch(self, request, *args, **kwargs):
          model = kwargs.pop('model')
          self.queryset = model.objects.all()
  
          return super().dispatch(request, *args, **kwargs)
  
      def get_extra_context(self, request, instance):
          related_paths = []
  
          # If tracing a PathEndpoint, locate the CablePath (if one exists) by its origin
          if isinstance(instance, PathEndpoint):
              path = instance._path
  
          # Otherwise, find all CablePaths which traverse the specified object
          else:
              related_paths = CablePath.objects.filter(_nodes__contains=instance)
              # Check for specification of a particular path (when tracing pass-through ports)
              try:
                  path_id = int(request.GET.get('cablepath_id'))
              except TypeError:
                  path_id = None
              if path_id in list(related_paths.values_list('pk', flat=True)):
                  path = CablePath.objects.get(pk=path_id)
              else:
                  path = related_paths.first()
  
          # No paths found
          if path is None:
              return {
                  'path': None
              }
  
          # Get the total length of the cable and whether the length is definitive (fully defined)
          total_length, is_definitive = path.get_total_length() if path else (None, False)
  
          # Determine the path to the SVG trace image
          api_viewname = f"{path.origin_type.app_label}-api:{path.origin_type.model}-trace"
          svg_url = f"{reverse(api_viewname, kwargs={'pk': path.origins[0].pk})}?render=svg"
  
          return {
              'path': path,
              'related_paths': related_paths,
              'total_length': total_length,
              'is_definitive': is_definitive,
              'svg_url': svg_url,
          }
  
  
  #
  # Regions
  #
  
  @register_model_view(Region, 'list', path='', detail=False)
  class RegionListView(generic.ObjectListView):
      queryset = Region.objects.add_related_count(
          Region.objects.all(),
          Site,
          'region',
          'site_count',
          cumulative=True
      )
      filterset = filtersets.RegionFilterSet
      filterset_form = forms.RegionFilterForm
      table = tables.RegionTable
  
  
  @register_model_view(Region)
  class RegionView(GetRelatedModelsMixin, generic.ObjectView):
      queryset = Region.objects.all()
  
      def get_extra_context(self, request, instance):
          regions = instance.get_descendants(include_self=True)
  
          return {
              'related_models': self.get_related_models(
                  request,
                  regions,
                  omit=(Cluster, CircuitTermination, Prefix, WirelessLAN),
                  extra=(
                      (Location.objects.restrict(request.user, 'view').filter(site__region__in=regions), 'region_id'),
                      (Rack.objects.restrict(request.user, 'view').filter(site__region__in=regions), 'region_id'),
                      (
                          Circuit.objects.restrict(request.user, 'view').filter(
                              terminations___region=instance
                          ).distinct(),
                          'region_id'
                      ),
                      (
                          VLANGroup.objects.restrict(request.user, 'view').filter(
                              scope_type=ContentType.objects.get_for_model(Region),
                              scope_id__in=regions
                          ).distinct(),
                          'region'
                      ),
  
                      # Handle these relations manually to avoid erroneous filter name resolution
                      (
                          CircuitTermination.objects.restrict(request.user, 'view').filter(_region__in=regions),
                          'region_id'
                      ),
                      (Cluster.objects.restrict(request.user, 'view').filter(_region__in=regions), 'region_id'),
                      (Prefix.objects.restrict(request.user, 'view').filter(_region__in=regions), 'region_id'),
                      (WirelessLAN.objects.restrict(request.user, 'view').filter(_region__in=regions), 'region_id'),
                  ),
              ),
          }
  
  
  @register_model_view(Region, 'add', detail=False)
  @register_model_view(Region, 'edit')
  class RegionEditView(generic.ObjectEditView):
      queryset = Region.objects.all()
      form = forms.RegionForm
  
  
  @register_model_view(Region, 'delete')
  class RegionDeleteView(generic.ObjectDeleteView):
      queryset = Region.objects.all()
  
  
  @register_model_view(Region, 'bulk_import', path='import', detail=False)
  class RegionBulkImportView(generic.BulkImportView):
      queryset = Region.objects.all()
      model_form = forms.RegionImportForm
  
  
  @register_model_view(Region, 'bulk_edit', path='edit', detail=False)
  class RegionBulkEditView(generic.BulkEditView):
      queryset = Region.objects.add_related_count(
          Region.objects.all(),
          Site,
          'region',
          'site_count',
          cumulative=True
      )
      filterset = filtersets.RegionFilterSet
      table = tables.RegionTable
      form = forms.RegionBulkEditForm
  
  
  @register_model_view(Region, 'bulk_rename', path='rename', detail=False)
  class RegionBulkRenameView(generic.BulkRenameView):
      queryset = Region.objects.all()
      filterset = filtersets.RegionFilterSet
  
  
  @register_model_view(Region, 'bulk_delete', path='delete', detail=False)
  class RegionBulkDeleteView(generic.BulkDeleteView):
      queryset = Region.objects.add_related_count(
          Region.objects.all(),
          Site,
          'region',
          'site_count',
          cumulative=True
      )
      filterset = filtersets.RegionFilterSet
      table = tables.RegionTable
  
  
  #
  # Site groups
  #
  
  @register_model_view(SiteGroup, 'list', path='', detail=False)
  class SiteGroupListView(generic.ObjectListView):
      queryset = SiteGroup.objects.add_related_count(
          SiteGroup.objects.all(),
          Site,
          'group',
          'site_count',
          cumulative=True
      )
      filterset = filtersets.SiteGroupFilterSet
      filterset_form = forms.SiteGroupFilterForm
      table = tables.SiteGroupTable
  
  
  @register_model_view(SiteGroup)
  class SiteGroupView(GetRelatedModelsMixin, generic.ObjectView):
      queryset = SiteGroup.objects.all()
  
      def get_extra_context(self, request, instance):
          groups = instance.get_descendants(include_self=True)
  
          return {
              'related_models': self.get_related_models(
                  request,
                  groups,
                  omit=(Cluster, CircuitTermination, Prefix, WirelessLAN),
                  extra=(
                      (Location.objects.restrict(request.user, 'view').filter(site__group__in=groups), 'site_group_id'),
                      (Rack.objects.restrict(request.user, 'view').filter(site__group__in=groups), 'site_group_id'),
                      (Device.objects.restrict(request.user, 'view').filter(site__group__in=groups), 'site_group_id'),
                      (VLAN.objects.restrict(request.user, 'view').filter(site__group__in=groups), 'site_group_id'),
                      (
                          ASN.objects.restrict(request.user, 'view').filter(
                              sites__group__in=groups
                          ).distinct(),
                          'site_group_id'),
                      (
                          VirtualMachine.objects.restrict(request.user, 'view').filter(
                              site__group__in=groups),
                          'site_group_id'
                      ),
                      (
                          VLANGroup.objects.restrict(request.user, 'view').filter(
                              scope_type=ContentType.objects.get_for_model(SiteGroup),
                              scope_id__in=groups
                          ).distinct(),
                          'site_group'
                      ),
                      (
                          Circuit.objects.restrict(request.user, 'view').filter(
                              terminations___site_group=instance
                          ).distinct(),
                          'site_group_id'
                      ),
  
                      # Handle these relations manually to avoid erroneous filter name resolution
                      (
                          CircuitTermination.objects.restrict(request.user, 'view').filter(_site_group__in=groups),
                          'site_group_id'
                      ),
                      (
                          Cluster.objects.restrict(request.user, 'view').filter(_site_group__in=groups),
                          'site_group_id'
                      ),
                      (
                          Prefix.objects.restrict(request.user, 'view').filter(_site_group__in=groups),
                          'site_group_id'
                      ),
                      (
                          WirelessLAN.objects.restrict(request.user, 'view').filter(_site_group__in=groups),
                          'site_group_id'
                      ),
                  ),
              ),
          }
  
  
  @register_model_view(SiteGroup, 'add', detail=False)
  @register_model_view(SiteGroup, 'edit')
  class SiteGroupEditView(generic.ObjectEditView):
      queryset = SiteGroup.objects.all()
      form = forms.SiteGroupForm
  
  
  @register_model_view(SiteGroup, 'delete')
  class SiteGroupDeleteView(generic.ObjectDeleteView):
      queryset = SiteGroup.objects.all()
  
  
  @register_model_view(SiteGroup, 'bulk_import', path='import', detail=False)
  class SiteGroupBulkImportView(generic.BulkImportView):
      queryset = SiteGroup.objects.all()
      model_form = forms.SiteGroupImportForm
  
  
  @register_model_view(SiteGroup, 'bulk_edit', path='edit', detail=False)
  class SiteGroupBulkEditView(generic.BulkEditView):
      queryset = SiteGroup.objects.add_related_count(
          SiteGroup.objects.all(),
          Site,
          'group',
          'site_count',
          cumulative=True
      )
      filterset = filtersets.SiteGroupFilterSet
      table = tables.SiteGroupTable
      form = forms.SiteGroupBulkEditForm
  
  
  @register_model_view(SiteGroup, 'bulk_rename', path='rename', detail=False)
  class SiteGroupBulkRenameView(generic.BulkRenameView):
      queryset = SiteGroup.objects.all()
      filterset = filtersets.SiteGroupFilterSet
  
  
  @register_model_view(SiteGroup, 'bulk_delete', path='delete', detail=False)
  class SiteGroupBulkDeleteView(generic.BulkDeleteView):
      queryset = SiteGroup.objects.add_related_count(
          SiteGroup.objects.all(),
          Site,
          'group',
          'site_count',
          cumulative=True
      )
      filterset = filtersets.SiteGroupFilterSet
      table = tables.SiteGroupTable
  
  
  #
  # Sites
  #
  
  @register_model_view(Site, 'list', path='', detail=False)
  class SiteListView(generic.ObjectListView):
      queryset = Site.objects.annotate(
          device_count=count_related(Device, 'site'),
          asn_count=count_related(ASN, 'sites')
      )
      filterset = filtersets.SiteFilterSet
      filterset_form = forms.SiteFilterForm
      table = tables.SiteTable
  
  
  @register_model_view(Site)
  class SiteView(GetRelatedModelsMixin, generic.ObjectView):
      queryset = Site.objects.prefetch_related('tenant__group')
  
      def get_extra_context(self, request, instance):
          return {
              'related_models': self.get_related_models(
                  request,
                  instance,
                  omit=(CableTermination, CircuitTermination, Cluster, Prefix, WirelessLAN),
                  extra=(
                      (VLANGroup.objects.restrict(request.user, 'view').filter(
                          scope_type=ContentType.objects.get_for_model(Site),
                          scope_id=instance.pk
                      ), 'site'),
                      (ASN.objects.restrict(request.user, 'view').filter(sites=instance), 'site_id'),
                      (
                          Circuit.objects.restrict(request.user, 'view').filter(terminations___site=instance).distinct(),
                          'site_id'
                      ),
  
                      # Handle these relations manually to avoid erroneous filter name resolution
                      (Cluster.objects.restrict(request.user, 'view').filter(_site=instance), 'site_id'),
                      (Prefix.objects.restrict(request.user, 'view').filter(_site=instance), 'site_id'),
                      (WirelessLAN.objects.restrict(request.user, 'view').filter(_site=instance), 'site_id'),
                      (CircuitTermination.objects.restrict(request.user, 'view').filter(_site=instance), 'site_id'),
                  ),
              ),
          }
  
  
  @register_model_view(Site, 'add', detail=False)
  @register_model_view(Site, 'edit')
  class SiteEditView(generic.ObjectEditView):
      queryset = Site.objects.all()
      form = forms.SiteForm
  
  
  @register_model_view(Site, 'delete')
  class SiteDeleteView(generic.ObjectDeleteView):
      queryset = Site.objects.all()
  
  
  @register_model_view(Site, 'bulk_import', path='import', detail=False)
  class SiteBulkImportView(generic.BulkImportView):
      queryset = Site.objects.all()
      model_form = forms.SiteImportForm
  
  
  @register_model_view(Site, 'bulk_edit', path='edit', detail=False)
  class SiteBulkEditView(generic.BulkEditView):
      queryset = Site.objects.all()
      filterset = filtersets.SiteFilterSet
      table = tables.SiteTable
      form = forms.SiteBulkEditForm
  
  
  @register_model_view(Site, 'bulk_rename', path='rename', detail=False)
  class SiteBulkRenameView(generic.BulkRenameView):
      queryset = Site.objects.all()
      filterset = filtersets.SiteFilterSet
  
  
  @register_model_view(Site, 'bulk_delete', path='delete', detail=False)
  class SiteBulkDeleteView(generic.BulkDeleteView):
      queryset = Site.objects.all()
      filterset = filtersets.SiteFilterSet
      table = tables.SiteTable
  
  
  #
  # Locations
  #
  
  @register_model_view(Location, 'list', path='', detail=False)
  class LocationListView(generic.ObjectListView):
      queryset = Location.objects.add_related_count(
                  Location.objects.add_related_count(
                          Location.objects.add_related_count(
                              Location.objects.all(),
                              Device,
                              'location',
                              'device_count',
                              cumulative=True
                          ),
                          Rack,
                          'location',
                          'rack_count',
                          cumulative=True
                      ),
                  VLANGroup,
                  'location',
                  'vlangroup_count',
                  cumulative=True
              )
      filterset = filtersets.LocationFilterSet
      filterset_form = forms.LocationFilterForm
      table = tables.LocationTable
  
  
  @register_model_view(Location)
  class LocationView(GetRelatedModelsMixin, generic.ObjectView):
      queryset = Location.objects.all()
  
      def get_extra_context(self, request, instance):
          locations = instance.get_descendants(include_self=True)
          location_content_type = ContentType.objects.get_for_model(instance)
          return {
              'related_models': self.get_related_models(
                  request,
                  locations,
                  omit=[CableTermination, CircuitTermination, Cluster, Prefix, WirelessLAN],
                  extra=(
                      (
                          Circuit.objects.restrict(request.user, 'view').filter(
                              terminations___location=instance
                          ).distinct(),
                          'location_id'
                      ),
  
                      # Handle these relations manually to avoid erroneous filter name resolution
                      (
                          CircuitTermination.objects.restrict(request.user, 'view').filter(_location=instance),
                          'location_id'
                      ),
                      (Cluster.objects.restrict(request.user, 'view').filter(_location=instance), 'location_id'),
                      (Prefix.objects.restrict(request.user, 'view').filter(_location=instance), 'location_id'),
                      (WirelessLAN.objects.restrict(request.user, 'view').filter(_location=instance), 'location_id'),
                      (VLANGroup.objects.restrict(request.user, 'view').filter(
                          scope_type_id=location_content_type.id, scope_id=instance.id), 'location'),
                  ),
              ),
          }
  
  
  @register_model_view(Location, 'add', detail=False)
  @register_model_view(Location, 'edit')
  class LocationEditView(generic.ObjectEditView):
      queryset = Location.objects.all()
      form = forms.LocationForm
  
  
  @register_model_view(Location, 'delete')
  class LocationDeleteView(generic.ObjectDeleteView):
      queryset = Location.objects.all()
  
  
  @register_model_view(Location, 'bulk_import', path='import', detail=False)
  class LocationBulkImportView(generic.BulkImportView):
      queryset = Location.objects.all()
      model_form = forms.LocationImportForm
  
  
  @register_model_view(Location, 'bulk_edit', path='edit', detail=False)
  class LocationBulkEditView(generic.BulkEditView):
      queryset = Location.objects.add_related_count(
          Location.objects.all(),
          Rack,
          'location',
          'rack_count',
          cumulative=True
      ).prefetch_related('site')
      filterset = filtersets.LocationFilterSet
      table = tables.LocationTable
      form = forms.LocationBulkEditForm
  
  
  @register_model_view(Location, 'bulk_rename', path='rename', detail=False)
  class LocationBulkRenameView(generic.BulkRenameView):
      queryset = Location.objects.all()
      filterset = filtersets.LocationFilterSet
  
  
  @register_model_view(Location, 'bulk_delete', path='delete', detail=False)
  class LocationBulkDeleteView(generic.BulkDeleteView):
      queryset = Location.objects.add_related_count(
          Location.objects.all(),
          Rack,
          'location',
          'rack_count',
          cumulative=True
      ).prefetch_related('site')
      filterset = filtersets.LocationFilterSet
      table = tables.LocationTable
  
  
  #
  # Rack roles
  #
  
  @register_model_view(RackRole, 'list', path='', detail=False)
  class RackRoleListView(generic.ObjectListView):
      queryset = RackRole.objects.annotate(
          rack_count=count_related(Rack, 'role')
      )
      filterset = filtersets.RackRoleFilterSet
      filterset_form = forms.RackRoleFilterForm
      table = tables.RackRoleTable
  
  
  @register_model_view(RackRole)
  class RackRoleView(GetRelatedModelsMixin, generic.ObjectView):
      queryset = RackRole.objects.all()
  
      def get_extra_context(self, request, instance):
          return {
              'related_models': self.get_related_models(request, instance),
          }
  
  
  @register_model_view(RackRole, 'add', detail=False)
  @register_model_view(RackRole, 'edit')
  class RackRoleEditView(generic.ObjectEditView):
      queryset = RackRole.objects.all()
      form = forms.RackRoleForm
  
  
  @register_model_view(RackRole, 'delete')
  class RackRoleDeleteView(generic.ObjectDeleteView):
      queryset = RackRole.objects.all()
  
  
  @register_model_view(RackRole, 'bulk_import', path='import', detail=False)
  class RackRoleBulkImportView(generic.BulkImportView):
      queryset = RackRole.objects.all()
      model_form = forms.RackRoleImportForm
  
  
  @register_model_view(RackRole, 'bulk_edit', path='edit', detail=False)
  class RackRoleBulkEditView(generic.BulkEditView):
      queryset = RackRole.objects.annotate(
          rack_count=count_related(Rack, 'role')
      )
      filterset = filtersets.RackRoleFilterSet
      table = tables.RackRoleTable
      form = forms.RackRoleBulkEditForm
  
  
  @register_model_view(RackRole, 'bulk_rename', path='rename', detail=False)
  class RackRoleBulkRenameView(generic.BulkRenameView):
      queryset = RackRole.objects.all()
      filterset = filtersets.RackRoleFilterSet
  
  
  @register_model_view(RackRole, 'bulk_delete', path='delete', detail=False)
  class RackRoleBulkDeleteView(generic.BulkDeleteView):
      queryset = RackRole.objects.annotate(
          rack_count=count_related(Rack, 'role')
      )
      filterset = filtersets.RackRoleFilterSet
      table = tables.RackRoleTable
  
  
  #
  # RackTypes
  #
  
  @register_model_view(RackType, 'list', path='', detail=False)
  class RackTypeListView(generic.ObjectListView):
      queryset = RackType.objects.annotate(
          instance_count=count_related(Rack, 'rack_type')
      )
      filterset = filtersets.RackTypeFilterSet
      filterset_form = forms.RackTypeFilterForm
      table = tables.RackTypeTable
  
  
  @register_model_view(RackType)
  class RackTypeView(GetRelatedModelsMixin, generic.ObjectView):
      queryset = RackType.objects.all()
  
      def get_extra_context(self, request, instance):
          return {
              'related_models': self.get_related_models(request, instance),
          }
  
  
  @register_model_view(RackType, 'add', detail=False)
  @register_model_view(RackType, 'edit')
  class RackTypeEditView(generic.ObjectEditView):
      queryset = RackType.objects.all()
      form = forms.RackTypeForm
  
  
  @register_model_view(RackType, 'delete')
  class RackTypeDeleteView(generic.ObjectDeleteView):
      queryset = RackType.objects.all()
  
  
  @register_model_view(RackType, 'bulk_import', path='import', detail=False)
  class RackTypeBulkImportView(generic.BulkImportView):
      queryset = RackType.objects.all()
      model_form = forms.RackTypeImportForm
  
  
  @register_model_view(RackType, 'bulk_edit', path='edit', detail=False)
  class RackTypeBulkEditView(generic.BulkEditView):
      queryset = RackType.objects.all()
      filterset = filtersets.RackTypeFilterSet
      table = tables.RackTypeTable
      form = forms.RackTypeBulkEditForm
  
  
  @register_model_view(RackType, 'bulk_rename', path='rename', detail=False)
  class RackTypeBulkRenameView(generic.BulkRenameView):
      queryset = RackType.objects.all()
      field_name = 'model'
      filterset = filtersets.RackTypeFilterSet
  
  
  @register_model_view(RackType, 'bulk_delete', path='delete', detail=False)
  class RackTypeBulkDeleteView(generic.BulkDeleteView):
      queryset = RackType.objects.all()
      filterset = filtersets.RackTypeFilterSet
      table = tables.RackTypeTable
  
  
  #
  # Racks
  #
  
  @register_model_view(Rack, 'list', path='', detail=False)
  class RackListView(generic.ObjectListView):
      queryset = Rack.objects.annotate(
          device_count=count_related(Device, 'rack')
      )
      filterset = filtersets.RackFilterSet
      filterset_form = forms.RackFilterForm
      table = tables.RackTable
      template_name = 'dcim/rack_list.html'
  
  
  class RackElevationListView(generic.ObjectListView):
      """
      Display a set of rack elevations side-by-side.
      """
      queryset = Rack.objects.prefetch_related('role')
  
      def get(self, request):
  
          racks = filtersets.RackFilterSet(request.GET, self.queryset).qs
          total_count = racks.count()
  
          # Ordering
          ORDERING_CHOICES = {
              'name': 'Name (A-Z)',
              '-name': 'Name (Z-A)',
              'facility_id': 'Facility ID (A-Z)',
              '-facility_id': 'Facility ID (Z-A)',
          }
          sort = request.GET.get('sort', 'name')
          if sort not in ORDERING_CHOICES:
              sort = 'name'
          racks = racks.order_by(sort)
  
          # Pagination
          per_page = get_paginate_count(request)
          page_number = request.GET.get('page', 1)
          paginator = EnhancedPaginator(racks, per_page)
          try:
              page = paginator.page(page_number)
          except PageNotAnInteger:
              page = paginator.page(1)
          except EmptyPage:
              page = paginator.page(paginator.num_pages)
  
          # Determine rack face
          rack_face = request.GET.get('face', DeviceFaceChoices.FACE_FRONT)
          if rack_face not in DeviceFaceChoices.values():
              rack_face = DeviceFaceChoices.FACE_FRONT
  
          return render(request, 'dcim/rack_elevation_list.html', {
              'paginator': paginator,
              'page': page,
              'total_count': total_count,
              'sort': sort,
              'sort_display_name': ORDERING_CHOICES[sort],
              'sort_choices': ORDERING_CHOICES,
              'rack_face': rack_face,
              'filter_form': forms.RackElevationFilterForm(request.GET),
              'model': self.queryset.model,
          })
  
  
  @register_model_view(Rack)
  class RackView(GetRelatedModelsMixin, generic.ObjectView):
      queryset = Rack.objects.prefetch_related('site__region', 'tenant__group', 'location', 'role')
  
      def get_extra_context(self, request, instance):
          peer_racks = Rack.objects.restrict(request.user, 'view').filter(site=instance.site)
  
          if instance.location:
              peer_racks = peer_racks.filter(location=instance.location)
          else:
              peer_racks = peer_racks.filter(location__isnull=True)
          next_rack = peer_racks.filter(name__gt=instance.name).first()
          prev_rack = peer_racks.filter(name__lt=instance.name).reverse().first()
  
          # Determine any additional parameters to pass when embedding the rack elevations
          svg_extra = '&'.join([
              f'highlight=id:{pk}' for pk in request.GET.getlist('device')
          ])
  
          return {
              'related_models': self.get_related_models(
                  request,
                  instance,
                  omit=(CableTermination,),
                  extra=(
                      (
                      VLANGroup.objects.restrict(request.user, 'view').filter(
                          scope_type=ContentType.objects.get_for_model(Rack),
                          scope_id=instance.pk
                      ), 'rack'),
                  ),
              ),
              'next_rack': next_rack,
              'prev_rack': prev_rack,
              'svg_extra': svg_extra,
              'peer_racks': peer_racks,
          }
  
  
  @register_model_view(Rack, 'reservations')
  class RackRackReservationsView(generic.ObjectChildrenView):
      queryset = Rack.objects.all()
      child_model = RackReservation
      table = tables.RackReservationTable
      filterset = filtersets.RackReservationFilterSet
      filterset_form = forms.RackReservationFilterForm
      template_name = 'dcim/rack/reservations.html'
      tab = ViewTab(
          label=_('Reservations'),
          badge=lambda obj: obj.reservations.count(),
          permission='dcim.view_rackreservation',
          weight=510
      )
  
      def get_children(self, request, parent):
          return parent.reservations.restrict(request.user, 'view')
  
  
  @register_model_view(Rack, 'nonracked_devices', 'nonracked-devices')
  class RackNonRackedView(generic.ObjectChildrenView):
      queryset = Rack.objects.all()
      child_model = Device
      table = tables.DeviceTable
      filterset = filtersets.DeviceFilterSet
      filterset_form = forms.DeviceFilterForm
      template_name = 'dcim/rack/non_racked_devices.html'
      tab = ViewTab(
          label=_('Non-Racked Devices'),
          badge=lambda obj: obj.devices.filter(rack=obj, position__isnull=True, parent_bay__isnull=True).count(),
          weight=500,
          permission='dcim.view_device',
      )
  
      def get_children(self, request, parent):
          return parent.devices.restrict(request.user, 'view').filter(
              rack=parent, position__isnull=True, parent_bay__isnull=True
          )
  
  
  