Просмотр исходного кода

Migrate all ObjectView subclasses to use get_extra_context()

Jeremy Stretch 5 лет назад
Родитель
Сommit
1d922ee813

+ 11 - 14
netbox/circuits/views.py

@@ -26,11 +26,9 @@ class ProviderListView(generic.ObjectListView):
 class ProviderView(generic.ObjectView):
 class ProviderView(generic.ObjectView):
     queryset = Provider.objects.all()
     queryset = Provider.objects.all()
 
 
-    def get(self, request, slug):
-
-        provider = get_object_or_404(self.queryset, slug=slug)
+    def get_extra_context(self, request, instance):
         circuits = Circuit.objects.restrict(request.user, 'view').filter(
         circuits = Circuit.objects.restrict(request.user, 'view').filter(
-            provider=provider
+            provider=instance
         ).prefetch_related(
         ).prefetch_related(
             'type', 'tenant', 'terminations__site'
             'type', 'tenant', 'terminations__site'
         ).annotate_sites()
         ).annotate_sites()
@@ -44,10 +42,9 @@ class ProviderView(generic.ObjectView):
         }
         }
         RequestConfig(request, paginate).configure(circuits_table)
         RequestConfig(request, paginate).configure(circuits_table)
 
 
-        return render(request, 'circuits/provider.html', {
-            'object': provider,
+        return {
             'circuits_table': circuits_table,
             'circuits_table': circuits_table,
-        })
+        }
 
 
 
 
 class ProviderEditView(generic.ObjectEditView):
 class ProviderEditView(generic.ObjectEditView):
@@ -124,30 +121,30 @@ class CircuitListView(generic.ObjectListView):
 class CircuitView(generic.ObjectView):
 class CircuitView(generic.ObjectView):
     queryset = Circuit.objects.all()
     queryset = Circuit.objects.all()
 
 
-    def get(self, request, pk):
-        circuit = get_object_or_404(self.queryset, pk=pk)
+    def get_extra_context(self, request, instance):
 
 
+        # A-side termination
         termination_a = CircuitTermination.objects.restrict(request.user, 'view').prefetch_related(
         termination_a = CircuitTermination.objects.restrict(request.user, 'view').prefetch_related(
             'site__region'
             'site__region'
         ).filter(
         ).filter(
-            circuit=circuit, term_side=CircuitTerminationSideChoices.SIDE_A
+            circuit=instance, term_side=CircuitTerminationSideChoices.SIDE_A
         ).first()
         ).first()
         if termination_a and termination_a.connected_endpoint:
         if termination_a and termination_a.connected_endpoint:
             termination_a.ip_addresses = termination_a.connected_endpoint.ip_addresses.restrict(request.user, 'view')
             termination_a.ip_addresses = termination_a.connected_endpoint.ip_addresses.restrict(request.user, 'view')
 
 
+        # Z-side termination
         termination_z = CircuitTermination.objects.restrict(request.user, 'view').prefetch_related(
         termination_z = CircuitTermination.objects.restrict(request.user, 'view').prefetch_related(
             'site__region'
             'site__region'
         ).filter(
         ).filter(
-            circuit=circuit, term_side=CircuitTerminationSideChoices.SIDE_Z
+            circuit=instance, term_side=CircuitTerminationSideChoices.SIDE_Z
         ).first()
         ).first()
         if termination_z and termination_z.connected_endpoint:
         if termination_z and termination_z.connected_endpoint:
             termination_z.ip_addresses = termination_z.connected_endpoint.ip_addresses.restrict(request.user, 'view')
             termination_z.ip_addresses = termination_z.connected_endpoint.ip_addresses.restrict(request.user, 'view')
 
 
-        return render(request, 'circuits/circuit.html', {
-            'object': circuit,
+        return {
             'termination_a': termination_a,
             'termination_a': termination_a,
             'termination_z': termination_z,
             'termination_z': termination_z,
-        })
+        }
 
 
 
 
 class CircuitEditView(generic.ObjectEditView):
 class CircuitEditView(generic.ObjectEditView):

+ 124 - 160
netbox/dcim/views.py

@@ -152,16 +152,14 @@ class SiteListView(generic.ObjectListView):
 class SiteView(generic.ObjectView):
 class SiteView(generic.ObjectView):
     queryset = Site.objects.prefetch_related('region', 'tenant__group')
     queryset = Site.objects.prefetch_related('region', 'tenant__group')
 
 
-    def get(self, request, slug):
-
-        site = get_object_or_404(self.queryset, slug=slug)
+    def get_extra_context(self, request, instance):
         stats = {
         stats = {
-            'rack_count': Rack.objects.restrict(request.user, 'view').filter(site=site).count(),
-            'device_count': Device.objects.restrict(request.user, 'view').filter(site=site).count(),
-            'prefix_count': Prefix.objects.restrict(request.user, 'view').filter(site=site).count(),
-            'vlan_count': VLAN.objects.restrict(request.user, 'view').filter(site=site).count(),
-            'circuit_count': Circuit.objects.restrict(request.user, 'view').filter(terminations__site=site).count(),
-            'vm_count': VirtualMachine.objects.restrict(request.user, 'view').filter(cluster__site=site).count(),
+            'rack_count': Rack.objects.restrict(request.user, 'view').filter(site=instance).count(),
+            'device_count': Device.objects.restrict(request.user, 'view').filter(site=instance).count(),
+            'prefix_count': Prefix.objects.restrict(request.user, 'view').filter(site=instance).count(),
+            'vlan_count': VLAN.objects.restrict(request.user, 'view').filter(site=instance).count(),
+            'circuit_count': Circuit.objects.restrict(request.user, 'view').filter(terminations__site=instance).count(),
+            'vm_count': VirtualMachine.objects.restrict(request.user, 'view').filter(cluster__site=instance).count(),
         }
         }
         rack_groups = RackGroup.objects.add_related_count(
         rack_groups = RackGroup.objects.add_related_count(
             RackGroup.objects.all(),
             RackGroup.objects.all(),
@@ -169,13 +167,12 @@ class SiteView(generic.ObjectView):
             'group',
             'group',
             'rack_count',
             'rack_count',
             cumulative=True
             cumulative=True
-        ).restrict(request.user, 'view').filter(site=site)
+        ).restrict(request.user, 'view').filter(site=instance)
 
 
-        return render(request, 'dcim/site.html', {
-            'object': site,
+        return {
             'stats': stats,
             'stats': stats,
             'rack_groups': rack_groups,
             'rack_groups': rack_groups,
-        })
+        }
 
 
 
 
 class SiteEditView(generic.ObjectEditView):
 class SiteEditView(generic.ObjectEditView):
@@ -338,36 +335,37 @@ class RackElevationListView(generic.ObjectListView):
 class RackView(generic.ObjectView):
 class RackView(generic.ObjectView):
     queryset = Rack.objects.prefetch_related('site__region', 'tenant__group', 'group', 'role')
     queryset = Rack.objects.prefetch_related('site__region', 'tenant__group', 'group', 'role')
 
 
-    def get(self, request, pk):
-        rack = get_object_or_404(self.queryset, pk=pk)
-
+    def get_extra_context(self, request, instance):
         # Get 0U and child devices located within the rack
         # Get 0U and child devices located within the rack
         nonracked_devices = Device.objects.filter(
         nonracked_devices = Device.objects.filter(
-            rack=rack,
+            rack=instance,
             position__isnull=True
             position__isnull=True
         ).prefetch_related('device_type__manufacturer')
         ).prefetch_related('device_type__manufacturer')
 
 
-        peer_racks = Rack.objects.restrict(request.user, 'view').filter(site=rack.site)
+        peer_racks = Rack.objects.restrict(request.user, 'view').filter(site=instance.site)
 
 
-        if rack.group:
-            peer_racks = peer_racks.filter(group=rack.group)
+        if instance.group:
+            peer_racks = peer_racks.filter(group=instance.group)
         else:
         else:
             peer_racks = peer_racks.filter(group__isnull=True)
             peer_racks = peer_racks.filter(group__isnull=True)
-        next_rack = peer_racks.filter(name__gt=rack.name).order_by('name').first()
-        prev_rack = peer_racks.filter(name__lt=rack.name).order_by('-name').first()
+        next_rack = peer_racks.filter(name__gt=instance.name).order_by('name').first()
+        prev_rack = peer_racks.filter(name__lt=instance.name).order_by('-name').first()
 
 
-        reservations = RackReservation.objects.restrict(request.user, 'view').filter(rack=rack)
-        power_feeds = PowerFeed.objects.restrict(request.user, 'view').filter(rack=rack).prefetch_related('power_panel')
+        reservations = RackReservation.objects.restrict(request.user, 'view').filter(rack=instance)
+        power_feeds = PowerFeed.objects.restrict(request.user, 'view').filter(rack=instance).prefetch_related(
+            'power_panel'
+        )
+
+        device_count = Device.objects.restrict(request.user, 'view').filter(rack=instance).count()
 
 
-        return render(request, 'dcim/rack.html', {
-            'object': rack,
-            'device_count': Device.objects.restrict(request.user, 'view').filter(rack=rack).count(),
+        return {
+            'device_count': device_count,
             'reservations': reservations,
             'reservations': reservations,
             'power_feeds': power_feeds,
             'power_feeds': power_feeds,
             'nonracked_devices': nonracked_devices,
             'nonracked_devices': nonracked_devices,
             'next_rack': next_rack,
             'next_rack': next_rack,
             'prev_rack': prev_rack,
             'prev_rack': prev_rack,
-        })
+        }
 
 
 
 
 class RackEditView(generic.ObjectEditView):
 class RackEditView(generic.ObjectEditView):
@@ -511,42 +509,40 @@ class DeviceTypeListView(generic.ObjectListView):
 class DeviceTypeView(generic.ObjectView):
 class DeviceTypeView(generic.ObjectView):
     queryset = DeviceType.objects.prefetch_related('manufacturer')
     queryset = DeviceType.objects.prefetch_related('manufacturer')
 
 
