diff --git a/netbox/circuits/views.py b/netbox/circuits/views.py index 2edb5a78e..8e58cfffa 100644 --- a/netbox/circuits/views.py +++ b/netbox/circuits/views.py @@ -26,11 +26,9 @@ class ProviderListView(generic.ObjectListView): class ProviderView(generic.ObjectView): 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( - provider=provider + provider=instance ).prefetch_related( 'type', 'tenant', 'terminations__site' ).annotate_sites() @@ -44,10 +42,9 @@ class ProviderView(generic.ObjectView): } RequestConfig(request, paginate).configure(circuits_table) - return render(request, 'circuits/provider.html', { - 'provider': provider, + return { 'circuits_table': circuits_table, - }) + } class ProviderEditView(generic.ObjectEditView): @@ -124,30 +121,30 @@ class CircuitListView(generic.ObjectListView): class CircuitView(generic.ObjectView): 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( 'site__region' ).filter( - circuit=circuit, term_side=CircuitTerminationSideChoices.SIDE_A + circuit=instance, term_side=CircuitTerminationSideChoices.SIDE_A ).first() if termination_a and termination_a.connected_endpoint: 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( 'site__region' ).filter( - circuit=circuit, term_side=CircuitTerminationSideChoices.SIDE_Z + circuit=instance, term_side=CircuitTerminationSideChoices.SIDE_Z ).first() if termination_z and termination_z.connected_endpoint: termination_z.ip_addresses = termination_z.connected_endpoint.ip_addresses.restrict(request.user, 'view') - return render(request, 'circuits/circuit.html', { - 'circuit': circuit, + return { 'termination_a': termination_a, 'termination_z': termination_z, - }) + } class CircuitEditView(generic.ObjectEditView): diff --git a/netbox/dcim/tables/template_code.py b/netbox/dcim/tables/template_code.py index fa1ec976f..34ec06900 100644 --- a/netbox/dcim/tables/template_code.py +++ b/netbox/dcim/tables/template_code.py @@ -100,9 +100,9 @@ CONSOLEPORT_BUTTONS = """ {% endif %} @@ -120,9 +120,9 @@ CONSOLESERVERPORT_BUTTONS = """ {% endif %} @@ -140,8 +140,8 @@ POWERPORT_BUTTONS = """ {% endif %} @@ -154,7 +154,7 @@ POWEROUTLET_BUTTONS = """ {% elif perms.dcim.add_cable %} - + {% endif %} @@ -162,7 +162,7 @@ POWEROUTLET_BUTTONS = """ INTERFACE_BUTTONS = """ {% if perms.ipam.add_ipaddress %} - + {% endif %} @@ -177,10 +177,10 @@ INTERFACE_BUTTONS = """ {% endif %} @@ -198,12 +198,12 @@ FRONTPORT_BUTTONS = """ {% endif %} @@ -221,10 +221,10 @@ REARPORT_BUTTONS = """ {% endif %} @@ -233,11 +233,11 @@ REARPORT_BUTTONS = """ DEVICEBAY_BUTTONS = """ {% if perms.dcim.change_devicebay %} {% if record.installed_device %} - + {% else %} - + {% endif %} diff --git a/netbox/dcim/urls.py b/netbox/dcim/urls.py index 2cd2ed27b..d167ebdb7 100644 --- a/netbox/dcim/urls.py +++ b/netbox/dcim/urls.py @@ -197,7 +197,7 @@ urlpatterns = [ path('devices//device-bays/', views.DeviceDeviceBaysView.as_view(), name='device_devicebays'), path('devices//inventory/', views.DeviceInventoryView.as_view(), name='device_inventory'), path('devices//config-context/', views.DeviceConfigContextView.as_view(), name='device_configcontext'), - path('devices//changelog/', ObjectChangeLogView.as_view(), name='device_changelog', kwargs={'model': Device}), + path('devices//changelog/', views.DeviceChangeLogView.as_view(), name='device_changelog', kwargs={'model': Device}), path('devices//status/', views.DeviceStatusView.as_view(), name='device_status'), path('devices//lldp-neighbors/', views.DeviceLLDPNeighborsView.as_view(), name='device_lldp_neighbors'), path('devices//config/', views.DeviceConfigView.as_view(), name='device_config'), diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index a632bc6f3..0df8d818d 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -12,7 +12,7 @@ from django.utils.safestring import mark_safe from django.views.generic import View from circuits.models import Circuit -from extras.views import ObjectConfigContextView +from extras.views import ObjectChangeLogView, ObjectConfigContextView from ipam.models import IPAddress, Prefix, Service, VLAN from ipam.tables import InterfaceIPAddressTable, InterfaceVLANTable from netbox.views import generic @@ -152,16 +152,14 @@ class SiteListView(generic.ObjectListView): class SiteView(generic.ObjectView): 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 = { - '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( RackGroup.objects.all(), @@ -169,13 +167,12 @@ class SiteView(generic.ObjectView): 'group', 'rack_count', cumulative=True - ).restrict(request.user, 'view').filter(site=site) + ).restrict(request.user, 'view').filter(site=instance) - return render(request, 'dcim/site.html', { - 'site': site, + return { 'stats': stats, 'rack_groups': rack_groups, - }) + } class SiteEditView(generic.ObjectEditView): @@ -338,36 +335,37 @@ class RackElevationListView(generic.ObjectListView): class RackView(generic.ObjectView): 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 nonracked_devices = Device.objects.filter( - rack=rack, + rack=instance, position__isnull=True ).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: 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' + ) - return render(request, 'dcim/rack.html', { - 'rack': rack, - 'device_count': Device.objects.restrict(request.user, 'view').filter(rack=rack).count(), + device_count = Device.objects.restrict(request.user, 'view').filter(rack=instance).count() + + return { + 'device_count': device_count, 'reservations': reservations, 'power_feeds': power_feeds, 'nonracked_devices': nonracked_devices, 'next_rack': next_rack, 'prev_rack': prev_rack, - }) + } class RackEditView(generic.ObjectEditView): @@ -413,14 +411,6 @@ class RackReservationListView(generic.ObjectListView): class RackReservationView(generic.ObjectView): queryset = RackReservation.objects.prefetch_related('rack') - def get(self, request, pk): - - rackreservation = get_object_or_404(self.queryset, pk=pk) - - return render(request, 'dcim/rackreservation.html', { - 'rackreservation': rackreservation, - }) - class RackReservationEditView(generic.ObjectEditView): queryset = RackReservation.objects.all() @@ -519,42 +509,40 @@ class DeviceTypeListView(generic.ObjectListView): class DeviceTypeView(generic.ObjectView): 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 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) if request.user.has_perm('dcim.change_devicetype'): @@ -567,8 +555,7 @@ class DeviceTypeView(generic.ObjectView): rear_port_table.columns.show('pk') devicebay_table.columns.show('pk') - return render(request, 'dcim/devicetype.html', { - 'devicetype': devicetype, + return { 'instance_count': instance_count, 'consoleport_table': consoleport_table, 'consoleserverport_table': consoleserverport_table, @@ -578,7 +565,7 @@ class DeviceTypeView(generic.ObjectView): 'front_port_table': front_port_table, 'rear_port_table': rear_port_table, 'devicebay_table': devicebay_table, - }) + } class DeviceTypeEditView(generic.ObjectEditView): @@ -995,50 +982,45 @@ class DeviceView(generic.ObjectView): 'site__region', 'rack__group', 'tenant__group', 'device_role', 'platform', 'primary_ip4', 'primary_ip6' ) - def get(self, request, pk): - - device = get_object_or_404(self.queryset, pk=pk) - + def get_extra_context(self, request, instance): # 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( - virtual_chassis=device.virtual_chassis + virtual_chassis=instance.virtual_chassis ).order_by('vc_position') else: vc_members = [] # Services - services = Service.objects.restrict(request.user, 'view').filter(device=device) + services = Service.objects.restrict(request.user, 'view').filter(device=instance) # 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. 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( - pk=device.pk + pk=instance.pk ).prefetch_related( 'rack', 'device_type__manufacturer' )[:10] - return render(request, 'dcim/device/device.html', { - 'device': device, + return { 'services': services, 'secrets': secrets, 'vc_members': vc_members, 'related_devices': related_devices, 'active_tab': 'device', - }) + } class DeviceConsolePortsView(generic.ObjectView): 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', ) consoleport_table = tables.DeviceConsolePortTable( @@ -1049,21 +1031,19 @@ class DeviceConsolePortsView(generic.ObjectView): if request.user.has_perm('dcim.change_consoleport') or request.user.has_perm('dcim.delete_consoleport'): consoleport_table.columns.show('pk') - return render(request, 'dcim/device/consoleports.html', { - 'device': device, + return { 'consoleport_table': consoleport_table, 'active_tab': 'console-ports', - }) + } class DeviceConsoleServerPortsView(generic.ObjectView): 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( - device=device + device=instance ).prefetch_related( 'cable', '_path__destination', ) @@ -1076,20 +1056,18 @@ class DeviceConsoleServerPortsView(generic.ObjectView): request.user.has_perm('dcim.delete_consoleserverport'): consoleserverport_table.columns.show('pk') - return render(request, 'dcim/device/consoleserverports.html', { - 'device': device, + return { 'consoleserverport_table': consoleserverport_table, 'active_tab': 'console-server-ports', - }) + } class DevicePowerPortsView(generic.ObjectView): 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', ) powerport_table = tables.DevicePowerPortTable( @@ -1100,20 +1078,18 @@ class DevicePowerPortsView(generic.ObjectView): if request.user.has_perm('dcim.change_powerport') or request.user.has_perm('dcim.delete_powerport'): powerport_table.columns.show('pk') - return render(request, 'dcim/device/powerports.html', { - 'device': device, + return { 'powerport_table': powerport_table, 'active_tab': 'power-ports', - }) + } class DevicePowerOutletsView(generic.ObjectView): 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', ) poweroutlet_table = tables.DevicePowerOutletTable( @@ -1124,20 +1100,18 @@ class DevicePowerOutletsView(generic.ObjectView): if request.user.has_perm('dcim.change_poweroutlet') or request.user.has_perm('dcim.delete_poweroutlet'): poweroutlet_table.columns.show('pk') - return render(request, 'dcim/device/poweroutlets.html', { - 'device': device, + return { 'poweroutlet_table': poweroutlet_table, 'active_tab': 'power-outlets', - }) + } class DeviceInterfacesView(generic.ObjectView): 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('member_interfaces', queryset=Interface.objects.restrict(request.user)), 'lag', 'cable', '_path__destination', 'tags', @@ -1150,20 +1124,18 @@ class DeviceInterfacesView(generic.ObjectView): if request.user.has_perm('dcim.change_interface') or request.user.has_perm('dcim.delete_interface'): interface_table.columns.show('pk') - return render(request, 'dcim/device/interfaces.html', { - 'device': device, + return { 'interface_table': interface_table, 'active_tab': 'interfaces', - }) + } class DeviceFrontPortsView(generic.ObjectView): 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', ) frontport_table = tables.DeviceFrontPortTable( @@ -1174,20 +1146,18 @@ class DeviceFrontPortsView(generic.ObjectView): if request.user.has_perm('dcim.change_frontport') or request.user.has_perm('dcim.delete_frontport'): frontport_table.columns.show('pk') - return render(request, 'dcim/device/frontports.html', { - 'device': device, + return { 'frontport_table': frontport_table, 'active_tab': 'front-ports', - }) + } class DeviceRearPortsView(generic.ObjectView): 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( data=rearports, user=request.user, @@ -1196,20 +1166,18 @@ class DeviceRearPortsView(generic.ObjectView): if request.user.has_perm('dcim.change_rearport') or request.user.has_perm('dcim.delete_rearport'): rearport_table.columns.show('pk') - return render(request, 'dcim/device/rearports.html', { - 'device': device, + return { 'rearport_table': rearport_table, 'active_tab': 'rear-ports', - }) + } class DeviceDeviceBaysView(generic.ObjectView): 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', ) devicebay_table = tables.DeviceDeviceBayTable( @@ -1220,21 +1188,19 @@ class DeviceDeviceBaysView(generic.ObjectView): if request.user.has_perm('dcim.change_devicebay') or request.user.has_perm('dcim.delete_devicebay'): devicebay_table.columns.show('pk') - return render(request, 'dcim/device/devicebays.html', { - 'device': device, + return { 'devicebay_table': devicebay_table, 'active_tab': 'device-bays', - }) + } class DeviceInventoryView(generic.ObjectView): 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( - device=device + device=instance ).prefetch_related('manufacturer') inventoryitem_table = tables.DeviceInventoryItemTable( data=inventoryitems, @@ -1244,56 +1210,50 @@ class DeviceInventoryView(generic.ObjectView): if request.user.has_perm('dcim.change_inventoryitem') or request.user.has_perm('dcim.delete_inventoryitem'): inventoryitem_table.columns.show('pk') - return render(request, 'dcim/device/inventory.html', { - 'device': device, + return { 'inventoryitem_table': inventoryitem_table, 'active_tab': 'inventory', - }) + } class DeviceStatusView(generic.ObjectView): additional_permissions = ['dcim.napalm_read_device'] queryset = Device.objects.all() + template_name = 'dcim/device/status.html' - def get(self, request, pk): - device = get_object_or_404(self.queryset, pk=pk) - - return render(request, 'dcim/device/status.html', { - 'device': device, + def get_extra_context(self, request, instance): + return { 'active_tab': 'status', - }) + } class DeviceLLDPNeighborsView(generic.ObjectView): additional_permissions = ['dcim.napalm_read_device'] 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 ) - return render(request, 'dcim/device/lldp_neighbors.html', { - 'device': device, + return { 'interfaces': interfaces, 'active_tab': 'lldp-neighbors', - }) + } class DeviceConfigView(generic.ObjectView): additional_permissions = ['dcim.napalm_read_device'] queryset = Device.objects.all() + template_name = 'dcim/device/config.html' - def get(self, request, pk): - - device = get_object_or_404(self.queryset, pk=pk) - - return render(request, 'dcim/device/config.html', { - 'device': device, + def get_extra_context(self, request, instance): + return { 'active_tab': 'config', - }) + } class DeviceConfigContextView(ObjectConfigContextView): @@ -1301,6 +1261,10 @@ class DeviceConfigContextView(ObjectConfigContextView): base_template = 'dcim/device/base.html' +class DeviceChangeLogView(ObjectChangeLogView): + base_template = 'dcim/device/base.html' + + class DeviceEditView(generic.ObjectEditView): queryset = Device.objects.all() model_form = forms.DeviceForm @@ -1604,35 +1568,31 @@ class InterfaceListView(generic.ObjectListView): class InterfaceView(generic.ObjectView): 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 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 ) # Get assigned VLANs and annotate whether each is tagged or untagged 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 - 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 vlans.append(vlan) vlan_table = InterfaceVLANTable( - interface=interface, + interface=instance, data=vlans, orderable=False ) - return render(request, 'dcim/interface.html', { - 'instance': interface, + return { 'ipaddress_table': ipaddress_table, 'vlan_table': vlan_table, - }) + } class InterfaceCreateView(generic.ComponentCreateView): @@ -2093,20 +2053,13 @@ class CableListView(generic.ObjectListView): class CableView(generic.ObjectView): queryset = Cable.objects.all() - def get(self, request, pk): - - cable = get_object_or_404(self.queryset, pk=pk) - - return render(request, 'dcim/cable.html', { - 'cable': cable, - }) - class PathTraceView(generic.ObjectView): """ Trace a cable path beginning from the given path endpoint (origin). """ additional_permissions = ['dcim.view_cable'] + template_name = 'dcim/cable_trace.html' def dispatch(self, request, *args, **kwargs): model = kwargs.pop('model') @@ -2114,13 +2067,12 @@ class PathTraceView(generic.ObjectView): 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 = [] # 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 else: @@ -2135,12 +2087,11 @@ class PathTraceView(generic.ObjectView): else: path = related_paths.first() - return render(request, 'dcim/cable_trace.html', { - 'obj': obj, + return { 'path': path, 'related_paths': related_paths, 'total_length': path.get_total_length() if path else None, - }) + } class CableCreateView(generic.ObjectEditView): @@ -2347,14 +2298,12 @@ class VirtualChassisListView(generic.ObjectListView): class VirtualChassisView(generic.ObjectView): 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', { - 'virtualchassis': virtualchassis, + return { 'members': members, - }) + } class VirtualChassisCreateView(generic.ObjectEditView): @@ -2577,20 +2526,17 @@ class PowerPanelListView(generic.ObjectListView): class PowerPanelView(generic.ObjectView): 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( data=power_feeds, orderable=False ) powerfeed_table.exclude = ['power_panel'] - return render(request, 'dcim/powerpanel.html', { - 'powerpanel': powerpanel, + return { 'powerfeed_table': powerfeed_table, - }) + } class PowerPanelEditView(generic.ObjectEditView): @@ -2640,14 +2586,6 @@ class PowerFeedListView(generic.ObjectListView): class PowerFeedView(generic.ObjectView): queryset = PowerFeed.objects.prefetch_related('power_panel', 'rack') - def get(self, request, pk): - - powerfeed = get_object_or_404(self.queryset, pk=pk) - - return render(request, 'dcim/powerfeed.html', { - 'powerfeed': powerfeed, - }) - class PowerFeedEditView(generic.ObjectEditView): queryset = PowerFeed.objects.all() diff --git a/netbox/extras/views.py b/netbox/extras/views.py index 9fb6c6deb..8f72da59b 100644 --- a/netbox/extras/views.py +++ b/netbox/extras/views.py @@ -83,21 +83,7 @@ class ConfigContextListView(generic.ObjectListView): class ConfigContextView(generic.ObjectView): 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 if request.GET.get('format') in ['json', 'yaml']: format = request.GET.get('format') @@ -108,10 +94,9 @@ class ConfigContextView(generic.ObjectView): else: format = 'json' - return render(request, 'extras/configcontext.html', { - 'configcontext': configcontext, + return { 'format': format, - }) + } class ConfigContextEditView(generic.ObjectEditView): @@ -138,12 +123,10 @@ class ConfigContextBulkDeleteView(generic.BulkDeleteView): class ObjectConfigContextView(generic.ObjectView): 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 if request.GET.get('format') in ['json', 'yaml']: @@ -155,15 +138,13 @@ class ObjectConfigContextView(generic.ObjectView): else: format = 'json' - return render(request, 'extras/object_configcontext.html', { - model_name: obj, - 'obj': obj, - 'rendered_context': obj.get_config_context(), + return { + 'rendered_context': instance.get_config_context(), 'source_contexts': source_contexts, 'format': format, 'base_template': self.base_template, 'active_tab': 'config-context', - }) + } # @@ -182,14 +163,11 @@ class ObjectChangeListView(generic.ObjectListView): class ObjectChangeView(generic.ObjectView): 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( - request_id=objectchange.request_id + request_id=instance.request_id ).exclude( - pk=objectchange.pk + pk=instance.pk ) related_changes_table = tables.ObjectChangeTable( data=related_changes[:50], @@ -197,39 +175,41 @@ class ObjectChangeView(generic.ObjectView): ) 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: diff_added = shallow_compare_dict( prev_change.object_data, - objectchange.object_data, + instance.object_data, exclude=['last_updated'], ) diff_removed = {x: prev_change.object_data.get(x) for x in diff_added} else: # 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', { - 'objectchange': objectchange, + return { 'diff_added': diff_added, 'diff_removed': diff_removed, 'next_change': next_change, 'prev_change': prev_change, 'related_changes_table': related_changes_table, 'related_changes_count': related_changes.count() - }) + } class ObjectChangeLogView(View): """ Present a history of changes made to a particular object. + + base_template: The name of the template to extend. If not provided, "/.html" will be used. """ + base_template = None def get(self, request, model, **kwargs): @@ -259,20 +239,20 @@ class ObjectChangeLogView(View): } RequestConfig(request, paginate).configure(objectchanges_table) - # Check whether a header template exists for this model - base_template = '{}/{}.html'.format(model._meta.app_label, model._meta.model_name) - try: - template.loader.get_template(base_template) - object_var = model._meta.model_name - except template.TemplateDoesNotExist: - base_template = 'base.html' - object_var = 'obj' + # Default to using "/.html" as the template, if it exists. Otherwise, + # fall back to using base.html. + if self.base_template is None: + self.base_template = f"{model._meta.app_label}/{model._meta.model_name}.html" + # TODO: This can be removed once an object view has been established for every model. + try: + template.loader.get_template(self.base_template) + except template.TemplateDoesNotExist: + self.base_template = 'base.html' return render(request, 'extras/object_changelog.html', { - object_var: obj, - 'instance': obj, # We'll eventually standardize on 'instance` for the object variable name + 'object': obj, 'table': objectchanges_table, - 'base_template': base_template, + 'base_template': self.base_template, 'active_tab': 'changelog', }) diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index d2248e84c..5769f0a68 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -28,26 +28,23 @@ class VRFListView(generic.ObjectListView): class VRFView(generic.ObjectView): 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( - vrf.import_targets.prefetch_related('tenant'), + instance.import_targets.prefetch_related('tenant'), orderable=False ) export_targets_table = tables.RouteTargetTable( - vrf.export_targets.prefetch_related('tenant'), + instance.export_targets.prefetch_related('tenant'), orderable=False ) - return render(request, 'ipam/vrf.html', { - 'vrf': vrf, + return { 'prefix_count': prefix_count, 'import_targets_table': import_targets_table, 'export_targets_table': export_targets_table, - }) + } class VRFEditView(generic.ObjectEditView): @@ -93,23 +90,20 @@ class RouteTargetListView(generic.ObjectListView): class RouteTargetView(generic.ObjectView): 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( - routetarget.importing_vrfs.prefetch_related('tenant'), + instance.importing_vrfs.prefetch_related('tenant'), orderable=False ) exporting_vrfs_table = tables.VRFTable( - routetarget.exporting_vrfs.prefetch_related('tenant'), + instance.exporting_vrfs.prefetch_related('tenant'), orderable=False ) - return render(request, 'ipam/routetarget.html', { - 'routetarget': routetarget, + return { 'importing_vrfs_table': importing_vrfs_table, 'exporting_vrfs_table': exporting_vrfs_table, - }) + } class RouteTargetEditView(generic.ObjectEditView): @@ -206,13 +200,10 @@ class AggregateListView(generic.ObjectListView): class AggregateView(generic.ObjectView): 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 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( 'site', 'role' ).order_by( @@ -221,7 +212,7 @@ class AggregateView(generic.ObjectView): # Add available prefixes to the table if requested 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) 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'), } - return render(request, 'ipam/aggregate.html', { - 'aggregate': aggregate, + return { 'prefix_table': prefix_table, 'permissions': permissions, 'show_available': request.GET.get('show_available', 'true') == 'true', - }) + } class AggregateEditView(generic.ObjectEditView): @@ -324,22 +314,19 @@ class PrefixListView(generic.ObjectListView): class PrefixView(generic.ObjectView): 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: 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: aggregate = None # Parent prefixes table 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( - prefix__net_contains=str(prefix.prefix) + prefix__net_contains=str(instance.prefix) ).prefetch_related( 'site', 'role' ).annotate_tree() @@ -348,38 +335,35 @@ class PrefixView(generic.ObjectView): # Duplicate prefixes table duplicate_prefixes = Prefix.objects.restrict(request.user, 'view').filter( - vrf=prefix.vrf, prefix=str(prefix.prefix) + vrf=instance.vrf, prefix=str(instance.prefix) ).exclude( - pk=prefix.pk + pk=instance.pk ).prefetch_related( 'site', 'role' ) duplicate_prefix_table = tables.PrefixTable(list(duplicate_prefixes), orderable=False) duplicate_prefix_table.exclude = ('vrf',) - return render(request, 'ipam/prefix.html', { - 'prefix': prefix, + return { 'aggregate': aggregate, 'parent_prefix_table': parent_prefix_table, 'duplicate_prefix_table': duplicate_prefix_table, - }) + } class PrefixPrefixesView(generic.ObjectView): 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 = 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', ).annotate_tree() # Add available prefixes to the table if requested 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) 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'), } - return render(request, 'ipam/prefix_prefixes.html', { - 'prefix': 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, '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', 'show_available': request.GET.get('show_available', 'true') == 'true', - }) + } class PrefixIPAddressesView(generic.ObjectView): 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 - 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' ) # Add available IP addresses to the table if requested 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) 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'), } - return render(request, 'ipam/prefix_ipaddresses.html', { - 'prefix': 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, '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', 'show_available': request.GET.get('show_available', 'true') == 'true', - }) + } class PrefixEditView(generic.ObjectEditView): @@ -497,13 +481,11 @@ class IPAddressListView(generic.ObjectListView): class IPAddressView(generic.ObjectView): 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 = 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( 'site', 'role' ) @@ -512,23 +494,24 @@ class IPAddressView(generic.ObjectView): # Duplicate IPs table duplicate_ips = IPAddress.objects.restrict(request.user, 'view').filter( - vrf=ipaddress.vrf, address=str(ipaddress.address) + vrf=instance.vrf, + address=str(instance.address) ).exclude( - pk=ipaddress.pk + pk=instance.pk ).prefetch_related( 'nat_inside' ) # 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) # Limit to a maximum of 10 duplicates displayed here duplicate_ips_table = tables.IPAddressTable(duplicate_ips[:10], orderable=False) # Related IP table related_ips = IPAddress.objects.restrict(request.user, 'view').exclude( - address=str(ipaddress.address) + address=str(instance.address) ).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) @@ -538,13 +521,12 @@ class IPAddressView(generic.ObjectView): } RequestConfig(request, paginate).configure(related_ips_table) - return render(request, 'ipam/ipaddress.html', { - 'ipaddress': ipaddress, + return { 'parent_prefixes_table': parent_prefixes_table, 'duplicate_ips_table': duplicate_ips_table, 'more_duplicate_ips': duplicate_ips.count() > 10, 'related_ips_table': related_ips_table, - }) + } class IPAddressEditView(generic.ObjectEditView): @@ -569,6 +551,7 @@ class IPAddressEditView(generic.ObjectEditView): return obj +# TODO: Standardize or remove this view class IPAddressAssignView(generic.ObjectView): """ Search for IPAddresses to be assigned to an Interface. @@ -678,14 +661,13 @@ class VLANGroupBulkDeleteView(generic.BulkDeleteView): class VLANGroupVLANsView(generic.ObjectView): 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)) ) - vlans = add_available_vlans(vlan_group, vlans) + vlans = add_available_vlans(instance, vlans) vlan_table = tables.VLANDetailTable(vlans) 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'), } - 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, 'permissions': permissions, - }) + } # @@ -729,27 +710,24 @@ class VLANListView(generic.ObjectListView): class VLANView(generic.ObjectView): 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' ) prefix_table = tables.PrefixTable(list(prefixes), orderable=False) prefix_table.exclude = ('vlan',) - return render(request, 'ipam/vlan.html', { - 'vlan': vlan, + return { 'prefix_table': prefix_table, - }) + } class VLANInterfacesView(generic.ObjectView): 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) paginate = { @@ -758,19 +736,18 @@ class VLANInterfacesView(generic.ObjectView): } RequestConfig(request, paginate).configure(members_table) - return render(request, 'ipam/vlan_interfaces.html', { - 'vlan': vlan, + return { 'members_table': members_table, 'active_tab': 'interfaces', - }) + } class VLANVMInterfacesView(generic.ObjectView): 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) paginate = { @@ -779,11 +756,10 @@ class VLANVMInterfacesView(generic.ObjectView): } RequestConfig(request, paginate).configure(members_table) - return render(request, 'ipam/vlan_vminterfaces.html', { - 'vlan': vlan, + return { 'members_table': members_table, 'active_tab': 'vminterfaces', - }) + } class VLANEditView(generic.ObjectEditView): @@ -830,14 +806,6 @@ class ServiceListView(generic.ObjectListView): class ServiceView(generic.ObjectView): queryset = Service.objects.prefetch_related('ipaddresses') - def get(self, request, pk): - - service = get_object_or_404(self.queryset, pk=pk) - - return render(request, 'ipam/service.html', { - 'service': service, - }) - class ServiceEditView(generic.ObjectEditView): queryset = Service.objects.prefetch_related('ipaddresses') diff --git a/netbox/netbox/views/generic.py b/netbox/netbox/views/generic.py index 7731dfe24..eb7b2542f 100644 --- a/netbox/netbox/views/generic.py +++ b/netbox/netbox/views/generic.py @@ -32,9 +32,11 @@ class ObjectView(ObjectPermissionRequiredMixin, View): """ 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 + template_name = None def get_required_permission(self): return get_permission_for_model(self.queryset.model, 'view') @@ -43,19 +45,29 @@ class ObjectView(ObjectPermissionRequiredMixin, View): """ 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 model_opts = self.queryset.model._meta return f'{model_opts.app_label}/{model_opts.model_name}.html' - def get(self, request, pk): + def get_extra_context(self, request, instance): """ - Generic GET handler for accessing an object by PK + Return any additional context data for the template. + + request: The current request + instance: The object being viewed """ - instance = get_object_or_404(self.queryset, pk=pk) + return {} + + def get(self, request, *args, **kwargs): + """ + Generic GET handler for accessing an object by PK or slug + """ + instance = get_object_or_404(self.queryset, **kwargs) return render(request, self.get_template_name(), { - 'instance': instance, + 'object': instance, + **self.get_extra_context(request, instance), }) diff --git a/netbox/secrets/views.py b/netbox/secrets/views.py index 539cbb160..14253486d 100644 --- a/netbox/secrets/views.py +++ b/netbox/secrets/views.py @@ -66,14 +66,6 @@ class SecretListView(generic.ObjectListView): class SecretView(generic.ObjectView): queryset = Secret.objects.all() - def get(self, request, pk): - - secret = get_object_or_404(self.queryset, pk=pk) - - return render(request, 'secrets/secret.html', { - 'secret': secret, - }) - class SecretEditView(generic.ObjectEditView): queryset = Secret.objects.all() diff --git a/netbox/templates/circuits/circuit.html b/netbox/templates/circuits/circuit.html index dde053ee0..c104b154b 100644 --- a/netbox/templates/circuits/circuit.html +++ b/netbox/templates/circuits/circuit.html @@ -4,15 +4,15 @@ {% load helpers %} {% load plugins %} -{% block title %}{{ circuit }}{% endblock %} +{% block title %}{{ object }}{% endblock %} {% block header %}
@@ -29,29 +29,29 @@
- {% plugin_buttons circuit %} + {% plugin_buttons object %} {% if perms.circuits.add_circuit %} - {% clone_button circuit %} + {% clone_button object %} {% endif %} {% if perms.circuits.change_circuit %} - {% edit_button circuit %} + {% edit_button object %} {% endif %} {% if perms.circuits.delete_circuit %} - {% delete_button circuit %} + {% delete_button object %} {% endif %}
-

