content: from django.contrib.contenttypes.models import ContentType
  from django.db.models import Prefetch
  from django.db.models.expressions import RawSQL
  from django.shortcuts import get_object_or_404, redirect, render
  from django.urls import reverse
  from django.utils.translation import gettext_lazy as _
  
  from circuits.models import Provider
  from dcim.filtersets import InterfaceFilterSet
  from dcim.forms import InterfaceFilterForm
  from dcim.models import Device, Interface, Site
  from ipam.tables import VLANTranslationRuleTable
  from netbox.object_actions import AddObject, BulkDelete, BulkEdit, BulkExport, BulkImport
  from netbox.views import generic
  from utilities.query import count_related
  from utilities.tables import get_table_ordering
  from utilities.views import GetRelatedModelsMixin, ViewTab, register_model_view
  from virtualization.filtersets import VMInterfaceFilterSet
  from virtualization.forms import VMInterfaceFilterForm
  from virtualization.models import VirtualMachine, VMInterface
  from . import filtersets, forms, tables
  from .choices import PrefixStatusChoices
  from .constants import *
  from .models import *
  from .utils import add_requested_prefixes, add_available_vlans, annotate_ip_space
  
  
  #
  # VRFs
  #
  
  @register_model_view(VRF, 'list', path='', detail=False)
  class VRFListView(generic.ObjectListView):
      queryset = VRF.objects.all()
      filterset = filtersets.VRFFilterSet
      filterset_form = forms.VRFFilterForm
      table = tables.VRFTable
  
  
  @register_model_view(VRF)
  class VRFView(GetRelatedModelsMixin, generic.ObjectView):
      queryset = VRF.objects.all()
  
      def get_extra_context(self, request, instance):
          import_targets_table = tables.RouteTargetTable(
              instance.import_targets.all(),
              orderable=False
          )
          import_targets_table.configure(request)
  
          export_targets_table = tables.RouteTargetTable(
              instance.export_targets.all(),
              orderable=False
          )
          export_targets_table.configure(request)
  
          related_models = self.get_related_models(
              request,
              instance,
              omit=(Interface, VMInterface),
              extra=(
                  (
                      Interface.objects.restrict(request.user, 'view').filter(vrf=instance),
                      'vrf_id',
                      _('Device Interfaces')
                  ),
                  (
                      VMInterface.objects.restrict(request.user, 'view').filter(vrf=instance),
                      'vrf_id',
                      _('VM Interfaces')
                  ),
              ),
          )
  
          return {
              'related_models': related_models,
              'import_targets_table': import_targets_table,
              'export_targets_table': export_targets_table,
          }
  
  
  @register_model_view(VRF, 'add', detail=False)
  @register_model_view(VRF, 'edit')
  class VRFEditView(generic.ObjectEditView):
      queryset = VRF.objects.all()
      form = forms.VRFForm
  
  
  @register_model_view(VRF, 'delete')
  class VRFDeleteView(generic.ObjectDeleteView):
      queryset = VRF.objects.all()
  
  
  @register_model_view(VRF, 'bulk_import', path='import', detail=False)
  class VRFBulkImportView(generic.BulkImportView):
      queryset = VRF.objects.all()
      model_form = forms.VRFImportForm
  
  
  @register_model_view(VRF, 'bulk_edit', path='edit', detail=False)
  class VRFBulkEditView(generic.BulkEditView):
      queryset = VRF.objects.all()
      filterset = filtersets.VRFFilterSet
      table = tables.VRFTable
      form = forms.VRFBulkEditForm
  
  
  @register_model_view(VRF, 'bulk_rename', path='rename', detail=False)
  class VRFBulkRenameView(generic.BulkRenameView):
      queryset = VRF.objects.all()
      filterset = filtersets.VRFFilterSet
  
  
  @register_model_view(VRF, 'bulk_delete', path='delete', detail=False)
  class VRFBulkDeleteView(generic.BulkDeleteView):
      queryset = VRF.objects.all()
      filterset = filtersets.VRFFilterSet
      table = tables.VRFTable
  
  
  #
  # Route targets
  #
  
  @register_model_view(RouteTarget, 'list', path='', detail=False)
  class RouteTargetListView(generic.ObjectListView):
      queryset = RouteTarget.objects.all()
      filterset = filtersets.RouteTargetFilterSet
      filterset_form = forms.RouteTargetFilterForm
      table = tables.RouteTargetTable
  
  
  @register_model_view(RouteTarget)
  class RouteTargetView(generic.ObjectView):
      queryset = RouteTarget.objects.all()
  
  
  @register_model_view(RouteTarget, 'add', detail=False)
  @register_model_view(RouteTarget, 'edit')
  class RouteTargetEditView(generic.ObjectEditView):
      queryset = RouteTarget.objects.all()
      form = forms.RouteTargetForm
  
  
  @register_model_view(RouteTarget, 'delete')
  class RouteTargetDeleteView(generic.ObjectDeleteView):
      queryset = RouteTarget.objects.all()
  
  
  @register_model_view(RouteTarget, 'bulk_import', path='import', detail=False)
  class RouteTargetBulkImportView(generic.BulkImportView):
      queryset = RouteTarget.objects.all()
      model_form = forms.RouteTargetImportForm
  
  
  @register_model_view(RouteTarget, 'bulk_edit', path='edit', detail=False)
  class RouteTargetBulkEditView(generic.BulkEditView):
      queryset = RouteTarget.objects.all()
      filterset = filtersets.RouteTargetFilterSet
      table = tables.RouteTargetTable
      form = forms.RouteTargetBulkEditForm
  
  
  @register_model_view(RouteTarget, 'bulk_rename', path='rename', detail=False)
  class RouteTargetBulkRenameView(generic.BulkRenameView):
      queryset = RouteTarget.objects.all()
      filterset = filtersets.RouteTargetFilterSet
  
  
  @register_model_view(RouteTarget, 'bulk_delete', path='delete', detail=False)
  class RouteTargetBulkDeleteView(generic.BulkDeleteView):
      queryset = RouteTarget.objects.all()
      filterset = filtersets.RouteTargetFilterSet
      table = tables.RouteTargetTable
  
  
  #
  # RIRs
  #
  
  @register_model_view(RIR, 'list', path='', detail=False)
  class RIRListView(generic.ObjectListView):
      queryset = RIR.objects.annotate(
          aggregate_count=count_related(Aggregate, 'rir')
      )
      filterset = filtersets.RIRFilterSet
      filterset_form = forms.RIRFilterForm
      table = tables.RIRTable
  
  
  @register_model_view(RIR)
  class RIRView(GetRelatedModelsMixin, generic.ObjectView):
      queryset = RIR.objects.all()
  
      def get_extra_context(self, request, instance):
          return {
              'related_models': self.get_related_models(request, instance),
          }
  
  
  @register_model_view(RIR, 'add', detail=False)
  @register_model_view(RIR, 'edit')
  class RIREditView(generic.ObjectEditView):
      queryset = RIR.objects.all()
      form = forms.RIRForm
  
  
  @register_model_view(RIR, 'delete')
  class RIRDeleteView(generic.ObjectDeleteView):
      queryset = RIR.objects.all()
  
  
  @register_model_view(RIR, 'bulk_import', path='import', detail=False)
  class RIRBulkImportView(generic.BulkImportView):
      queryset = RIR.objects.all()
      model_form = forms.RIRImportForm
  
  
  @register_model_view(RIR, 'bulk_edit', path='edit', detail=False)
  class RIRBulkEditView(generic.BulkEditView):
      queryset = RIR.objects.annotate(
          aggregate_count=count_related(Aggregate, 'rir')
      )
      filterset = filtersets.RIRFilterSet
      table = tables.RIRTable
      form = forms.RIRBulkEditForm
  
  
  @register_model_view(RIR, 'bulk_rename', path='rename', detail=False)
  class RIRBulkRenameView(generic.BulkRenameView):
      queryset = RIR.objects.all()
      filterset = filtersets.RIRFilterSet
  
  
  @register_model_view(RIR, 'bulk_delete', path='delete', detail=False)
  class RIRBulkDeleteView(generic.BulkDeleteView):
      queryset = RIR.objects.annotate(
          aggregate_count=count_related(Aggregate, 'rir')
      )
      filterset = filtersets.RIRFilterSet
      table = tables.RIRTable
  
  
  #
  # ASN ranges
  #
  
  @register_model_view(ASNRange, 'list', path='', detail=False)
  class ASNRangeListView(generic.ObjectListView):
      queryset = ASNRange.objects.annotate_asn_counts()
      filterset = filtersets.ASNRangeFilterSet
      filterset_form = forms.ASNRangeFilterForm
      table = tables.ASNRangeTable
  
  
  @register_model_view(ASNRange)
  class ASNRangeView(generic.ObjectView):
      queryset = ASNRange.objects.all()
  
  
  @register_model_view(ASNRange, 'asns')
  class ASNRangeASNsView(generic.ObjectChildrenView):
      queryset = ASNRange.objects.all()
      child_model = ASN
      table = tables.ASNTable
      filterset = filtersets.ASNFilterSet
      filterset_form = forms.ASNFilterForm
      tab = ViewTab(
          label=_('ASNs'),
          badge=lambda x: x.get_child_asns().count(),
          permission='ipam.view_asn',
          weight=500
      )
  
      def get_children(self, request, parent):
          return parent.get_child_asns().restrict(request.user, 'view').annotate(
              site_count=count_related(Site, 'asns'),
              provider_count=count_related(Provider, 'asns')
          )
  
  
  @register_model_view(ASNRange, 'add', detail=False)
  @register_model_view(ASNRange, 'edit')
  class ASNRangeEditView(generic.ObjectEditView):
      queryset = ASNRange.objects.all()
      form = forms.ASNRangeForm
  
  
  @register_model_view(ASNRange, 'delete')
  class ASNRangeDeleteView(generic.ObjectDeleteView):
      queryset = ASNRange.objects.all()
  
  
  @register_model_view(ASNRange, 'bulk_import', path='import', detail=False)
  class ASNRangeBulkImportView(generic.BulkImportView):
      queryset = ASNRange.objects.all()
      model_form = forms.ASNRangeImportForm
  
  
  @register_model_view(ASNRange, 'bulk_edit', path='edit', detail=False)
  class ASNRangeBulkEditView(generic.BulkEditView):
      queryset = ASNRange.objects.annotate_asn_counts()
      filterset = filtersets.ASNRangeFilterSet
      table = tables.ASNRangeTable
      form = forms.ASNRangeBulkEditForm
  
  
  @register_model_view(ASNRange, 'bulk_rename', path='rename', detail=False)
  class ASNRangeBulkRenameView(generic.BulkRenameView):
      queryset = ASNRange.objects.all()
      filterset = filtersets.ASNRangeFilterSet
  
  
  @register_model_view(ASNRange, 'bulk_delete', path='delete', detail=False)
  class ASNRangeBulkDeleteView(generic.BulkDeleteView):
      queryset = ASNRange.objects.annotate_asn_counts()
      filterset = filtersets.ASNRangeFilterSet
      table = tables.ASNRangeTable
  
  
  #
  # ASNs
  #
  
  @register_model_view(ASN, 'list', path='', detail=False)
  class ASNListView(generic.ObjectListView):
      queryset = ASN.objects.annotate(
          site_count=count_related(Site, 'asns'),
          provider_count=count_related(Provider, 'asns')
      )
      filterset = filtersets.ASNFilterSet
      filterset_form = forms.ASNFilterForm
      table = tables.ASNTable
  
  
  @register_model_view(ASN)
  class ASNView(GetRelatedModelsMixin, generic.ObjectView):
      queryset = ASN.objects.all()
  
      def get_extra_context(self, request, instance):
          return {
              'related_models': self.get_related_models(
                  request,
                  instance,
                  extra=(
                      (Site.objects.restrict(request.user, 'view').filter(asns__in=[instance]), 'asn_id'),
                      (Provider.objects.restrict(request.user, 'view').filter(asns__in=[instance]), 'asn_id'),
                  ),
              ),
          }
  
  
  @register_model_view(ASN, 'add', detail=False)
  @register_model_view(ASN, 'edit')
  class ASNEditView(generic.ObjectEditView):
      queryset = ASN.objects.all()
      form = forms.ASNForm
  
  
  @register_model_view(ASN, 'delete')
  class ASNDeleteView(generic.ObjectDeleteView):
      queryset = ASN.objects.all()
  
  
  @register_model_view(ASN, 'bulk_import', path='import', detail=False)
  class ASNBulkImportView(generic.BulkImportView):
      queryset = ASN.objects.all()
      model_form = forms.ASNImportForm
  
  
  @register_model_view(ASN, 'bulk_edit', path='edit', detail=False)
  class ASNBulkEditView(generic.BulkEditView):
      queryset = ASN.objects.annotate(
          site_count=count_related(Site, 'asns')
      )
      filterset = filtersets.ASNFilterSet
      table = tables.ASNTable
      form = forms.ASNBulkEditForm
  
  
  @register_model_view(ASN, 'bulk_rename', path='rename', detail=False)
  class ASNBulkRenameView(generic.BulkRenameView):
      queryset = ASN.objects.all()
      filterset = filtersets.ASNFilterSet
  
  
  @register_model_view(ASN, 'bulk_delete', path='delete', detail=False)
  class ASNBulkDeleteView(generic.BulkDeleteView):
      queryset = ASN.objects.annotate(
          site_count=count_related(Site, 'asns')
      )
      filterset = filtersets.ASNFilterSet
      table = tables.ASNTable
  
  
  #
  # Aggregates
  #
  
  @register_model_view(Aggregate, 'list', path='', detail=False)
  class AggregateListView(generic.ObjectListView):
      queryset = Aggregate.objects.annotate(
          child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ())
      )
      filterset = filtersets.AggregateFilterSet
      filterset_form = forms.AggregateFilterForm
      table = tables.AggregateTable
      actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete)
  
  
  @register_model_view(Aggregate)
  class AggregateView(generic.ObjectView):
      queryset = Aggregate.objects.all()
  
  
  @register_model_view(Aggregate, 'prefixes')
  class AggregatePrefixesView(generic.ObjectChildrenView):
      queryset = Aggregate.objects.all()
      child_model = Prefix
      table = tables.PrefixTable
      filterset = filtersets.PrefixFilterSet
      filterset_form = forms.PrefixFilterForm
      template_name = 'ipam/aggregate/prefixes.html'
      tab = ViewTab(
          label=_('Prefixes'),
          badge=lambda x: x.get_child_prefixes().count(),
          permission='ipam.view_prefix',
          weight=500
      )
  
      def get_children(self, request, parent):
          return Prefix.objects.restrict(request.user, 'view').filter(
              prefix__net_contained_or_equal=str(parent.prefix)
          ).prefetch_related('scope', 'role', 'tenant', 'tenant__group', 'vlan')
  
      def prep_table_data(self, request, queryset, parent):
          # Determine whether to show assigned prefixes, available prefixes, or both
          show_available = bool(request.GET.get('show_available', 'true') == 'true')
          show_assigned = bool(request.GET.get('show_assigned', 'true') == 'true')
  
          return add_requested_prefixes(parent.prefix, queryset, show_available, show_assigned)
  
      def get_extra_context(self, request, instance):
          return {
              'bulk_querystring': f'within={instance.prefix}',
              'first_available_prefix': instance.get_first_available_prefix(),
              'show_available': bool(request.GET.get('show_available', 'true') == 'true'),
              'show_assigned': bool(request.GET.get('show_assigned', 'true') == 'true'),
          }
  
  
  @register_model_view(Aggregate, 'add', detail=False)
  @register_model_view(Aggregate, 'edit')
  class AggregateEditView(generic.ObjectEditView):
      queryset = Aggregate.objects.all()
      form = forms.AggregateForm
  
  
  @register_model_view(Aggregate, 'delete')
  class AggregateDeleteView(generic.ObjectDeleteView):
      queryset = Aggregate.objects.all()
  
  
  @register_model_view(Aggregate, 'bulk_import', path='import', detail=False)
  class AggregateBulkImportView(generic.BulkImportView):
      queryset = Aggregate.objects.all()
      model_form = forms.AggregateImportForm
  
  
  @register_model_view(Aggregate, 'bulk_edit', path='edit', detail=False)
  class AggregateBulkEditView(generic.BulkEditView):
      queryset = Aggregate.objects.annotate(
          child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ())
      )
      filterset = filtersets.AggregateFilterSet
      table = tables.AggregateTable
      form = forms.AggregateBulkEditForm
  
  
  @register_model_view(Aggregate, 'bulk_delete', path='delete', detail=False)
  class AggregateBulkDeleteView(generic.BulkDeleteView):
      queryset = Aggregate.objects.annotate(
          child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ())
      )
      filterset = filtersets.AggregateFilterSet
      table = tables.AggregateTable
  
  
  #
  # Prefix/VLAN roles
  #
  
  @register_model_view(Role, 'list', path='', detail=False)
  class RoleListView(generic.ObjectListView):
      queryset = Role.objects.annotate(
          prefix_count=count_related(Prefix, 'role'),
          iprange_count=count_related(IPRange, 'role'),
          vlan_count=count_related(VLAN, 'role')
      )
      filterset = filtersets.RoleFilterSet
      filterset_form = forms.RoleFilterForm
      table = tables.RoleTable
  
  
  @register_model_view(Role)
  class RoleView(GetRelatedModelsMixin, generic.ObjectView):
      queryset = Role.objects.all()
  
      def get_extra_context(self, request, instance):
          return {
              'related_models': self.get_related_models(request, instance),
          }
  
  
  @register_model_view(Role, 'add', detail=False)
  @register_model_view(Role, 'edit')
  class RoleEditView(generic.ObjectEditView):
      queryset = Role.objects.all()
      form = forms.RoleForm
  
  
  @register_model_view(Role, 'delete')
  class RoleDeleteView(generic.ObjectDeleteView):
      queryset = Role.objects.all()
  
  
  @register_model_view(Role, 'bulk_import', path='import', detail=False)
  class RoleBulkImportView(generic.BulkImportView):
      queryset = Role.objects.all()
      model_form = forms.RoleImportForm
  
  
  @register_model_view(Role, 'bulk_edit', path='edit', detail=False)
  class RoleBulkEditView(generic.BulkEditView):
      queryset = Role.objects.all()
      filterset = filtersets.RoleFilterSet
      table = tables.RoleTable
      form = forms.RoleBulkEditForm
  
  
  @register_model_view(Role, 'bulk_rename', path='rename', detail=False)
  class RoleBulkRenameView(generic.BulkRenameView):
      queryset = Role.objects.all()
      filterset = filtersets.RoleFilterSet
  
  
  @register_model_view(Role, 'bulk_delete', path='delete', detail=False)
  class RoleBulkDeleteView(generic.BulkDeleteView):
      queryset = Role.objects.all()
      filterset = filtersets.RoleFilterSet
      table = tables.RoleTable
  
  
  #
  # Prefixes
  #
  
  @register_model_view(Prefix, 'list', path='', detail=False)
  class PrefixListView(generic.ObjectListView):
      queryset = Prefix.objects.all()
      filterset = filtersets.PrefixFilterSet
      filterset_form = forms.PrefixFilterForm
      table = tables.PrefixTable
      template_name = 'ipam/prefix_list.html'
      actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete)
  
  
  @register_model_view(Prefix)
  class PrefixView(generic.ObjectView):
      queryset = Prefix.objects.all()
  
      def get_extra_context(self, request, instance):
          try:
              aggregate = Aggregate.objects.restrict(request.user, 'view').get(
                  prefix__net_contains_or_equals=str(instance.prefix)
              )
          except Aggregate.DoesNotExist:
              aggregate = None
  
          # Parent prefixes table
          parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
              Q(vrf=instance.vrf) | Q(vrf__isnull=True, status=PrefixStatusChoices.STATUS_CONTAINER)
          ).filter(
              prefix__net_contains=str(instance.prefix)
          ).prefetch_related(
              'scope', 'role', 'tenant', 'vlan',
          )
          parent_prefix_table = tables.PrefixTable(
              list(parent_prefixes),
              exclude=('vrf', 'utilization'),
              orderable=False
          )
          parent_prefix_table.configure(request)
  
          # Duplicate prefixes table
          duplicate_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
              vrf=instance.vrf, prefix=str(instance.prefix)
          ).exclude(
              pk=instance.pk
          ).prefetch_related(
              'scope', 'role', 'tenant', 'vlan',
          )
          duplicate_prefix_table = tables.PrefixTable(
              list(duplicate_prefixes),
              exclude=('vrf', 'utilization'),
              orderable=False
          )
          duplicate_prefix_table.configure(request)
  
          return {
              'aggregate': aggregate,
              'parent_prefix_table': parent_prefix_table,
              'duplicate_prefix_table': duplicate_prefix_table,
          }
  
  
  @register_model_view(Prefix, 'prefixes')
  class PrefixPrefixesView(generic.ObjectChildrenView):
      queryset = Prefix.objects.all()
      child_model = Prefix
      table = tables.PrefixTable
      filterset = filtersets.PrefixFilterSet
      filterset_form = forms.PrefixFilterForm
      template_name = 'ipam/prefix/prefixes.html'
      tab = ViewTab(
          label=_('Child Prefixes'),
          badge=lambda x: x.get_child_prefixes().count(),
          permission='ipam.view_prefix',
          weight=500
      )
  
      def get_children(self, request, parent):
          return parent.get_child_prefixes().restrict(request.user, 'view').prefetch_related(
              'scope', 'vrf', 'vlan', 'role', 'tenant', 'tenant__group'
          )
  
      def prep_table_data(self, request, queryset, parent):
          # Determine whether to show assigned prefixes, available prefixes, or both
          show_available = bool(request.GET.get('show_available', 'true') == 'true')
          show_assigned = bool(request.GET.get('show_assigned', 'true') == 'true')
  
          return add_requested_prefixes(parent.prefix, queryset, show_available, show_assigned)
  
      def get_extra_context(self, request, instance):
          return {
              'bulk_querystring': f"vrf_id={instance.vrf.pk if instance.vrf else '0'}&within={instance.prefix}",
              'first_available_prefix': instance.get_first_available_prefix(),
              'show_available': bool(request.GET.get('show_available', 'true') == 'true'),
              'show_assigned': bool(request.GET.get('show_assigned', 'true') == 'true'),
          }
  
  
  @register_model_view(Prefix, 'ipranges', path='ip-ranges')
  class PrefixIPRangesView(generic.ObjectChildrenView):
      queryset = Prefix.objects.all()
      child_model = IPRange
      table = tables.IPRangeTable
      filterset = filtersets.IPRangeFilterSet
      filterset_form = forms.IPRangeFilterForm
      template_name = 'ipam/prefix/ip_ranges.html'
      tab = ViewTab(
          label=_('Child Ranges'),
          badge=lambda x: x.get_child_ranges().count(),
          permission='ipam.view_iprange',
          weight=600
      )
  
      def get_children(self, request, parent):
          return parent.get_child_ranges().restrict(request.user, 'view').prefetch_related(
              'tenant__group',
          )
  
      def get_extra_context(self, request, instance):
          return {
              'bulk_querystring': f"vrf_id={instance.vrf.pk if instance.vrf else '0'}&parent={instance.prefix}",
              'first_available_ip': instance.get_first_available_ip(),
          }
  
  
  @register_model_view(Prefix, 'ipaddresses', path='ip-addresses')
  class PrefixIPAddressesView(generic.ObjectChildrenView):
      queryset = Prefix.objects.all()
      child_model = IPAddress
      table = tables.AnnotatedIPAddressTable
      filterset = filtersets.IPAddressFilterSet
      filterset_form = forms.IPAddressFilterForm
      template_name = 'ipam/prefix/ip_addresses.html'
      tab = ViewTab(
          label=_('IP Addresses'),
          badge=lambda x: x.get_child_ips().count(),
          permission='ipam.view_ipaddress',
          weight=700
      )
  
      def get_children(self, request, parent):
          return parent.get_child_ips().restrict(request.user, 'view').prefetch_related('vrf', 'tenant', 'tenant__group')
  
      def prep_table_data(self, request, queryset, parent):
          if not request.GET.get('q') and not get_table_ordering(request, self.table):
              return annotate_ip_space(parent)
          return queryset
  
      def get_extra_context(self, request, instance):
          return {
              'bulk_querystring': f"vrf_id={instance.vrf.pk if instance.vrf else '0'}&parent={instance.prefix}",
              'first_available_ip': instance.get_first_available_ip(),
          }
  
  
  @register_model_view(Prefix, 'add', detail=False)
  @register_model_view(Prefix, 'edit')
  class PrefixEditView(generic.ObjectEditView):
      queryset = Prefix.objects.all()
      form = forms.PrefixForm
  
  
  @register_model_view(Prefix, 'delete')
  class PrefixDeleteView(generic.ObjectDeleteView):
      queryset = Prefix.objects.all()
  
  
  @register_model_view(Prefix, 'bulk_import', path='import', detail=False)
  class PrefixBulkImportView(generic.BulkImportView):
      queryset = Prefix.objects.all()
      model_form = forms.PrefixImportForm
  
  
  @register_model_view(Prefix, 'bulk_edit', path='edit', detail=False)
  class PrefixBulkEditView(generic.BulkEditView):
      queryset = Prefix.objects.prefetch_related('vrf__tenant')
      filterset = filtersets.PrefixFilterSet
      table = tables.PrefixTable
      form = forms.PrefixBulkEditForm
  
  
  @register_model_view(Prefix, 'bulk_delete', path='delete', detail=False)
  class PrefixBulkDeleteView(generic.BulkDeleteView):
      queryset = Prefix.objects.prefetch_related('vrf__tenant')
      filterset = filtersets.PrefixFilterSet
      table = tables.PrefixTable
  
  
  #
  # IP Ranges
  #
  
  @register_model_view(IPRange, 'list', path='', detail=False)
  class IPRangeListView(generic.ObjectListView):
      queryset = IPRange.objects.all()
      filterset = filtersets.IPRangeFilterSet
      filterset_form = forms.IPRangeFilterForm
      table = tables.IPRangeTable
  
  
  @register_model_view(IPRange)
  class IPRangeView(generic.ObjectView):
      queryset = IPRange.objects.all()
  
      def get_extra_context(self, request, instance):
  
          # Parent prefixes table
          parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
              Q(prefix__net_contains_or_equals=str(instance.start_address.ip)),
              Q(prefix__net_contains_or_equals=str(instance.end_address.ip)),
              vrf=instance.vrf
          ).prefetch_related(
              'scope', 'role', 'tenant', 'vlan', 'role'
          )
          parent_prefixes_table = tables.PrefixTable(
              list(parent_prefixes),
              exclude=('vrf', 'utilization'),
              orderable=False
          )
          parent_prefixes_table.configure(request)
  
          return {
              'parent_prefixes_table': parent_prefixes_table,
          }
  
  
  @register_model_view(IPRange, 'ipaddresses', path='ip-addresses')
  class IPRangeIPAddressesView(generic.ObjectChildrenView):
      queryset = IPRange.objects.all()
      child_model = IPAddress
      table = tables.IPAddressTable
      filterset = filtersets.IPAddressFilterSet
      filterset_form = forms.IPRangeFilterForm
      template_name = 'ipam/iprange/ip_addresses.html'
      tab = ViewTab(
          label=_('IP Addresses'),
          badge=lambda x: x.get_child_ips().count(),
          permission='ipam.view_ipaddress',
          weight=500
      )
  
      def get_children(self, request, parent):
          return parent.get_child_ips().restrict(request.user, 'view')
  
  
  @register_model_view(IPRange, 'add', detail=False)
  @register_model_view(IPRange, 'edit')
  class IPRangeEditView(generic.ObjectEditView):
      queryset = IPRange.objects.all()
      form = forms.IPRangeForm
  
  
  @register_model_view(IPRange, 'delete')
  class IPRangeDeleteView(generic.ObjectDeleteView):
      queryset = IPRange.objects.all()
  
  
  @register_model_view(IPRange, 'bulk_import', path='import', detail=False)
  class IPRangeBulkImportView(generic.BulkImportView):
      queryset = IPRange.objects.all()
      model_form = forms.IPRangeImportForm
  
  
  @register_model_view(IPRange, 'bulk_edit', path='edit', detail=False)
  class IPRangeBulkEditView(generic.BulkEditView):
      queryset = IPRange.objects.all()
      filterset = filtersets.IPRangeFilterSet
      table = tables.IPRangeTable
      form = forms.IPRangeBulkEditForm
  
  
  @register_model_view(IPRange, 'bulk_rename', path='rename', detail=False)
  class IPRangeBulkRenameView(generic.BulkRenameView):
      queryset = IPRange.objects.all()
      filterset = filtersets.IPRangeFilterSet
  
  
  @register_model_view(IPRange, 'bulk_delete', path='delete', detail=False)
  class IPRangeBulkDeleteView(generic.BulkDeleteView):
      queryset = IPRange.objects.all()
      filterset = filtersets.IPRangeFilterSet
      table = tables.IPRangeTable
  
  
  #
  # IP addresses
  #
  
  @register_model_view(IPAddress, 'list', path='', detail=False)
  class IPAddressListView(generic.ObjectListView):
      queryset = IPAddress.objects.all()
      filterset = filtersets.IPAddressFilterSet
      filterset_form = forms.IPAddressFilterForm
      table = tables.IPAddressTable
      actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete)
  
  
  @register_model_view(IPAddress)
  class IPAddressView(generic.ObjectView):
      queryset = IPAddress.objects.prefetch_related('vrf__tenant', 'tenant')
  
      def get_extra_context(self, request, instance):
          # Parent prefixes table
          parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
              vrf=instance.vrf,
              prefix__net_contains_or_equals=str(instance.address.ip)
          ).prefetch_related(
              'scope', 'role'
          )
          parent_prefixes_table = tables.PrefixTable(
              list(parent_prefixes),
              exclude=('vrf', 'utilization'),
              orderable=False
          )
          parent_prefixes_table.configure(request)
  
          # Duplicate IPs table
          duplicate_ips = IPAddress.objects.restrict(request.user, 'view').filter(
              vrf=instance.vrf,
              address=str(instance.address)
          ).exclude(
              pk=instance.pk
          ).prefetch_related(
              'nat_inside'
          )
          # Exclude anycast IPs if this IP is anycast
          if instance.role == IPAddressRoleChoices.ROLE_ANYCAST:
              duplicate_ips = duplicate_ips.exclude(role=IPAddressRoleChoices.ROLE_ANYCAST)
          # Limit to a maximum of 10 duplicates displayed here
          duplicate_ips_table = tables.IPAddressTable(duplicate_ips[:10], orderable=False)
          duplicate_ips_table.configure(request)
  
          return {
              'parent_prefixes_table': parent_prefixes_table,
              'duplicate_ips_table': duplicate_ips_table,
          }
  
  
  @register_model_view(IPAddress, 'add', detail=False)
  @register_model_view(IPAddress, 'edit')
  class IPAddressEditView(generic.ObjectEditView):
      queryset = IPAddress.objects.all()
      form = forms.IPAddressForm
      template_name = 'ipam/ipaddress_edit.html'
  
      def alter_object(self, obj, request, url_args, url_kwargs):
  
          if 'interface' in request.GET:
              try:
                  obj.assigned_object = Interface.objects.get(pk=request.GET['interface'])
              except (ValueError, Interface.DoesNotExist):
                  pass
  
          elif 'vminterface' in request.GET:
              try:
                  obj.assigned_object = VMInterface.objects.get(pk=request.GET['vminterface'])
              except (ValueError, VMInterface.DoesNotExist):
                  pass
  
          elif 'fhrpgroup' in request.GET:
              try:
                  obj.assigned_object = FHRPGroup.objects.get(pk=request.GET['fhrpgroup'])
              except (ValueError, FHRPGroup.DoesNotExist):
                  pass
  
          return obj
  
      def get_extra_addanother_params(self, request):
          if 'interface' in request.GET:
              return {'interface': request.GET['interface']}
          elif 'vminterface' in request.GET:
              return {'vminterface': request.GET['vminterface']}
          return {}
  
  
  # TODO: Standardize or remove this view
  @register_model_view(IPAddress, 'assign', path='assign', detail=False)
  class IPAddressAssignView(generic.ObjectView):
      """
      Search for IPAddresses to be assigned to an Interface.
      """
      queryset = IPAddress.objects.all()
  
      def dispatch(self, request, *args, **kwargs):
  
          # Redirect user if an interface has not been provided
          if 'interface' not in request.GET and 'vminterface' not in request.GET:
              return redirect('ipam:ipaddress_add')
  
          return super().dispatch(request, *args, **kwargs)
  
      def get(self, request):
          form = forms.IPAddressAssignForm()
  
          return render(request, 'ipam/ipaddress_assign.html', {
              'form': form,
              'return_url': request.GET.get('return_url', ''),
          })
  
      def post(self, request):
          form = forms.IPAddressAssignForm(request.POST)
          table = None
  
          if form.is_valid():
              addresses = self.queryset.prefetch_related('vrf', 'tenant')
              # Limit to 100 results
              addresses = filtersets.IPAddressFilterSet(request.POST, addresses).qs[:100]
              table = tables.IPAddressAssignTable(addresses)
              table.configure(request)
  
          return render(request, 'ipam/ipaddress_assign.html', {
              'form': form,
              'table': table,
              'return_url': request.GET.get('return_url'),
          })
  
  
  @register_model_view(IPAddress, 'delete')
  class IPAddressDeleteView(generic.ObjectDeleteView):
      queryset = IPAddress.objects.all()
  
  
  @register_model_view(IPAddress, 'bulk_add', path='bulk-add', detail=False)
  class IPAddressBulkCreateView(generic.BulkCreateView):
      queryset = IPAddress.objects.all()
      form = forms.IPAddressBulkCreateForm
      model_form = forms.IPAddressBulkAddForm
      pattern_target = 'address'
      template_name = 'ipam/ipaddress_bulk_add.html'
  
  
  