-    def get(self, request, pk):
-
-        devicetype = get_object_or_404(self.queryset, pk=pk)
-        instance_count = Device.objects.restrict(request.user).filter(device_type=devicetype).count()
+    def get_extra_context(self, request, instance):
+        instance_count = Device.objects.restrict(request.user).filter(device_type=instance).count()
 
 
         # Component tables
         # Component tables
         consoleport_table = tables.ConsolePortTemplateTable(
         consoleport_table = tables.ConsolePortTemplateTable(
-            ConsolePortTemplate.objects.restrict(request.user, 'view').filter(device_type=devicetype),
+            ConsolePortTemplate.objects.restrict(request.user, 'view').filter(device_type=instance),
             orderable=False
             orderable=False
         )
         )
         consoleserverport_table = tables.ConsoleServerPortTemplateTable(
         consoleserverport_table = tables.ConsoleServerPortTemplateTable(
-            ConsoleServerPortTemplate.objects.restrict(request.user, 'view').filter(device_type=devicetype),
+            ConsoleServerPortTemplate.objects.restrict(request.user, 'view').filter(device_type=instance),
             orderable=False
             orderable=False
         )
         )
         powerport_table = tables.PowerPortTemplateTable(
         powerport_table = tables.PowerPortTemplateTable(
-            PowerPortTemplate.objects.restrict(request.user, 'view').filter(device_type=devicetype),
+            PowerPortTemplate.objects.restrict(request.user, 'view').filter(device_type=instance),
             orderable=False
             orderable=False
         )
         )
         poweroutlet_table = tables.PowerOutletTemplateTable(
         poweroutlet_table = tables.PowerOutletTemplateTable(
-            PowerOutletTemplate.objects.restrict(request.user, 'view').filter(device_type=devicetype),
+            PowerOutletTemplate.objects.restrict(request.user, 'view').filter(device_type=instance),
             orderable=False
             orderable=False
         )
         )
         interface_table = tables.InterfaceTemplateTable(
         interface_table = tables.InterfaceTemplateTable(
-            list(InterfaceTemplate.objects.restrict(request.user, 'view').filter(device_type=devicetype)),
+            list(InterfaceTemplate.objects.restrict(request.user, 'view').filter(device_type=instance)),
             orderable=False
             orderable=False
         )
         )
         front_port_table = tables.FrontPortTemplateTable(
         front_port_table = tables.FrontPortTemplateTable(
-            FrontPortTemplate.objects.restrict(request.user, 'view').filter(device_type=devicetype),
+            FrontPortTemplate.objects.restrict(request.user, 'view').filter(device_type=instance),
             orderable=False
             orderable=False
         )
         )
         rear_port_table = tables.RearPortTemplateTable(
         rear_port_table = tables.RearPortTemplateTable(
-            RearPortTemplate.objects.restrict(request.user, 'view').filter(device_type=devicetype),
+            RearPortTemplate.objects.restrict(request.user, 'view').filter(device_type=instance),
             orderable=False
             orderable=False
         )
         )
         devicebay_table = tables.DeviceBayTemplateTable(
         devicebay_table = tables.DeviceBayTemplateTable(
-            DeviceBayTemplate.objects.restrict(request.user, 'view').filter(device_type=devicetype),
+            DeviceBayTemplate.objects.restrict(request.user, 'view').filter(device_type=instance),
             orderable=False
             orderable=False
         )
         )
         if request.user.has_perm('dcim.change_devicetype'):
         if request.user.has_perm('dcim.change_devicetype'):
@@ -559,8 +555,7 @@ class DeviceTypeView(generic.ObjectView):
             rear_port_table.columns.show('pk')
             rear_port_table.columns.show('pk')
             devicebay_table.columns.show('pk')
             devicebay_table.columns.show('pk')
 
 
-        return render(request, 'dcim/devicetype.html', {
-            'object': devicetype,
+        return {
             'instance_count': instance_count,
             'instance_count': instance_count,
             'consoleport_table': consoleport_table,
             'consoleport_table': consoleport_table,
             'consoleserverport_table': consoleserverport_table,
             'consoleserverport_table': consoleserverport_table,
@@ -570,7 +565,7 @@ class DeviceTypeView(generic.ObjectView):
             'front_port_table': front_port_table,
             'front_port_table': front_port_table,
             'rear_port_table': rear_port_table,
             'rear_port_table': rear_port_table,
             'devicebay_table': devicebay_table,
             'devicebay_table': devicebay_table,
-        })
+        }
 
 
 
 
 class DeviceTypeEditView(generic.ObjectEditView):
 class DeviceTypeEditView(generic.ObjectEditView):
@@ -986,51 +981,47 @@ class DeviceView(generic.ObjectView):
     queryset = Device.objects.prefetch_related(
     queryset = Device.objects.prefetch_related(
         'site__region', 'rack__group', 'tenant__group', 'device_role', 'platform', 'primary_ip4', 'primary_ip6'
         'site__region', 'rack__group', 'tenant__group', 'device_role', 'platform', 'primary_ip4', 'primary_ip6'
     )
     )
+    template_name = 'dcim/device/device.html'
 
 
-    def get(self, request, pk):
-
-        device = get_object_or_404(self.queryset, pk=pk)
-
+    def get_extra_context(self, request, instance):
         # VirtualChassis members
         # VirtualChassis members
-        if device.virtual_chassis is not None:
+        if instance.virtual_chassis is not None:
             vc_members = Device.objects.restrict(request.user, 'view').filter(
             vc_members = Device.objects.restrict(request.user, 'view').filter(
-                virtual_chassis=device.virtual_chassis
+                virtual_chassis=instance.virtual_chassis
             ).order_by('vc_position')
             ).order_by('vc_position')
         else:
         else:
             vc_members = []
             vc_members = []
 
 
         # Services
         # Services
-        services = Service.objects.restrict(request.user, 'view').filter(device=device)
+        services = Service.objects.restrict(request.user, 'view').filter(device=instance)
 
 
         # Secrets
         # Secrets
-        secrets = Secret.objects.restrict(request.user, 'view').filter(device=device)
+        secrets = Secret.objects.restrict(request.user, 'view').filter(device=instance)
 
 
         # Find up to ten devices in the same site with the same functional role for quick reference.
         # Find up to ten devices in the same site with the same functional role for quick reference.
         related_devices = Device.objects.restrict(request.user, 'view').filter(
         related_devices = Device.objects.restrict(request.user, 'view').filter(
-            site=device.site, device_role=device.device_role
+            site=instance.site, device_role=instance.device_role
         ).exclude(
         ).exclude(
-            pk=device.pk
+            pk=instance.pk
         ).prefetch_related(
         ).prefetch_related(
             'rack', 'device_type__manufacturer'
             'rack', 'device_type__manufacturer'
         )[:10]
         )[:10]
 
 
-        return render(request, 'dcim/device/device.html', {
-            'object': device,
+        return {
             'services': services,
             'services': services,
             'secrets': secrets,
             'secrets': secrets,
             'vc_members': vc_members,
             'vc_members': vc_members,
             'related_devices': related_devices,
             'related_devices': related_devices,
             'active_tab': 'device',
             'active_tab': 'device',
-        })
+        }
 
 
 
 
 class DeviceConsolePortsView(generic.ObjectView):
 class DeviceConsolePortsView(generic.ObjectView):
     queryset = Device.objects.all()
     queryset = Device.objects.all()