{{ circuit }}

- {% include 'inc/created_updated.html' with obj=circuit %} +

{{ object }}

+ {% include 'inc/created_updated.html' %}
- {% custom_links circuit %} + {% custom_links object %}
@@ -68,31 +68,31 @@ Status - {{ circuit.get_status_display }} + {{ object.get_status_display }} Provider - {{ circuit.provider }} + {{ object.provider }} Circuit ID - {{ circuit.cid }} + {{ object.cid }} Type - {{ circuit.type }} + {{ object.type }} Tenant - {% if circuit.tenant %} - {% if circuit.tenant.group %} - {{ circuit.tenant.group }} / + {% if object.tenant %} + {% if object.tenant.group %} + {{ object.tenant.group }} / {% endif %} - {{ circuit.tenant }} + {{ object.tenant }} {% else %} None {% endif %} @@ -100,43 +100,43 @@ Install Date - {{ circuit.install_date|placeholder }} + {{ object.install_date|placeholder }} Commit Rate - {{ circuit.commit_rate|humanize_speed|placeholder }} + {{ object.commit_rate|humanize_speed|placeholder }} Description - {{ circuit.description|placeholder }} + {{ object.description|placeholder }} - {% include 'inc/custom_fields_panel.html' with obj=circuit %} - {% include 'extras/inc/tags_panel.html' with tags=circuit.tags.all url='circuits:circuit_list' %} + {% include 'inc/custom_fields_panel.html' %} + {% include 'extras/inc/tags_panel.html' with tags=object.tags.all url='circuits:circuit_list' %}
Comments
- {% if circuit.comments %} - {{ circuit.comments|render_markdown }} + {% if object.comments %} + {{ object.comments|render_markdown }} {% else %} None {% endif %}
- {% plugin_left_page circuit %} + {% plugin_left_page object %}
{% include 'circuits/inc/circuit_termination.html' with termination=termination_a side='A' %} {% include 'circuits/inc/circuit_termination.html' with termination=termination_z side='Z' %} - {% plugin_right_page circuit %} + {% plugin_right_page object %}
- {% plugin_full_width_page circuit %} + {% plugin_full_width_page object %}
{% endblock %} diff --git a/netbox/templates/circuits/inc/circuit_termination.html b/netbox/templates/circuits/inc/circuit_termination.html index aaeb114eb..477788931 100644 --- a/netbox/templates/circuits/inc/circuit_termination.html +++ b/netbox/templates/circuits/inc/circuit_termination.html @@ -4,7 +4,7 @@
{% if not termination and perms.circuits.add_circuittermination %} - + Add {% endif %} @@ -12,12 +12,12 @@ Edit - + Swap {% endif %} {% if termination and perms.circuits.delete_circuittermination %} - + Delete {% endif %} @@ -67,10 +67,10 @@ Connect
diff --git a/netbox/templates/circuits/provider.html b/netbox/templates/circuits/provider.html index a746e4eed..e96d5abcb 100644 --- a/netbox/templates/circuits/provider.html +++ b/netbox/templates/circuits/provider.html @@ -5,14 +5,14 @@ {% load helpers %} {% load plugins %} -{% block title %}{{ provider }}{% endblock %} +{% block title %}{{ object }}{% endblock %} {% block header %}
@@ -29,29 +29,29 @@
- {% plugin_buttons provider %} + {% plugin_buttons object %} {% if perms.circuits.add_provider %} - {% clone_button provider %} + {% clone_button object %} {% endif %} {% if perms.circuits.change_provider %} - {% edit_button provider %} + {% edit_button object %} {% endif %} {% if perms.circuits.delete_provider %} - {% delete_button provider %} + {% delete_button object %} {% endif %}
-