+    template_name = 'dcim/device/consoleports.html'
 
 
-    def get(self, request, pk):
-        device = get_object_or_404(self.queryset, pk=pk)
-
-        consoleports = ConsolePort.objects.restrict(request.user, 'view').filter(device=device).prefetch_related(
+    def get_extra_context(self, request, instance):
+        consoleports = ConsolePort.objects.restrict(request.user, 'view').filter(device=instance).prefetch_related(
             'cable', '_path__destination',
             'cable', '_path__destination',
         )
         )
         consoleport_table = tables.DeviceConsolePortTable(
         consoleport_table = tables.DeviceConsolePortTable(
@@ -1041,21 +1032,19 @@ class DeviceConsolePortsView(generic.ObjectView):
         if request.user.has_perm('dcim.change_consoleport') or request.user.has_perm('dcim.delete_consoleport'):
         if request.user.has_perm('dcim.change_consoleport') or request.user.has_perm('dcim.delete_consoleport'):
             consoleport_table.columns.show('pk')
             consoleport_table.columns.show('pk')
 
 
-        return render(request, 'dcim/device/consoleports.html', {
-            'object': device,
+        return {
             'consoleport_table': consoleport_table,
             'consoleport_table': consoleport_table,
             'active_tab': 'console-ports',
             'active_tab': 'console-ports',
-        })
+        }
 
 
 
 
 class DeviceConsoleServerPortsView(generic.ObjectView):
 class DeviceConsoleServerPortsView(generic.ObjectView):
     queryset = Device.objects.all()
     queryset = Device.objects.all()
+    template_name = 'dcim/device/consoleserverports.html'
 
 
-    def get(self, request, pk):
-        device = get_object_or_404(self.queryset, pk=pk)
-
+    def get_extra_context(self, request, instance):
         consoleserverports = ConsoleServerPort.objects.restrict(request.user, 'view').filter(
         consoleserverports = ConsoleServerPort.objects.restrict(request.user, 'view').filter(
-            device=device
+            device=instance
         ).prefetch_related(
         ).prefetch_related(
             'cable', '_path__destination',
             'cable', '_path__destination',
         )
         )
@@ -1068,20 +1057,18 @@ class DeviceConsoleServerPortsView(generic.ObjectView):
                 request.user.has_perm('dcim.delete_consoleserverport'):
                 request.user.has_perm('dcim.delete_consoleserverport'):
             consoleserverport_table.columns.show('pk')
             consoleserverport_table.columns.show('pk')
 
 
-        return render(request, 'dcim/device/consoleserverports.html', {
-            'object': device,
+        return {
             'consoleserverport_table': consoleserverport_table,
             'consoleserverport_table': consoleserverport_table,
             'active_tab': 'console-server-ports',
             'active_tab': 'console-server-ports',
-        })
+        }
 
 
 
 
 class DevicePowerPortsView(generic.ObjectView):
 class DevicePowerPortsView(generic.ObjectView):
     queryset = Device.objects.all()
     queryset = Device.objects.all()
+    template_name = 'dcim/device/powerports.html'
 
 
-    def get(self, request, pk):
-        device = get_object_or_404(self.queryset, pk=pk)
-
-        powerports = PowerPort.objects.restrict(request.user, 'view').filter(device=device).prefetch_related(
+    def get_extra_context(self, request, instance):
+        powerports = PowerPort.objects.restrict(request.user, 'view').filter(device=instance).prefetch_related(
             'cable', '_path__destination',
             'cable', '_path__destination',
         )
         )
         powerport_table = tables.DevicePowerPortTable(
         powerport_table = tables.DevicePowerPortTable(
@@ -1092,20 +1079,18 @@ class DevicePowerPortsView(generic.ObjectView):
         if request.user.has_perm('dcim.change_powerport') or request.user.has_perm('dcim.delete_powerport'):
         if request.user.has_perm('dcim.change_powerport') or request.user.has_perm('dcim.delete_powerport'):
             powerport_table.columns.show('pk')
             powerport_table.columns.show('pk')
 
 
-        return render(request, 'dcim/device/powerports.html', {
-            'object': device,
+        return {
             'powerport_table': powerport_table,
             'powerport_table': powerport_table,
             'active_tab': 'power-ports',
             'active_tab': 'power-ports',
-        })
+        }
 
 
 
 
 class DevicePowerOutletsView(generic.ObjectView):
 class DevicePowerOutletsView(generic.ObjectView):
     queryset = Device.objects.all()
     queryset = Device.objects.all()
+    template_name = 'dcim/device/poweroutlets.html'
 
 
-    def get(self, request, pk):
-        device = get_object_or_404(self.queryset, pk=pk)
-
-        poweroutlets = PowerOutlet.objects.restrict(request.user, 'view').filter(device=device).prefetch_related(
+    def get_extra_context(self, request, instance):
+        poweroutlets = PowerOutlet.objects.restrict(request.user, 'view').filter(device=instance).prefetch_related(
             'cable', 'power_port', '_path__destination',
             'cable', 'power_port', '_path__destination',
         )
         )
         poweroutlet_table = tables.DevicePowerOutletTable(
         poweroutlet_table = tables.DevicePowerOutletTable(
@@ -1116,20 +1101,18 @@ class DevicePowerOutletsView(generic.ObjectView):
         if request.user.has_perm('dcim.change_poweroutlet') or request.user.has_perm('dcim.delete_poweroutlet'):
         if request.user.has_perm('dcim.change_poweroutlet') or request.user.has_perm('dcim.delete_poweroutlet'):
             poweroutlet_table.columns.show('pk')
             poweroutlet_table.columns.show('pk')
 
 
-        return render(request, 'dcim/device/poweroutlets.html', {
-            'object': device,
+        return {
             'poweroutlet_table': poweroutlet_table,
             'poweroutlet_table': poweroutlet_table,
             'active_tab': 'power-outlets',
             'active_tab': 'power-outlets',
-        })
+        }
 
 
 
 
 class DeviceInterfacesView(generic.ObjectView):
 class DeviceInterfacesView(generic.ObjectView):
     queryset = Device.objects.all()
     queryset = Device.objects.all()
+    template_name = 'dcim/device/interfaces.html'
 
 
-    def get(self, request, pk):
-        device = get_object_or_404(self.queryset, pk=pk)
-
-        interfaces = device.vc_interfaces.restrict(request.user, 'view').prefetch_related(
+    def get_extra_context(self, request, instance):
+        interfaces = instance.vc_interfaces.restrict(request.user, 'view').prefetch_related(
             Prefetch('ip_addresses', queryset=IPAddress.objects.restrict(request.user)),
             Prefetch('ip_addresses', queryset=IPAddress.objects.restrict(request.user)),
             Prefetch('member_interfaces', queryset=Interface.objects.restrict(request.user)),
             Prefetch('member_interfaces', queryset=Interface.objects.restrict(request.user)),
             'lag', 'cable', '_path__destination', 'tags',
             'lag', 'cable', '_path__destination', 'tags',
@@ -1142,20 +1125,18 @@ class DeviceInterfacesView(generic.ObjectView):
         if request.user.has_perm('dcim.change_interface') or request.user.has_perm('dcim.delete_interface'):
         if request.user.has_perm('dcim.change_interface') or request.user.has_perm('dcim.delete_interface'):
             interface_table.columns.show('pk')
             interface_table.columns.show('pk')
 
 
-        return render(request, 'dcim/device/interfaces.html', {
-            'object': device,
+        return {
             'interface_table': interface_table,
             'interface_table': interface_table,
             'active_tab': 'interfaces',
             'active_tab': 'interfaces',
-        })
+        }
 
 
 
 
 class DeviceFrontPortsView(generic.ObjectView):
 class DeviceFrontPortsView(generic.ObjectView):
     queryset = Device.objects.all()
     queryset = Device.objects.all()
+    template_name = 'dcim/device/frontports.html'
 
 
-    def get(self, request, pk):
-        device = get_object_or_404(self.queryset, pk=pk)
-
-        frontports = FrontPort.objects.restrict(request.user, 'view').filter(device=device).prefetch_related(
+    def get_extra_context(self, request, instance):
+        frontports = FrontPort.objects.restrict(request.user, 'view').filter(device=instance).prefetch_related(
             'rear_port', 'cable',
             'rear_port', 'cable',
         )
         )
         frontport_table = tables.DeviceFrontPortTable(
         frontport_table = tables.DeviceFrontPortTable(
@@ -1166,20 +1147,18 @@ class DeviceFrontPortsView(generic.ObjectView):
         if request.user.has_perm('dcim.change_frontport') or request.user.has_perm('dcim.delete_frontport'):
         if request.user.has_perm('dcim.change_frontport') or request.user.has_perm('dcim.delete_frontport'):
             frontport_table.columns.show('pk')
             frontport_table.columns.show('pk')
 
 
-        return render(request, 'dcim/device/frontports.html', {
-            'object': device,
+        return {
             'frontport_table': frontport_table,
             'frontport_table': frontport_table,
             'active_tab': 'front-ports',
             'active_tab': 'front-ports',
-        })
+        }
 
 
 
 
 class DeviceRearPortsView(generic.ObjectView):
 class DeviceRearPortsView(generic.ObjectView):
     queryset = Device.objects.all()
     queryset = Device.objects.all()
+    template_name = 'dcim/device/rearports.html'
 
 
-    def get(self, request, pk):
-        device = get_object_or_404(self.queryset, pk=pk)
-
-        rearports = RearPort.objects.restrict(request.user, 'view').filter(device=device).prefetch_related('cable')
+    def get_extra_context(self, request, instance):
+        rearports = RearPort.objects.restrict(request.user, 'view').filter(device=instance).prefetch_related('cable')
         rearport_table = tables.DeviceRearPortTable(
         rearport_table = tables.DeviceRearPortTable(
             data=rearports,
             data=rearports,
             user=request.user,
             user=request.user,
@@ -1188,20 +1167,18 @@ class DeviceRearPortsView(generic.ObjectView):
         if request.user.has_perm('dcim.change_rearport') or request.user.has_perm('dcim.delete_rearport'):
         if request.user.has_perm('dcim.change_rearport') or request.user.has_perm('dcim.delete_rearport'):
             rearport_table.columns.show('pk')
             rearport_table.columns.show('pk')
 
 
-        return render(request, 'dcim/device/rearports.html', {
-            'object': device,
+        return {
             'rearport_table': rearport_table,
             'rearport_table': rearport_table,
             'active_tab': 'rear-ports',
             'active_tab': 'rear-ports',
-        })
+        }
 
 
 
 
 class DeviceDeviceBaysView(generic.ObjectView):
 class DeviceDeviceBaysView(generic.ObjectView):
     queryset = Device.objects.all()
     queryset = Device.objects.all()
+    template_name = 'dcim/device/devicebays.html'
 
 
-    def get(self, request, pk):
-        device = get_object_or_404(self.queryset, pk=pk)
-
-        devicebays = DeviceBay.objects.restrict(request.user, 'view').filter(device=device).prefetch_related(
+    def get_extra_context(self, request, instance):
+        devicebays = DeviceBay.objects.restrict(request.user, 'view').filter(device=instance).prefetch_related(
             'installed_device__device_type__manufacturer',
             'installed_device__device_type__manufacturer',
         )
         )
         devicebay_table = tables.DeviceDeviceBayTable(
         devicebay_table = tables.DeviceDeviceBayTable(
@@ -1212,21 +1189,19 @@ class DeviceDeviceBaysView(generic.ObjectView):
         if request.user.has_perm('dcim.change_devicebay') or request.user.has_perm('dcim.delete_devicebay'):
         if request.user.has_perm('dcim.change_devicebay') or request.user.has_perm('dcim.delete_devicebay'):
             devicebay_table.columns.show('pk')
             devicebay_table.columns.show('pk')
 
 
-        return render(request, 'dcim/device/devicebays.html', {
-            'object': device,
+        return {
             'devicebay_table': devicebay_table,
             'devicebay_table': devicebay_table,
             'active_tab': 'device-bays',
             'active_tab': 'device-bays',
-        })
+        }
 
 
 
 
 class DeviceInventoryView(generic.ObjectView):
 class DeviceInventoryView(generic.ObjectView):
     queryset = Device.objects.all()
     queryset = Device.objects.all()
+    template_name = 'dcim/device/inventory.html'
 
 
-    def get(self, request, pk):
-        device = get_object_or_404(self.queryset, pk=pk)
-
+    def get_extra_context(self, request, instance):
         inventoryitems = InventoryItem.objects.restrict(request.user, 'view').filter(
         inventoryitems = InventoryItem.objects.restrict(request.user, 'view').filter(
-            device=device
+            device=instance
         ).prefetch_related('manufacturer')
         ).prefetch_related('manufacturer')
         inventoryitem_table = tables.DeviceInventoryItemTable(
         inventoryitem_table = tables.DeviceInventoryItemTable(
             data=inventoryitems,
             data=inventoryitems,
@@ -1236,11 +1211,10 @@ class DeviceInventoryView(generic.ObjectView):
         if request.user.has_perm('dcim.change_inventoryitem') or request.user.has_perm('dcim.delete_inventoryitem'):
         if request.user.has_perm('dcim.change_inventoryitem') or request.user.has_perm('dcim.delete_inventoryitem'):
             inventoryitem_table.columns.show('pk')
             inventoryitem_table.columns.show('pk')
 
 
-        return render(request, 'dcim/device/inventory.html', {
-            'object': device,
+        return {
             'inventoryitem_table': inventoryitem_table,
             'inventoryitem_table': inventoryitem_table,
             'active_tab': 'inventory',
             'active_tab': 'inventory',
-        })
+        }
 
 
 
 
 class DeviceStatusView(generic.ObjectView):
 class DeviceStatusView(generic.ObjectView):
@@ -1248,7 +1222,7 @@ class DeviceStatusView(generic.ObjectView):
     queryset = Device.objects.all()
     queryset = Device.objects.all()
     template_name = 'dcim/device/status.html'
     template_name = 'dcim/device/status.html'
 
 
-    def get_extra_context(self):
+    def get_extra_context(self, request, instance):
         return {
         return {
             'active_tab': 'status',
             'active_tab': 'status',
         }
         }
@@ -1257,19 +1231,19 @@ class DeviceStatusView(generic.ObjectView):
 class DeviceLLDPNeighborsView(generic.ObjectView):
 class DeviceLLDPNeighborsView(generic.ObjectView):
     additional_permissions = ['dcim.napalm_read_device']
     additional_permissions = ['dcim.napalm_read_device']
     queryset = Device.objects.all()
     queryset = Device.objects.all()
+    template_name = 'dcim/device/lldp_neighbors.html'
 
 
-    def get(self, request, pk):
-
-        device = get_object_or_404(self.queryset, pk=pk)
-        interfaces = device.vc_interfaces.restrict(request.user, 'view').prefetch_related('_path__destination').exclude(
+    def get_extra_context(self, request, instance):
+        interfaces = instance.vc_interfaces.restrict(request.user, 'view').prefetch_related(
+            '_path__destination'
+        ).exclude(
             type__in=NONCONNECTABLE_IFACE_TYPES
             type__in=NONCONNECTABLE_IFACE_TYPES
         )
         )
 
 
-        return render(request, 'dcim/device/lldp_neighbors.html', {
-            'object': device,
+        return {
             'interfaces': interfaces,
             'interfaces': interfaces,
             'active_tab': 'lldp-neighbors',
             'active_tab': 'lldp-neighbors',
-        })
+        }
 
 
 
 
 class DeviceConfigView(generic.ObjectView):
 class DeviceConfigView(generic.ObjectView):
@@ -1277,7 +1251,7 @@ class DeviceConfigView(generic.ObjectView):
     queryset = Device.objects.all()
     queryset = Device.objects.all()
     template_name = 'dcim/device/config.html'
     template_name = 'dcim/device/config.html'
 
 
-    def get_extra_context(self):
+    def get_extra_context(self, request, instance):
         return {
         return {
             'active_tab': 'config',
             'active_tab': 'config',
         }
         }
@@ -1595,35 +1569,31 @@ class InterfaceListView(generic.ObjectListView):
 class InterfaceView(generic.ObjectView):
 class InterfaceView(generic.ObjectView):
     queryset = Interface.objects.all()
     queryset = Interface.objects.all()
 
 
-    def get(self, request, pk):
-
-        interface = get_object_or_404(self.queryset, pk=pk)
-
+    def get_extra_context(self, request, instance):
         # Get assigned IP addresses
         # Get assigned IP addresses
         ipaddress_table = InterfaceIPAddressTable(
         ipaddress_table = InterfaceIPAddressTable(
-            data=interface.ip_addresses.restrict(request.user, 'view').prefetch_related('vrf', 'tenant'),
+            data=instance.ip_addresses.restrict(request.user, 'view').prefetch_related('vrf', 'tenant'),
             orderable=False
             orderable=False
         )
         )
 
 
         # Get assigned VLANs and annotate whether each is tagged or untagged
         # Get assigned VLANs and annotate whether each is tagged or untagged
         vlans = []
         vlans = []
-        if interface.untagged_vlan is not None:
-            vlans.append(interface.untagged_vlan)
+        if instance.untagged_vlan is not None:
+            vlans.append(instance.untagged_vlan)
             vlans[0].tagged = False
             vlans[0].tagged = False
-        for vlan in interface.tagged_vlans.restrict(request.user).prefetch_related('site', 'group', 'tenant', 'role'):
+        for vlan in instance.tagged_vlans.restrict(request.user).prefetch_related('site', 'group', 'tenant', 'role'):
             vlan.tagged = True
             vlan.tagged = True
             vlans.append(vlan)
             vlans.append(vlan)
         vlan_table = InterfaceVLANTable(
         vlan_table = InterfaceVLANTable(
-            interface=interface,
+            interface=instance,
             data=vlans,
             data=vlans,
             orderable=False
             orderable=False
         )
         )
 
 
-        return render(request, 'dcim/interface.html', {
-            'object': interface,
+        return {
             'ipaddress_table': ipaddress_table,
             'ipaddress_table': ipaddress_table,
             'vlan_table': vlan_table,
             'vlan_table': vlan_table,
-        })
+        }
 
 
 
 
 class InterfaceCreateView(generic.ComponentCreateView):
 class InterfaceCreateView(generic.ComponentCreateView):
@@ -2090,6 +2060,7 @@ class PathTraceView(generic.ObjectView):
     Trace a cable path beginning from the given path endpoint (origin).
     Trace a cable path beginning from the given path endpoint (origin).
     """
     """
     additional_permissions = ['dcim.view_cable']
     additional_permissions = ['dcim.view_cable']
+    template_name = 'dcim/cable_trace.html'
 
 
     def dispatch(self, request, *args, **kwargs):
     def dispatch(self, request, *args, **kwargs):
         model = kwargs.pop('model')
         model = kwargs.pop('model')
@@ -2097,13 +2068,12 @@ class PathTraceView(generic.ObjectView):
 
 
         return super().dispatch(request, *args, **kwargs)
         return super().dispatch(request, *args, **kwargs)
 
 
-    def get(self, request, pk):
-        obj = get_object_or_404(self.queryset, pk=pk)
+    def get_extra_context(self, request, instance):
         related_paths = []
         related_paths = []
 
 
         # If tracing a PathEndpoint, locate the CablePath (if one exists) by its origin
         # If tracing a PathEndpoint, locate the CablePath (if one exists) by its origin
-        if isinstance(obj, PathEndpoint):
-            path = obj._path
+        if isinstance(instance, PathEndpoint):
+            path = instance._path
 
 
         # Otherwise, find all CablePaths which traverse the specified object
         # Otherwise, find all CablePaths which traverse the specified object
         else:
         else:
@@ -2118,12 +2088,11 @@ class PathTraceView(generic.ObjectView):
             else:
             else:
                 path = related_paths.first()
                 path = related_paths.first()
 
 
-        return render(request, 'dcim/cable_trace.html', {
-            'object': obj,
+        return {
             'path': path,
             'path': path,
             'related_paths': related_paths,
             'related_paths': related_paths,
             'total_length': path.get_total_length() if path else None,
             'total_length': path.get_total_length() if path else None,
-        })
+        }
 
 
 
 
 class CableCreateView(generic.ObjectEditView):
 class CableCreateView(generic.ObjectEditView):
@@ -2330,14 +2299,12 @@ class VirtualChassisListView(generic.ObjectListView):
 class VirtualChassisView(generic.ObjectView):
 class VirtualChassisView(generic.ObjectView):
     queryset = VirtualChassis.objects.all()
     queryset = VirtualChassis.objects.all()
 
 
-    def get(self, request, pk):
-        virtualchassis = get_object_or_404(self.queryset, pk=pk)
-        members = Device.objects.restrict(request.user).filter(virtual_chassis=virtualchassis)
+    def get_extra_context(self, request, instance):
+        members = Device.objects.restrict(request.user).filter(virtual_chassis=instance)
 
 
-        return render(request, 'dcim/virtualchassis.html', {
-            'object': virtualchassis,
+        return {
             'members': members,
             'members': members,
-        })
+        }
 
 
 
 
 class VirtualChassisCreateView(generic.ObjectEditView):
 class VirtualChassisCreateView(generic.ObjectEditView):
@@ -2560,20 +2527,17 @@ class PowerPanelListView(generic.ObjectListView):
 class PowerPanelView(generic.ObjectView):
 class PowerPanelView(generic.ObjectView):
     queryset = PowerPanel.objects.prefetch_related('site', 'rack_group')
     queryset = PowerPanel.objects.prefetch_related('site', 'rack_group')
 
 
-    def get(self, request, pk):
-
-        powerpanel = get_object_or_404(self.queryset, pk=pk)
-        power_feeds = PowerFeed.objects.restrict(request.user).filter(power_panel=powerpanel).prefetch_related('rack')
+    def get_extra_context(self, request, instance):
+        power_feeds = PowerFeed.objects.restrict(request.user).filter(power_panel=instance).prefetch_related('rack')
         powerfeed_table = tables.PowerFeedTable(
         powerfeed_table = tables.PowerFeedTable(
             data=power_feeds,
             data=power_feeds,
             orderable=False
             orderable=False
         )
         )
         powerfeed_table.exclude = ['power_panel']
         powerfeed_table.exclude = ['power_panel']
 
 
-        return render(request, 'dcim/powerpanel.html', {
-            'object': powerpanel,
+        return {
             'powerfeed_table': powerfeed_table,
             'powerfeed_table': powerfeed_table,
-        })
+        }
 
 
 
 
 class PowerPanelEditView(generic.ObjectEditView):
 class PowerPanelEditView(generic.ObjectEditView):

+ 20 - 42
netbox/extras/views.py

@@ -83,21 +83,7 @@ class ConfigContextListView(generic.ObjectListView):
 class ConfigContextView(generic.ObjectView):
 class ConfigContextView(generic.ObjectView):
     queryset = ConfigContext.objects.all()
     queryset = ConfigContext.objects.all()
 
 
-    def get(self, request, pk):
-        # Extend queryset to prefetch related objects
-        self.queryset = self.queryset.prefetch_related(
-            Prefetch('regions', queryset=Region.objects.restrict(request.user)),
-            Prefetch('sites', queryset=Site.objects.restrict(request.user)),
-            Prefetch('roles', queryset=DeviceRole.objects.restrict(request.user)),
-            Prefetch('platforms', queryset=Platform.objects.restrict(request.user)),
-            Prefetch('clusters', queryset=Cluster.objects.restrict(request.user)),
-            Prefetch('cluster_groups', queryset=ClusterGroup.objects.restrict(request.user)),
-            Prefetch('tenants', queryset=Tenant.objects.restrict(request.user)),
-            Prefetch('tenant_groups', queryset=TenantGroup.objects.restrict(request.user)),
-        )
-
-        configcontext = get_object_or_404(self.queryset, pk=pk)
-
+    def get_extra_context(self, request, instance):
         # Determine user's preferred output format
         # Determine user's preferred output format
         if request.GET.get('format') in ['json', 'yaml']:
         if request.GET.get('format') in ['json', 'yaml']:
             format = request.GET.get('format')
             format = request.GET.get('format')
@@ -108,10 +94,9 @@ class ConfigContextView(generic.ObjectView):
         else:
         else:
             format = 'json'
             format = 'json'
 
 
-        return render(request, 'extras/configcontext.html', {
-            'object': configcontext,
+        return {
             'format': format,
             'format': format,
-        })
+        }
 
 
 
 
 class ConfigContextEditView(generic.ObjectEditView):
 class ConfigContextEditView(generic.ObjectEditView):
@@ -138,12 +123,10 @@ class ConfigContextBulkDeleteView(generic.BulkDeleteView):
 
 
 class ObjectConfigContextView(generic.ObjectView):
 class ObjectConfigContextView(generic.ObjectView):
     base_template = None
     base_template = None
+    template_name = 'extras/object_configcontext.html'
 
 
-    def get(self, request, pk):
-
-        obj = get_object_or_404(self.queryset, pk=pk)
-        source_contexts = ConfigContext.objects.restrict(request.user, 'view').get_for_object(obj)
-        model_name = self.queryset.model._meta.model_name
+    def get_extra_context(self, request, instance):
+        source_contexts = ConfigContext.objects.restrict(request.user, 'view').get_for_object(instance)
 
 
         # Determine user's preferred output format
         # Determine user's preferred output format
         if request.GET.get('format') in ['json', 'yaml']:
         if request.GET.get('format') in ['json', 'yaml']:
@@ -155,14 +138,13 @@ class ObjectConfigContextView(generic.ObjectView):
         else:
         else:
             format = 'json'
             format = 'json'
 
 
-        return render(request, 'extras/object_configcontext.html', {
-            'object': obj,
-            'rendered_context': obj.get_config_context(),
+        return {
+            'rendered_context': instance.get_config_context(),
             'source_contexts': source_contexts,
             'source_contexts': source_contexts,
             'format': format,
             'format': format,
             'base_template': self.base_template,
             'base_template': self.base_template,
             'active_tab': 'config-context',
             'active_tab': 'config-context',
-        })
+        }
 
 
 
 
 #
 #
@@ -181,14 +163,11 @@ class ObjectChangeListView(generic.ObjectListView):
 class ObjectChangeView(generic.ObjectView):
 class ObjectChangeView(generic.ObjectView):
     queryset = ObjectChange.objects.all()
     queryset = ObjectChange.objects.all()
 
 
-    def get(self, request, pk):
-
-        objectchange = get_object_or_404(self.queryset, pk=pk)
-
+    def get_extra_context(self, request, instance):
         related_changes = ObjectChange.objects.restrict(request.user, 'view').filter(
         related_changes = ObjectChange.objects.restrict(request.user, 'view').filter(
-            request_id=objectchange.request_id
+            request_id=instance.request_id
         ).exclude(
         ).exclude(
-            pk=objectchange.pk
+            pk=instance.pk
         )
         )
         related_changes_table = tables.ObjectChangeTable(
         related_changes_table = tables.ObjectChangeTable(
             data=related_changes[:50],
             data=related_changes[:50],
@@ -196,33 +175,32 @@ class ObjectChangeView(generic.ObjectView):
         )
         )
 
 
         objectchanges = ObjectChange.objects.restrict(request.user, 'view').filter(
         objectchanges = ObjectChange.objects.restrict(request.user, 'view').filter(
-            changed_object_type=objectchange.changed_object_type,
-            changed_object_id=objectchange.changed_object_id,
+            changed_object_type=instance.changed_object_type,
+            changed_object_id=instance.changed_object_id,
         )
         )
 
 
-        next_change = objectchanges.filter(time__gt=objectchange.time).order_by('time').first()
-        prev_change = objectchanges.filter(time__lt=objectchange.time).order_by('-time').first()
+        next_change = objectchanges.filter(time__gt=instance.time).order_by('time').first()
+        prev_change = objectchanges.filter(time__lt=instance.time).order_by('-time').first()
 
 
         if prev_change:
         if prev_change:
             diff_added = shallow_compare_dict(
             diff_added = shallow_compare_dict(
                 prev_change.object_data,
                 prev_change.object_data,
-                objectchange.object_data,
+                instance.object_data,
                 exclude=['last_updated'],
                 exclude=['last_updated'],
             )
             )
             diff_removed = {x: prev_change.object_data.get(x) for x in diff_added}
             diff_removed = {x: prev_change.object_data.get(x) for x in diff_added}
         else:
         else:
             # No previous change; this is the initial change that added the object
             # No previous change; this is the initial change that added the object
-            diff_added = diff_removed = objectchange.object_data
+            diff_added = diff_removed = instance.object_data
 
 
-        return render(request, 'extras/objectchange.html', {
-            'object': objectchange,
+        return {
             'diff_added': diff_added,
             'diff_added': diff_added,
             'diff_removed': diff_removed,
             'diff_removed': diff_removed,
             'next_change': next_change,
             'next_change': next_change,
             'prev_change': prev_change,
             'prev_change': prev_change,
             'related_changes_table': related_changes_table,
             'related_changes_table': related_changes_table,
             'related_changes_count': related_changes.count()
             'related_changes_count': related_changes.count()
-        })
+        }
 
 
 
 
 class ObjectChangeLogView(View):
 class ObjectChangeLogView(View):

+ 78 - 102
netbox/ipam/views.py

@@ -28,26 +28,23 @@ class VRFListView(generic.ObjectListView):
 class VRFView(generic.ObjectView):
 class VRFView(generic.ObjectView):
     queryset = VRF.objects.all()
     queryset = VRF.objects.all()
 
 
-    def get(self, request, pk):
-
-        vrf = get_object_or_404(self.queryset, pk=pk)
-        prefix_count = Prefix.objects.restrict(request.user, 'view').filter(vrf=vrf).count()
+    def get_extra_context(self, request, instance):
+        prefix_count = Prefix.objects.restrict(request.user, 'view').filter(vrf=instance).count()
 
 
         import_targets_table = tables.RouteTargetTable(
         import_targets_table = tables.RouteTargetTable(
-            vrf.import_targets.prefetch_related('tenant'),
+            instance.import_targets.prefetch_related('tenant'),
             orderable=False
             orderable=False
         )
         )
         export_targets_table = tables.RouteTargetTable(
         export_targets_table = tables.RouteTargetTable(
-            vrf.export_targets.prefetch_related('tenant'),
+            instance.export_targets.prefetch_related('tenant'),
             orderable=False
             orderable=False
         )
         )
 
 
-        return render(request, 'ipam/vrf.html', {
-            'object': vrf,
+        return {
             'prefix_count': prefix_count,
             'prefix_count': prefix_count,
             'import_targets_table': import_targets_table,
             'import_targets_table': import_targets_table,
             'export_targets_table': export_targets_table,
             'export_targets_table': export_targets_table,
-        })
+        }
 
 
 
 
 class VRFEditView(generic.ObjectEditView):
 class VRFEditView(generic.ObjectEditView):
@@ -93,23 +90,20 @@ class RouteTargetListView(generic.ObjectListView):
 class RouteTargetView(generic.ObjectView):
 class RouteTargetView(generic.ObjectView):
     queryset = RouteTarget.objects.all()
     queryset = RouteTarget.objects.all()
 
 
-    def get(self, request, pk):
-        routetarget = get_object_or_404(self.queryset, pk=pk)
-
+    def get_extra_context(self, request, instance):
         importing_vrfs_table = tables.VRFTable(
         importing_vrfs_table = tables.VRFTable(
-            routetarget.importing_vrfs.prefetch_related('tenant'),
+            instance.importing_vrfs.prefetch_related('tenant'),
             orderable=False
             orderable=False
         )
         )
         exporting_vrfs_table = tables.VRFTable(
         exporting_vrfs_table = tables.VRFTable(
-            routetarget.exporting_vrfs.prefetch_related('tenant'),
+            instance.exporting_vrfs.prefetch_related('tenant'),
             orderable=False
             orderable=False
         )
         )
 
 
-        return render(request, 'ipam/routetarget.html', {
-            'object': routetarget,
+        return {
             'importing_vrfs_table': importing_vrfs_table,
             'importing_vrfs_table': importing_vrfs_table,
             'exporting_vrfs_table': exporting_vrfs_table,
             'exporting_vrfs_table': exporting_vrfs_table,
-        })
+        }
 
 
 
 
 class RouteTargetEditView(generic.ObjectEditView):
 class RouteTargetEditView(generic.ObjectEditView):
@@ -206,13 +200,10 @@ class AggregateListView(generic.ObjectListView):
 class AggregateView(generic.ObjectView):
 class AggregateView(generic.ObjectView):
     queryset = Aggregate.objects.all()
     queryset = Aggregate.objects.all()
 
 
-    def get(self, request, pk):
-
-        aggregate = get_object_or_404(self.queryset, pk=pk)
-
+    def get_extra_context(self, request, instance):
         # Find all child prefixes contained by this aggregate
         # Find all child prefixes contained by this aggregate
         child_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
         child_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
-            prefix__net_contained_or_equal=str(aggregate.prefix)
+            prefix__net_contained_or_equal=str(instance.prefix)
         ).prefetch_related(
         ).prefetch_related(
             'site', 'role'
             'site', 'role'
         ).order_by(
         ).order_by(
@@ -221,7 +212,7 @@ class AggregateView(generic.ObjectView):
 
 
         # Add available prefixes to the table if requested
         # Add available prefixes to the table if requested
         if request.GET.get('show_available', 'true') == 'true':
         if request.GET.get('show_available', 'true') == 'true':
-            child_prefixes = add_available_prefixes(aggregate.prefix, child_prefixes)
+            child_prefixes = add_available_prefixes(instance.prefix, child_prefixes)
 
 
         prefix_table = tables.PrefixDetailTable(child_prefixes)
         prefix_table = tables.PrefixDetailTable(child_prefixes)
         if request.user.has_perm('ipam.change_prefix') or request.user.has_perm('ipam.delete_prefix'):
         if request.user.has_perm('ipam.change_prefix') or request.user.has_perm('ipam.delete_prefix'):
@@ -240,12 +231,11 @@ class AggregateView(generic.ObjectView):
             'delete': request.user.has_perm('ipam.delete_prefix'),
             'delete': request.user.has_perm('ipam.delete_prefix'),
         }
         }
 
 
-        return render(request, 'ipam/aggregate.html', {
-            'object': aggregate,
+        return {
             'prefix_table': prefix_table,
             'prefix_table': prefix_table,
             'permissions': permissions,
             'permissions': permissions,
             'show_available': request.GET.get('show_available', 'true') == 'true',
             'show_available': request.GET.get('show_available', 'true') == 'true',
-        })
+        }
 
 
 
 
 class AggregateEditView(generic.ObjectEditView):
 class AggregateEditView(generic.ObjectEditView):
@@ -324,22 +314,19 @@ class PrefixListView(generic.ObjectListView):
 class PrefixView(generic.ObjectView):
 class PrefixView(generic.ObjectView):
     queryset = Prefix.objects.prefetch_related('vrf', 'site__region', 'tenant__group', 'vlan__group', 'role')
     queryset = Prefix.objects.prefetch_related('vrf', 'site__region', 'tenant__group', 'vlan__group', 'role')
 
 
-    def get(self, request, pk):
-
-        prefix = get_object_or_404(self.queryset, pk=pk)
-
+    def get_extra_context(self, request, instance):
         try:
         try:
             aggregate = Aggregate.objects.restrict(request.user, 'view').get(
             aggregate = Aggregate.objects.restrict(request.user, 'view').get(
-                prefix__net_contains_or_equals=str(prefix.prefix)
+                prefix__net_contains_or_equals=str(instance.prefix)
             )
             )
         except Aggregate.DoesNotExist:
         except Aggregate.DoesNotExist:
             aggregate = None
             aggregate = None
 
 
         # Parent prefixes table
         # Parent prefixes table
         parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
         parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
-            Q(vrf=prefix.vrf) | Q(vrf__isnull=True)
+            Q(vrf=instance.vrf) | Q(vrf__isnull=True)
         ).filter(
         ).filter(
-            prefix__net_contains=str(prefix.prefix)
+            prefix__net_contains=str(instance.prefix)
         ).prefetch_related(
         ).prefetch_related(
             'site', 'role'
             'site', 'role'
         ).annotate_tree()
         ).annotate_tree()
@@ -348,38 +335,35 @@ class PrefixView(generic.ObjectView):
 
 
         # Duplicate prefixes table
         # Duplicate prefixes table
         duplicate_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
         duplicate_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
-            vrf=prefix.vrf, prefix=str(prefix.prefix)
+            vrf=instance.vrf, prefix=str(instance.prefix)
         ).exclude(
         ).exclude(
-            pk=prefix.pk
+            pk=instance.pk
         ).prefetch_related(
         ).prefetch_related(
             'site', 'role'
             'site', 'role'
         )
         )
         duplicate_prefix_table = tables.PrefixTable(list(duplicate_prefixes), orderable=False)
         duplicate_prefix_table = tables.PrefixTable(list(duplicate_prefixes), orderable=False)
         duplicate_prefix_table.exclude = ('vrf',)
         duplicate_prefix_table.exclude = ('vrf',)
 
 
-        return render(request, 'ipam/prefix.html', {
-            'object': prefix,
+        return {
             'aggregate': aggregate,
             'aggregate': aggregate,
             'parent_prefix_table': parent_prefix_table,
             'parent_prefix_table': parent_prefix_table,
             'duplicate_prefix_table': duplicate_prefix_table,
             'duplicate_prefix_table': duplicate_prefix_table,
-        })
+        }
 
 
 
 
 class PrefixPrefixesView(generic.ObjectView):
 class PrefixPrefixesView(generic.ObjectView):
     queryset = Prefix.objects.all()
     queryset = Prefix.objects.all()
+    template_name = 'ipam/prefix_prefixes.html'
 
 
-    def get(self, request, pk):
-
-        prefix = get_object_or_404(self.queryset, pk=pk)
-
+    def get_extra_context(self, request, instance):
         # Child prefixes table
         # Child prefixes table
-        child_prefixes = prefix.get_child_prefixes().restrict(request.user, 'view').prefetch_related(
+        child_prefixes = instance.get_child_prefixes().restrict(request.user, 'view').prefetch_related(
             'site', 'vlan', 'role',
             'site', 'vlan', 'role',
         ).annotate_tree()
         ).annotate_tree()
 
 
         # Add available prefixes to the table if requested
         # Add available prefixes to the table if requested
         if child_prefixes and request.GET.get('show_available', 'true') == 'true':
         if child_prefixes and request.GET.get('show_available', 'true') == 'true':
-            child_prefixes = add_available_prefixes(prefix.prefix, child_prefixes)
+            child_prefixes = add_available_prefixes(instance.prefix, child_prefixes)
 
 
         prefix_table = tables.PrefixDetailTable(child_prefixes)
         prefix_table = tables.PrefixDetailTable(child_prefixes)
         if request.user.has_perm('ipam.change_prefix') or request.user.has_perm('ipam.delete_prefix'):
         if request.user.has_perm('ipam.change_prefix') or request.user.has_perm('ipam.delete_prefix'):
@@ -398,32 +382,31 @@ class PrefixPrefixesView(generic.ObjectView):
             'delete': request.user.has_perm('ipam.delete_prefix'),
             'delete': request.user.has_perm('ipam.delete_prefix'),
         }
         }
 
 
-        return render(request, 'ipam/prefix_prefixes.html', {
-            'object': prefix,
-            'first_available_prefix': prefix.get_first_available_prefix(),
+        bulk_querystring = 'vrf_id={}&within={}'.format(instance.vrf.pk if instance.vrf else '0', instance.prefix)
+
+        return {
+            'first_available_prefix': instance.get_first_available_prefix(),
             'prefix_table': prefix_table,
             'prefix_table': prefix_table,
             'permissions': permissions,
             'permissions': permissions,
-            'bulk_querystring': 'vrf_id={}&within={}'.format(prefix.vrf.pk if prefix.vrf else '0', prefix.prefix),
+            'bulk_querystring': bulk_querystring,
             'active_tab': 'prefixes',
             'active_tab': 'prefixes',
             'show_available': request.GET.get('show_available', 'true') == 'true',
             'show_available': request.GET.get('show_available', 'true') == 'true',
-        })
+        }
 
 
 
 
 class PrefixIPAddressesView(generic.ObjectView):
 class PrefixIPAddressesView(generic.ObjectView):
     queryset = Prefix.objects.all()
     queryset = Prefix.objects.all()
+    template_name = 'ipam/prefix_ipaddresses.html'
 
 
-    def get(self, request, pk):
-
-        prefix = get_object_or_404(self.queryset, pk=pk)
-
+    def get_extra_context(self, request, instance):
         # Find all IPAddresses belonging to this Prefix
         # Find all IPAddresses belonging to this Prefix
-        ipaddresses = prefix.get_child_ips().restrict(request.user, 'view').prefetch_related(
+        ipaddresses = instance.get_child_ips().restrict(request.user, 'view').prefetch_related(
             'vrf', 'primary_ip4_for', 'primary_ip6_for'
             'vrf', 'primary_ip4_for', 'primary_ip6_for'
         )
         )
 
 
         # Add available IP addresses to the table if requested
         # Add available IP addresses to the table if requested
         if request.GET.get('show_available', 'true') == 'true':
         if request.GET.get('show_available', 'true') == 'true':
-            ipaddresses = add_available_ipaddresses(prefix.prefix, ipaddresses, prefix.is_pool)
+            ipaddresses = add_available_ipaddresses(instance.prefix, ipaddresses, instance.is_pool)
 
 
         ip_table = tables.IPAddressTable(ipaddresses)
         ip_table = tables.IPAddressTable(ipaddresses)
         if request.user.has_perm('ipam.change_ipaddress') or request.user.has_perm('ipam.delete_ipaddress'):
         if request.user.has_perm('ipam.change_ipaddress') or request.user.has_perm('ipam.delete_ipaddress'):
@@ -442,15 +425,16 @@ class PrefixIPAddressesView(generic.ObjectView):
             'delete': request.user.has_perm('ipam.delete_ipaddress'),
             'delete': request.user.has_perm('ipam.delete_ipaddress'),
         }
         }
 
 
-        return render(request, 'ipam/prefix_ipaddresses.html', {
-            'object': prefix,
-            'first_available_ip': prefix.get_first_available_ip(),
+        bulk_querystring = 'vrf_id={}&parent={}'.format(instance.vrf.pk if instance.vrf else '0', instance.prefix)
+
+        return {
+            'first_available_ip': instance.get_first_available_ip(),
             'ip_table': ip_table,
             'ip_table': ip_table,
             'permissions': permissions,
             'permissions': permissions,
-            'bulk_querystring': 'vrf_id={}&parent={}'.format(prefix.vrf.pk if prefix.vrf else '0', prefix.prefix),
+            'bulk_querystring': bulk_querystring,
             'active_tab': 'ip-addresses',
             'active_tab': 'ip-addresses',
             'show_available': request.GET.get('show_available', 'true') == 'true',
             'show_available': request.GET.get('show_available', 'true') == 'true',
-        })
+        }
 
 
 
 
 class PrefixEditView(generic.ObjectEditView):
 class PrefixEditView(generic.ObjectEditView):
@@ -497,13 +481,11 @@ class IPAddressListView(generic.ObjectListView):
 class IPAddressView(generic.ObjectView):
 class IPAddressView(generic.ObjectView):
     queryset = IPAddress.objects.prefetch_related('vrf__tenant', 'tenant')
     queryset = IPAddress.objects.prefetch_related('vrf__tenant', 'tenant')
 
 
-    def get(self, request, pk):
-
-        ipaddress = get_object_or_404(self.queryset, pk=pk)
-
+    def get_extra_context(self, request, instance):
         # Parent prefixes table
         # Parent prefixes table
         parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
         parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
-            vrf=ipaddress.vrf, prefix__net_contains=str(ipaddress.address.ip)
+            vrf=instance.vrf,
+            prefix__net_contains=str(instance.address.ip)
         ).prefetch_related(
         ).prefetch_related(
             'site', 'role'
             'site', 'role'
         )
         )
@@ -512,23 +494,24 @@ class IPAddressView(generic.ObjectView):
 
 
         # Duplicate IPs table
         # Duplicate IPs table
         duplicate_ips = IPAddress.objects.restrict(request.user, 'view').filter(
         duplicate_ips = IPAddress.objects.restrict(request.user, 'view').filter(
-            vrf=ipaddress.vrf, address=str(ipaddress.address)
+            vrf=instance.vrf,
+            address=str(instance.address)
         ).exclude(
         ).exclude(
-            pk=ipaddress.pk
+            pk=instance.pk
         ).prefetch_related(
         ).prefetch_related(
             'nat_inside'
             'nat_inside'
         )
         )
         # Exclude anycast IPs if this IP is anycast
         # Exclude anycast IPs if this IP is anycast
-        if ipaddress.role == IPAddressRoleChoices.ROLE_ANYCAST:
+        if instance.role == IPAddressRoleChoices.ROLE_ANYCAST:
             duplicate_ips = duplicate_ips.exclude(role=IPAddressRoleChoices.ROLE_ANYCAST)
             duplicate_ips = duplicate_ips.exclude(role=IPAddressRoleChoices.ROLE_ANYCAST)
         # Limit to a maximum of 10 duplicates displayed here
         # Limit to a maximum of 10 duplicates displayed here
         duplicate_ips_table = tables.IPAddressTable(duplicate_ips[:10], orderable=False)
         duplicate_ips_table = tables.IPAddressTable(duplicate_ips[:10], orderable=False)
 
 
         # Related IP table
         # Related IP table
         related_ips = IPAddress.objects.restrict(request.user, 'view').exclude(
         related_ips = IPAddress.objects.restrict(request.user, 'view').exclude(
-            address=str(ipaddress.address)
+            address=str(instance.address)
         ).filter(
         ).filter(
-            vrf=ipaddress.vrf, address__net_contained_or_equal=str(ipaddress.address)
+            vrf=instance.vrf, address__net_contained_or_equal=str(instance.address)
         )
         )
         related_ips_table = tables.IPAddressTable(related_ips, orderable=False)
         related_ips_table = tables.IPAddressTable(related_ips, orderable=False)
 
 
@@ -538,13 +521,12 @@ class IPAddressView(generic.ObjectView):
         }
         }
         RequestConfig(request, paginate).configure(related_ips_table)
         RequestConfig(request, paginate).configure(related_ips_table)
 
 
-        return render(request, 'ipam/ipaddress.html', {
-            'object': ipaddress,
+        return {
             'parent_prefixes_table': parent_prefixes_table,
             'parent_prefixes_table': parent_prefixes_table,
             'duplicate_ips_table': duplicate_ips_table,
             'duplicate_ips_table': duplicate_ips_table,
             'more_duplicate_ips': duplicate_ips.count() > 10,
             'more_duplicate_ips': duplicate_ips.count() > 10,
             'related_ips_table': related_ips_table,
             'related_ips_table': related_ips_table,
-        })
+        }
 
 
 
 
 class IPAddressEditView(generic.ObjectEditView):
 class IPAddressEditView(generic.ObjectEditView):
@@ -569,6 +551,7 @@ class IPAddressEditView(generic.ObjectEditView):
         return obj
         return obj
 
 
 
 
+# TODO: Standardize or remove this view
 class IPAddressAssignView(generic.ObjectView):
 class IPAddressAssignView(generic.ObjectView):
     """
     """
     Search for IPAddresses to be assigned to an Interface.
     Search for IPAddresses to be assigned to an Interface.
@@ -678,14 +661,13 @@ class VLANGroupBulkDeleteView(generic.BulkDeleteView):
 
 
 class VLANGroupVLANsView(generic.ObjectView):
 class VLANGroupVLANsView(generic.ObjectView):
     queryset = VLANGroup.objects.all()
     queryset = VLANGroup.objects.all()
+    template_name = 'ipam/vlangroup_vlans.html'
 
 
-    def get(self, request, pk):
-        vlan_group = get_object_or_404(self.queryset, pk=pk)
-
-        vlans = VLAN.objects.restrict(request.user, 'view').filter(group_id=pk).prefetch_related(
+    def get_extra_context(self, request, instance):
+        vlans = VLAN.objects.restrict(request.user, 'view').filter(group=instance).prefetch_related(
             Prefetch('prefixes', queryset=Prefix.objects.restrict(request.user))
             Prefetch('prefixes', queryset=Prefix.objects.restrict(request.user))
         )
         )
-        vlans = add_available_vlans(vlan_group, vlans)
+        vlans = add_available_vlans(instance, vlans)
 
 
         vlan_table = tables.VLANDetailTable(vlans)
         vlan_table = tables.VLANDetailTable(vlans)
         if request.user.has_perm('ipam.change_vlan') or request.user.has_perm('ipam.delete_vlan'):
         if request.user.has_perm('ipam.change_vlan') or request.user.has_perm('ipam.delete_vlan'):
@@ -706,13 +688,12 @@ class VLANGroupVLANsView(generic.ObjectView):
             'delete': request.user.has_perm('ipam.delete_vlan'),
             'delete': request.user.has_perm('ipam.delete_vlan'),
         }
         }
 
 