{{ provider }}

- {% include 'inc/created_updated.html' with obj=provider %} +

{{ object }}

+ {% include 'inc/created_updated.html' %}
- {% custom_links provider %} + {% custom_links object %}
@@ -67,17 +67,17 @@ - + - + - + - +
ASN{{ provider.asn|placeholder }}{{ object.asn|placeholder }}
Account{{ provider.account|placeholder }}{{ object.account|placeholder }}
Customer Portal - {% if provider.portal_url %} - {{ provider.portal_url }} + {% if object.portal_url %} + {{ object.portal_url }} {% else %} {% endif %} @@ -85,35 +85,35 @@
NOC Contact{{ provider.noc_contact|render_markdown|placeholder }}{{ object.noc_contact|render_markdown|placeholder }}
Admin Contact{{ provider.admin_contact|render_markdown|placeholder }}{{ object.admin_contact|render_markdown|placeholder }}
Circuits - {{ circuits_table.rows|length }} + {{ circuits_table.rows|length }}
- {% include 'inc/custom_fields_panel.html' with obj=provider %} - {% include 'extras/inc/tags_panel.html' with tags=provider.tags.all url='circuits:provider_list' %} + {% include 'inc/custom_fields_panel.html' %} + {% include 'extras/inc/tags_panel.html' with tags=object.tags.all url='circuits:object_list' %}
Comments
- {% if provider.comments %} - {{ provider.comments|render_markdown }} + {% if object.comments %} + {{ object.comments|render_markdown }} {% else %} None {% endif %}
- {% plugin_left_page provider %} + {% plugin_left_page object %}
@@ -123,19 +123,19 @@ {% include 'inc/table.html' with table=circuits_table %} {% if perms.circuits.add_circuit %} {% endif %}
{% include 'inc/paginator.html' with paginator=circuits_table.paginator page=circuits_table.page %} - {% plugin_right_page provider %} + {% plugin_right_page object %}
- {% plugin_full_width_page provider %} + {% plugin_full_width_page object %}
{% endblock %} diff --git a/netbox/templates/dcim/cable.html b/netbox/templates/dcim/cable.html index bdae87c48..3bc7869fe 100644 --- a/netbox/templates/dcim/cable.html +++ b/netbox/templates/dcim/cable.html @@ -9,31 +9,31 @@
- {% plugin_buttons cable %} + {% plugin_buttons object %} {% if perms.dcim.change_cable %} - {% edit_button cable %} + {% edit_button object %} {% endif %} {% if perms.dcim.delete_cable %} - {% delete_button cable %} + {% delete_button object %} {% endif %}
-