-        return render(request, 'ipam/vlangroup_vlans.html', {
-            'vlan_group': vlan_group,
-            'first_available_vlan': vlan_group.get_next_available_vid(),
-            'bulk_querystring': 'group_id={}'.format(vlan_group.pk),
+        return {
+            'first_available_vlan': instance.get_next_available_vid(),
+            'bulk_querystring': f'group_id={instance.pk}',
             'vlan_table': vlan_table,
             'vlan_table': vlan_table,
             'permissions': permissions,
             'permissions': permissions,
-        })
+        }
 
 
 
 
 #
 #
@@ -729,27 +710,24 @@ class VLANListView(generic.ObjectListView):
 class VLANView(generic.ObjectView):
 class VLANView(generic.ObjectView):
     queryset = VLAN.objects.prefetch_related('site__region', 'tenant__group', 'role')
     queryset = VLAN.objects.prefetch_related('site__region', 'tenant__group', 'role')
 
 
-    def get(self, request, pk):
-
-        vlan = get_object_or_404(self.queryset, pk=pk)
-        prefixes = Prefix.objects.restrict(request.user, 'view').filter(vlan=vlan).prefetch_related(
+    def get_extra_context(self, request, instance):
+        prefixes = Prefix.objects.restrict(request.user, 'view').filter(vlan=instance).prefetch_related(
             'vrf', 'site', 'role'
             'vrf', 'site', 'role'
         )
         )
         prefix_table = tables.PrefixTable(list(prefixes), orderable=False)
         prefix_table = tables.PrefixTable(list(prefixes), orderable=False)
         prefix_table.exclude = ('vlan',)
         prefix_table.exclude = ('vlan',)
 
 
-        return render(request, 'ipam/vlan.html', {
-            'object': vlan,
+        return {
             'prefix_table': prefix_table,
             'prefix_table': prefix_table,
-        })
+        }
 
 
 
 
 class VLANInterfacesView(generic.ObjectView):
 class VLANInterfacesView(generic.ObjectView):
     queryset = VLAN.objects.all()
     queryset = VLAN.objects.all()
+    template_name = 'ipam/vlan_interfaces.html'
 
 
-    def get(self, request, pk):
-        vlan = get_object_or_404(self.queryset, pk=pk)
-        interfaces = vlan.get_interfaces().prefetch_related('device')
+    def get_extra_context(self, request, instance):
+        interfaces = instance.get_interfaces().prefetch_related('device')
         members_table = tables.VLANDevicesTable(interfaces)
         members_table = tables.VLANDevicesTable(interfaces)
 
 
         paginate = {
         paginate = {
@@ -758,19 +736,18 @@ class VLANInterfacesView(generic.ObjectView):
         }
         }
         RequestConfig(request, paginate).configure(members_table)
         RequestConfig(request, paginate).configure(members_table)
 
 
-        return render(request, 'ipam/vlan_interfaces.html', {
-            'object': vlan,
+        return {
             'members_table': members_table,
             'members_table': members_table,
             'active_tab': 'interfaces',
             'active_tab': 'interfaces',
-        })
+        }
 
 
 
 
 class VLANVMInterfacesView(generic.ObjectView):
 class VLANVMInterfacesView(generic.ObjectView):
     queryset = VLAN.objects.all()
     queryset = VLAN.objects.all()