{% block title %}Cable {{ cable }}{% endblock %}

- {% include 'inc/created_updated.html' with obj=cable %} +

{% block title %}Cable {{ object }}{% endblock %}

+ {% include 'inc/created_updated.html' %}
- {% custom_links cable %} + {% custom_links object %}
@@ -49,23 +49,23 @@ - + - +
Type{{ cable.get_type_display|placeholder }}{{ object.get_type_display|placeholder }}
Status - {{ cable.get_status_display }} + {{ object.get_status_display }}
Label{{ cable.label|placeholder }}{{ object.label|placeholder }}
Color - {% if cable.color %} -   + {% if object.color %} +   {% else %} {% endif %} @@ -74,8 +74,8 @@
Length - {% if cable.length %} - {{ cable.length }} {{ cable.get_length_unit_display }} + {% if object.length %} + {{ object.length }} {{ object.get_length_unit_display }} {% else %} {% endif %} @@ -83,29 +83,29 @@
- {% include 'inc/custom_fields_panel.html' with obj=cable %} - {% include 'extras/inc/tags_panel.html' with tags=cable.tags.all url='dcim:cable_list' %} - {% plugin_left_page cable %} + {% include 'inc/custom_fields_panel.html' %} + {% include 'extras/inc/tags_panel.html' with tags=object.tags.all url='dcim:cable_list' %} + {% plugin_left_page object %}
Termination A
- {% include 'dcim/inc/cable_termination.html' with termination=cable.termination_a %} + {% include 'dcim/inc/cable_termination.html' with termination=object.termination_a %}
Termination B
- {% include 'dcim/inc/cable_termination.html' with termination=cable.termination_b %} + {% include 'dcim/inc/cable_termination.html' with termination=object.termination_b %}
- {% plugin_right_page cable %} + {% plugin_right_page object %}
- {% plugin_full_width_page cable %} + {% plugin_full_width_page object %}
{% endblock %} diff --git a/netbox/templates/dcim/cable_trace.html b/netbox/templates/dcim/cable_trace.html index 73003954d..a36922612 100644 --- a/netbox/templates/dcim/cable_trace.html +++ b/netbox/templates/dcim/cable_trace.html @@ -2,7 +2,7 @@ {% load helpers %} {% block header %} -

{% block title %}Cable Trace for {{ obj|meta:"verbose_name"|bettertitle }} {{ obj }}{% endblock %}

+

{% block title %}Cable Trace for {{ object|meta:"verbose_name"|bettertitle }} {{ object }}{% endblock %}

{% endblock %} {% block content %} diff --git a/netbox/templates/dcim/consoleport.html b/netbox/templates/dcim/consoleport.html index ebf4a50b8..5d113c86a 100644 --- a/netbox/templates/dcim/consoleport.html +++ b/netbox/templates/dcim/consoleport.html @@ -13,71 +13,71 @@ Device - {{ instance.device }} + {{ object.device }} Name - {{ instance.name }} + {{ object.name }} Label - {{ instance.label|placeholder }} + {{ object.label|placeholder }} Type - {{ instance.get_type_display }} + {{ object.get_type_display }} Description - {{ instance.description|placeholder }} + {{ object.description|placeholder }} - {% include 'extras/inc/tags_panel.html' with tags=instance.tags.all %} - {% plugin_left_page instance %} + {% include 'extras/inc/tags_panel.html' with tags=object.tags.all %} + {% plugin_left_page object %}
Connection
- {% if instance.cable %} + {% if object.cable %} - {% if instance.connected_endpoint %} + {% if object.connected_endpoint %} - + - + - + - + - + - +
Cable - {{ instance.cable }} - + {{ object.cable }} +
Device - {{ instance.connected_endpoint.device }} + {{ object.connected_endpoint.device }}
Name - {{ instance.connected_endpoint.name }} + {{ object.connected_endpoint.name }}
Type{{ instance.connected_endpoint.get_type_display|placeholder }}{{ object.connected_endpoint.get_type_display|placeholder }}
Description{{ instance.connected_endpoint.description|placeholder }}{{ object.connected_endpoint.description|placeholder }}
Path Status - {% if instance.path.is_active %} + {% if object.path.is_active %} Reachable {% else %} Not Reachable @@ -95,21 +95,21 @@ Connect {% endif %} {% endif %} - {% plugin_right_page instance %} + {% plugin_right_page object %}
- {% plugin_full_width_page instance %} + {% plugin_full_width_page object %}
{% endblock %} diff --git a/netbox/templates/dcim/consoleserverport.html b/netbox/templates/dcim/consoleserverport.html index b90231602..b64b4aff2 100644 --- a/netbox/templates/dcim/consoleserverport.html +++ b/netbox/templates/dcim/consoleserverport.html @@ -13,71 +13,71 @@
Device - {{ instance.device }} + {{ object.device }}
Name{{ instance.name }}{{ object.name }}
Label{{ instance.label|placeholder }}{{ object.label|placeholder }}
Type{{ instance.get_type_display }}{{ object.get_type_display }}
Description{{ instance.description|placeholder }}{{ object.description|placeholder }}
- {% include 'extras/inc/tags_panel.html' with tags=instance.tags.all %} - {% plugin_left_page instance %} + {% include 'extras/inc/tags_panel.html' with tags=object.tags.all %} + {% plugin_left_page object %}
Connection
- {% if instance.cable %} + {% if object.cable %} - {% if instance.connected_endpoint %} + {% if object.connected_endpoint %} - + - + - + - +
Cable - {{ instance.cable }} - + {{ object.cable }} +
Device - {{ instance.connected_endpoint.device }} + {{ object.connected_endpoint.device }}
Name - {{ instance.connected_endpoint.name }} + {{ object.connected_endpoint.name }}
Type{{ instance.connected_endpoint.get_type_display|placeholder }}{{ object.connected_endpoint.get_type_display|placeholder }}
Description{{ instance.connected_endpoint.description|placeholder }}{{ object.connected_endpoint.description|placeholder }}
Path Status - {% if instance.path.is_active %} + {% if object.path.is_active %} Reachable {% else %} Not Reachable @@ -95,21 +95,21 @@ Connect {% endif %} {% endif %} - {% plugin_right_page instance %} + {% plugin_right_page object %}
- {% plugin_full_width_page instance %} + {% plugin_full_width_page object %}
{% endblock %} diff --git a/netbox/templates/dcim/device/device.html b/netbox/templates/dcim/device.html similarity index 83% rename from netbox/templates/dcim/device/device.html rename to netbox/templates/dcim/device.html index 6c9e3a833..97f7c8953 100644 --- a/netbox/templates/dcim/device/device.html +++ b/netbox/templates/dcim/device.html @@ -20,20 +20,20 @@
Site - {% if device.site.region %} - {{ device.site.region }} / + {% if object.site.region %} + {{ object.site.region }} / {% endif %} - {{ device.site }} + {{ object.site }}
Rack - {% if device.rack %} - {% if device.rack.group %} - {{ device.rack.group }} / + {% if object.rack %} + {% if object.rack.group %} + {{ object.rack.group }} / {% endif %} - {{ device.rack }} + {{ object.rack }} {% else %} None {% endif %} @@ -42,16 +42,16 @@
Position - {% if device.parent_bay %} - {% with device.parent_bay.device as parent %} - {{ parent }} / {{ device.parent_bay }} + {% if object.parent_bay %} + {% with object.parent_bay.device as parent %} + {{ parent }} / {{ object.parent_bay }} {% if parent.position %} (U{{ parent.position }} / {{ parent.get_face_display }}) {% endif %} {% endwith %} - {% elif device.rack and device.position %} - U{{ device.position }} / {{ device.get_face_display }} - {% elif device.rack and device.device_type.u_height %} + {% elif object.rack and object.position %} + U{{ object.position }} / {{ object.get_face_display }} + {% elif object.rack and object.device_type.u_height %} Not racked {% else %} @@ -61,11 +61,11 @@
Tenant - {% if device.tenant %} - {% if device.tenant.group %} - {{ device.tenant.group }} / + {% if object.tenant %} + {% if object.tenant.group %} + {{ object.tenant.group }} / {% endif %} - {{ device.tenant }} + {{ object.tenant }} {% else %} None {% endif %} @@ -74,16 +74,16 @@
Device Type - {{ device.device_type.display_name }} ({{ device.device_type.u_height }}U) + {{ object.device_type.display_name }} ({{ object.device_type.u_height }}U)
Serial Number{{ device.serial|placeholder }}{{ object.serial|placeholder }}
Asset Tag{{ device.asset_tag|placeholder }}{{ object.asset_tag|placeholder }}
@@ -100,18 +100,18 @@ Priority {% for vc_member in vc_members %} - + {{ vc_member }} {{ vc_member.vc_position }} - {% if device.virtual_chassis.master == vc_member %}{% endif %} + {% if object.virtual_chassis.master == vc_member %}{% endif %} {{ vc_member.vc_priority|default:"" }} {% endfor %} @@ -125,14 +125,14 @@ Role - {{ device.device_role }} + {{ object.device_role }} Platform - {% if device.platform %} - {{ device.platform }} + {% if object.platform %} + {{ object.platform }} {% else %} None {% endif %} @@ -141,18 +141,18 @@ Status - {{ device.get_status_display }} + {{ object.get_status_display }} Primary IPv4 - {% if device.primary_ip4 %} - {{ device.primary_ip4.address.ip }} - {% if device.primary_ip4.nat_inside %} - (NAT for {{ device.primary_ip4.nat_inside.address.ip }}) - {% elif device.primary_ip4.nat_outside %} - (NAT: {{ device.primary_ip4.nat_outside.address.ip }}) + {% if object.primary_ip4 %} + {{ object.primary_ip4.address.ip }} + {% if object.primary_ip4.nat_inside %} + (NAT for {{ object.primary_ip4.nat_inside.address.ip }}) + {% elif object.primary_ip4.nat_outside %} + (NAT: {{ object.primary_ip4.nat_outside.address.ip }}) {% endif %} {% else %} @@ -162,46 +162,46 @@ Primary IPv6 - {% if device.primary_ip6 %} - {{ device.primary_ip6.address.ip }} - {% if device.primary_ip6.nat_inside %} - (NAT for {{ device.primary_ip6.nat_inside.address.ip }}) - {% elif device.primary_ip6.nat_outside %} - (NAT: {{ device.primary_ip6.nat_outside.address.ip }}) + {% if object.primary_ip6 %} + {{ object.primary_ip6.address.ip }} + {% if object.primary_ip6.nat_inside %} + (NAT for {{ object.primary_ip6.nat_inside.address.ip }}) + {% elif object.primary_ip6.nat_outside %} + (NAT: {{ object.primary_ip6.nat_outside.address.ip }}) {% endif %} {% else %} {% endif %} - {% if device.cluster %} + {% if object.cluster %} Cluster - {% if device.cluster.group %} - {{ device.cluster.group }} / + {% if object.cluster.group %} + {{ object.cluster.group }} / {% endif %} - {{ device.cluster }} + {{ object.cluster }} {% endif %}
- {% include 'inc/custom_fields_panel.html' with obj=device %} - {% include 'extras/inc/tags_panel.html' with tags=device.tags.all url='dcim:device_list' %} + {% include 'inc/custom_fields_panel.html' %} + {% include 'extras/inc/tags_panel.html' with tags=object.tags.all url='dcim:device_list' %}
Comments
- {% if device.comments %} - {{ device.comments|render_markdown }} + {% if object.comments %} + {{ object.comments|render_markdown }} {% else %} None {% endif %}
- {% plugin_left_page device %} + {% plugin_left_page object %}
{% if power_ports and poweroutlets %} @@ -255,7 +255,7 @@ {% include 'secrets/inc/assigned_secrets.html' %} {% if perms.secrets.add_secret %} @@ -279,7 +279,7 @@ {% endif %} {% if perms.ipam.add_service %} @@ -289,10 +289,10 @@
Images
- {% include 'inc/image_attachments.html' with images=device.images.all %} + {% include 'inc/image_attachments.html' with images=object.images.all %} {% if perms.extras.add_imageattachment %} - {% plugin_right_page device %} + {% plugin_right_page object %}
- {% plugin_full_width_page device %} + {% plugin_full_width_page object %}
diff --git a/netbox/templates/dcim/device/base.html b/netbox/templates/dcim/device/base.html index 80c94a31a..2ee046f91 100644 --- a/netbox/templates/dcim/device/base.html +++ b/netbox/templates/dcim/device/base.html @@ -5,19 +5,19 @@ {% load custom_links %} {% load plugins %} -{% block title %}{{ device }}{% endblock %} +{% block title %}{{ object }}{% endblock %} {% block header %}
@@ -34,7 +34,7 @@
- {% plugin_buttons device %} + {% plugin_buttons object %} {% if perms.dcim.change_device %}
{% endif %} {% if perms.dcim.add_device %} - {% clone_button device %} + {% clone_button object %} {% endif %} {% if perms.dcim.change_device %} - {% edit_button device %} + {% edit_button object %} {% endif %} {% if perms.dcim.delete_device %} - {% delete_button device %} + {% delete_button object %} {% endif %}
-

{{ device }}

- {% include 'inc/created_updated.html' with obj=device %} +

{{ object }}

+ {% include 'inc/created_updated.html' %}
- {% custom_links device %} + {% custom_links object %}
diff --git a/netbox/templates/dcim/device/config.html b/netbox/templates/dcim/device/config.html index 5ef941c60..4b73a2577 100644 --- a/netbox/templates/dcim/device/config.html +++ b/netbox/templates/dcim/device/config.html @@ -1,7 +1,7 @@ {% extends 'dcim/device/base.html' %} {% load static %} -{% block title %}{{ device }} - Config{% endblock %} +{% block title %}{{ object }} - Config{% endblock %} {% block content %} {% include 'inc/ajax_loader.html' %} @@ -36,7 +36,7 @@