+    template_name = 'ipam/vlan_vminterfaces.html'
 
 
-    def get(self, request, pk):
-        vlan = get_object_or_404(self.queryset, pk=pk)
-        interfaces = vlan.get_vminterfaces().prefetch_related('virtual_machine')
+    def get_extra_context(self, request, instance):
+        interfaces = instance.get_vminterfaces().prefetch_related('virtual_machine')
         members_table = tables.VLANVirtualMachinesTable(interfaces)
         members_table = tables.VLANVirtualMachinesTable(interfaces)
 
 
         paginate = {
         paginate = {
@@ -779,11 +756,10 @@ class VLANVMInterfacesView(generic.ObjectView):
         }
         }
         RequestConfig(request, paginate).configure(members_table)
         RequestConfig(request, paginate).configure(members_table)
 
 
-        return render(request, 'ipam/vlan_vminterfaces.html', {
-            'object': vlan,
+        return {
             'members_table': members_table,
             'members_table': members_table,
             'active_tab': 'vminterfaces',
             'active_tab': 'vminterfaces',
-        })
+        }
 
 
 
 
 class VLANEditView(generic.ObjectEditView):
 class VLANEditView(generic.ObjectEditView):

+ 11 - 6
netbox/netbox/views/generic.py

@@ -32,9 +32,11 @@ class ObjectView(ObjectPermissionRequiredMixin, View):
     """
     """
     Retrieve a single object for display.
     Retrieve a single object for display.
 
 
-    queryset: The base queryset for retrieving the object.
+    queryset: The base queryset for retrieving the object
+    template_name: Name of the template to use
     """
     """
     queryset = None
     queryset = None
+    template_name = None
 
 
     def get_required_permission(self):
     def get_required_permission(self):
         return get_permission_for_model(self.queryset.model, 'view')
         return get_permission_for_model(self.queryset.model, 'view')
@@ -43,14 +45,17 @@ class ObjectView(ObjectPermissionRequiredMixin, View):
         """
         """
         Return self.template_name if set. Otherwise, resolve the template path by model app_label and name.
         Return self.template_name if set. Otherwise, resolve the template path by model app_label and name.
         """
         """
-        if hasattr(self, 'template_name'):
+        if self.template_name is not None:
             return self.template_name
             return self.template_name
         model_opts = self.queryset.model._meta
         model_opts = self.queryset.model._meta
         return f'{model_opts.app_label}/{model_opts.model_name}.html'
         return f'{model_opts.app_label}/{model_opts.model_name}.html'
 
 
-    def get_extra_context(self):
+    def get_extra_context(self, request, instance):
         """
         """
         Return any additional context data for the template.
         Return any additional context data for the template.
+
+        request: The current request
+        instance: The object being viewed
         """
         """
         return {}
         return {}
 
 
@@ -58,11 +63,11 @@ class ObjectView(ObjectPermissionRequiredMixin, View):
         """
         """
         Generic GET handler for accessing an object by PK or slug
         Generic GET handler for accessing an object by PK or slug
         """
         """
-        obj = get_object_or_404(self.queryset, **kwargs)
+        instance = get_object_or_404(self.queryset, **kwargs)
 
 
         return render(request, self.get_template_name(), {
         return render(request, self.get_template_name(), {
-            'object': obj,
-            **self.get_extra_context(),
+            'object': instance,
+            **self.get_extra_context(request, instance),
         })
         })
 
 
 
 

+ 3 - 3
netbox/templates/ipam/inc/vlangroup_header.html

@@ -1,14 +1,14 @@
 <div class="pull-right">
 <div class="pull-right">
     {% if perms.ipam.add_vlan and first_available_vlan %}
     {% if perms.ipam.add_vlan and first_available_vlan %}
-        <a href="{% url 'ipam:vlan_add' %}?vid={{ first_available_vlan }}&group={{ vlan_group.pk }}{% if vlan_group.site %}&site={{ vlan_group.site.pk }}{% endif %}" class="btn btn-success">
+        <a href="{% url 'ipam:vlan_add' %}?vid={{ first_available_vlan }}&group={{ object.pk }}{% if object.site %}&site={{ object.site.pk }}{% endif %}" class="btn btn-success">
             <i class="mdi mdi-plus-thick" aria-hidden="true"></i> Add a VLAN
             <i class="mdi mdi-plus-thick" aria-hidden="true"></i> Add a VLAN
         </a>
         </a>
     {% endif %}
     {% endif %}
     {% if perms.ipam.change_vlangroup %}
     {% if perms.ipam.change_vlangroup %}
-               <a href="{% url 'ipam:vlangroup_edit' pk=vlan_group.pk %}" class="btn btn-warning">
+               <a href="{% url 'ipam:vlangroup_edit' pk=object.pk %}" class="btn btn-warning">
                        <span class="mdi mdi-pencil" aria-hidden="true"></span>
                        <span class="mdi mdi-pencil" aria-hidden="true"></span>
                        Edit this VLAN Group
                        Edit this VLAN Group
                </a>
                </a>
     {% endif %}
     {% endif %}
 </div>
 </div>
-<h1>{{ vlan_group }}</h1>
+<h1>{{ object }}</h1>

+ 4 - 4
netbox/templates/ipam/vlangroup_vlans.html

@@ -1,16 +1,16 @@
 {% extends 'base.html' %}
 {% extends 'base.html' %}
 
 
-{% block title %}{{ vlan_group }} - VLANs{% endblock %}
+{% block title %}{{ object }} - VLANs{% endblock %}
 
 
 {% block content %}
 {% block content %}
 <div class="row noprint">
 <div class="row noprint">
     <div class="col-sm-12 col-md-12">
     <div class="col-sm-12 col-md-12">
         <ol class="breadcrumb">
         <ol class="breadcrumb">
             <li><a href="{% url 'ipam:vlangroup_list' %}">VLAN Groups</a></li>
             <li><a href="{% url 'ipam:vlangroup_list' %}">VLAN Groups</a></li>
-            {% if vlan_group.site %}
-                <li><a href="{% url 'dcim:site' slug=vlan_group.site.slug %}">{{ vlan_group.site }}</a></li>
+            {% if object.site %}
+                <li><a href="{% url 'dcim:site' slug=object.site.slug %}">{{ object.site }}</a></li>
             {% endif %}
             {% endif %}
-            <li>{{ vlan_group }}</li>
+            <li>{{ object }}</li>
         </ol>
         </ol>
     </div>
     </div>
 </div>
 </div>

+ 1 - 1
netbox/templates/virtualization/virtualmachine.html

@@ -250,7 +250,7 @@
             {% endif %}
             {% endif %}
             {% if perms.ipam.add_service %}
             {% if perms.ipam.add_service %}
                 <div class="panel-footer text-right noprint">
                 <div class="panel-footer text-right noprint">
-                    <a href="{% url 'virtualization:virtualmachine_service_assign' object=object.pk %}" class="btn btn-xs btn-primary">
+                    <a href="{% url 'virtualization:virtualmachine_service_assign' virtualmachine=object.pk %}" class="btn btn-xs btn-primary">
                         <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Assign service
                         <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Assign service
                     </a>
                     </a>
                 </div>
                 </div>

+ 14 - 17
netbox/tenancy/views.py

@@ -64,27 +64,24 @@ class TenantListView(generic.ObjectListView):
 class TenantView(generic.ObjectView):
 class TenantView(generic.ObjectView):
     queryset = Tenant.objects.prefetch_related('group')
     queryset = Tenant.objects.prefetch_related('group')
 
 
-    def get(self, request, slug):
-
-        tenant = get_object_or_404(self.queryset, slug=slug)
+    def get_extra_context(self, request, instance):
         stats = {
         stats = {
-            'site_count': Site.objects.restrict(request.user, 'view').filter(tenant=tenant).count(),
-            'rack_count': Rack.objects.restrict(request.user, 'view').filter(tenant=tenant).count(),
-            'rackreservation_count': RackReservation.objects.restrict(request.user, 'view').filter(tenant=tenant).count(),
-            'device_count': Device.objects.restrict(request.user, 'view').filter(tenant=tenant).count(),
-            'vrf_count': VRF.objects.restrict(request.user, 'view').filter(tenant=tenant).count(),
-            'prefix_count': Prefix.objects.restrict(request.user, 'view').filter(tenant=tenant).count(),
-            'ipaddress_count': IPAddress.objects.restrict(request.user, 'view').filter(tenant=tenant).count(),
-            'vlan_count': VLAN.objects.restrict(request.user, 'view').filter(tenant=tenant).count(),
-            'circuit_count': Circuit.objects.restrict(request.user, 'view').filter(tenant=tenant).count(),
-            'virtualmachine_count': VirtualMachine.objects.restrict(request.user, 'view').filter(tenant=tenant).count(),
-            'cluster_count': Cluster.objects.restrict(request.user, 'view').filter(tenant=tenant).count(),
+            'site_count': Site.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
+            'rack_count': Rack.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
+            'rackreservation_count': RackReservation.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
+            'device_count': Device.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
+            'vrf_count': VRF.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
+            'prefix_count': Prefix.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
+            'ipaddress_count': IPAddress.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
+            'vlan_count': VLAN.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
+            'circuit_count': Circuit.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
+            'virtualmachine_count': VirtualMachine.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
+            'cluster_count': Cluster.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
         }
         }
 
 
-        return render(request, 'tenancy/tenant.html', {
-            'object': tenant,
+        return {
             'stats': stats,
             'stats': stats,
-        })
+        }
 
 
 
 
 class TenantEditView(generic.ObjectEditView):
 class TenantEditView(generic.ObjectEditView):

+ 18 - 31
netbox/virtualization/views.py

@@ -92,23 +92,17 @@ class ClusterListView(generic.ObjectListView):
 class ClusterView(generic.ObjectView):
 class ClusterView(generic.ObjectView):
     queryset = Cluster.objects.all()
     queryset = Cluster.objects.all()
 
 
-    def get(self, request, pk):
-        self.queryset = self.queryset.prefetch_related(
-            Prefetch('virtual_machines', queryset=VirtualMachine.objects.restrict(request.user))
-        )
-
-        cluster = get_object_or_404(self.queryset, pk=pk)
-        devices = Device.objects.restrict(request.user, 'view').filter(cluster=cluster).prefetch_related(
+    def get_extra_context(self, request, instance):
+        devices = Device.objects.restrict(request.user, 'view').filter(cluster=instance).prefetch_related(
             'site', 'rack', 'tenant', 'device_type__manufacturer'
             'site', 'rack', 'tenant', 'device_type__manufacturer'
         )
         )
         device_table = DeviceTable(list(devices), orderable=False)
         device_table = DeviceTable(list(devices), orderable=False)
         if request.user.has_perm('virtualization.change_cluster'):
         if request.user.has_perm('virtualization.change_cluster'):
             device_table.columns.show('pk')
             device_table.columns.show('pk')
 
 
-        return render(request, 'virtualization/cluster.html', {
-            'object': cluster,
+        return {
             'device_table': device_table,
             'device_table': device_table,
-        })
+        }
 
 
 
 
 class ClusterEditView(generic.ObjectEditView):
 class ClusterEditView(generic.ObjectEditView):
@@ -237,12 +231,10 @@ class VirtualMachineListView(generic.ObjectListView):
 class VirtualMachineView(generic.ObjectView):
 class VirtualMachineView(generic.ObjectView):
     queryset = VirtualMachine.objects.prefetch_related('tenant__group')
     queryset = VirtualMachine.objects.prefetch_related('tenant__group')
 
 
-    def get(self, request, pk):
-        virtualmachine = get_object_or_404(self.queryset, pk=pk)
-
+    def get_extra_context(self, request, instance):
         # Interfaces
         # Interfaces
         vminterfaces = VMInterface.objects.restrict(request.user, 'view').filter(
         vminterfaces = VMInterface.objects.restrict(request.user, 'view').filter(
-            virtual_machine=virtualmachine
+            virtual_machine=instance
         ).prefetch_related(
         ).prefetch_related(
             Prefetch('ip_addresses', queryset=IPAddress.objects.restrict(request.user))
             Prefetch('ip_addresses', queryset=IPAddress.objects.restrict(request.user))
         )
         )
@@ -253,20 +245,19 @@ class VirtualMachineView(generic.ObjectView):
 
 
         # Services
         # Services
         services = Service.objects.restrict(request.user, 'view').filter(
         services = Service.objects.restrict(request.user, 'view').filter(
-            virtual_machine=virtualmachine
+            virtual_machine=instance
         ).prefetch_related(
         ).prefetch_related(
             Prefetch('ipaddresses', queryset=IPAddress.objects.restrict(request.user))
             Prefetch('ipaddresses', queryset=IPAddress.objects.restrict(request.user))
         )
         )
 
 
         # Secrets
         # Secrets
-        secrets = Secret.objects.restrict(request.user, 'view').filter(virtual_machine=virtualmachine)
+        secrets = Secret.objects.restrict(request.user, 'view').filter(virtual_machine=instance)
 
 
-        return render(request, 'virtualization/virtualmachine.html', {
-            'object': virtualmachine,
+        return {
             'vminterface_table': vminterface_table,
             'vminterface_table': vminterface_table,
             'services': services,
             'services': services,
             'secrets': secrets,
             'secrets': secrets,
-        })
+        }
 
 
 
 
 class VirtualMachineConfigContextView(ObjectConfigContextView):
 class VirtualMachineConfigContextView(ObjectConfigContextView):
@@ -318,35 +309,31 @@ class VMInterfaceListView(generic.ObjectListView):
 class VMInterfaceView(generic.ObjectView):
 class VMInterfaceView(generic.ObjectView):
     queryset = VMInterface.objects.all()
     queryset = VMInterface.objects.all()
 
 
-    def get(self, request, pk):
-
-        vminterface = get_object_or_404(self.queryset, pk=pk)
-
+    def get_extra_context(self, request, instance):
         # Get assigned IP addresses
         # Get assigned IP addresses
         ipaddress_table = InterfaceIPAddressTable(
         ipaddress_table = InterfaceIPAddressTable(
-            data=vminterface.ip_addresses.restrict(request.user, 'view').prefetch_related('vrf', 'tenant'),
+            data=instance.ip_addresses.restrict(request.user, 'view').prefetch_related('vrf', 'tenant'),
             orderable=False
             orderable=False
         )
         )
 
 
         # Get assigned VLANs and annotate whether each is tagged or untagged
         # Get assigned VLANs and annotate whether each is tagged or untagged
         vlans = []
         vlans = []
-        if vminterface.untagged_vlan is not None:
-            vlans.append(vminterface.untagged_vlan)
+        if instance.untagged_vlan is not None:
+            vlans.append(instance.untagged_vlan)
             vlans[0].tagged = False
             vlans[0].tagged = False
-        for vlan in vminterface.tagged_vlans.restrict(request.user).prefetch_related('site', 'group', 'tenant', 'role'):
+        for vlan in instance.tagged_vlans.restrict(request.user).prefetch_related('site', 'group', 'tenant', 'role'):
             vlan.tagged = True
             vlan.tagged = True
             vlans.append(vlan)
             vlans.append(vlan)
         vlan_table = InterfaceVLANTable(
         vlan_table = InterfaceVLANTable(
-            interface=vminterface,
+            interface=instance,
             data=vlans,
             data=vlans,
             orderable=False
             orderable=False
         )
         )
 
 
-        return render(request, 'virtualization/vminterface.html', {
-            'object': vminterface,
+        return {
             'ipaddress_table': ipaddress_table,
             'ipaddress_table': ipaddress_table,
             'vlan_table': vlan_table,
             'vlan_table': vlan_table,
-        })
+        }
 
 
 
 
 # TODO: This should not use ComponentCreateView
 # TODO: This should not use ComponentCreateView