Jelajahi Sumber

Merge pull request #10609 from netbox-community/10608-registered-model-views

Closes #10608: Register all core model views using register_model_view()
Jeremy Stretch 3 tahun lalu
induk
melakukan
3f37914b3c

+ 1 - 17
netbox/circuits/urls.py

@@ -1,9 +1,7 @@
 from django.urls import include, path
 
-from dcim.views import PathTraceView
 from utilities.urls import get_model_urls
 from . import views
-from .models import CircuitTermination
 
 app_name = 'circuits'
 urlpatterns = [
@@ -14,9 +12,6 @@ urlpatterns = [
     path('providers/import/', views.ProviderBulkImportView.as_view(), name='provider_import'),
     path('providers/edit/', views.ProviderBulkEditView.as_view(), name='provider_bulk_edit'),
     path('providers/delete/', views.ProviderBulkDeleteView.as_view(), name='provider_bulk_delete'),
-    path('providers/<int:pk>/', views.ProviderView.as_view(), name='provider'),
-    path('providers/<int:pk>/edit/', views.ProviderEditView.as_view(), name='provider_edit'),
-    path('providers/<int:pk>/delete/', views.ProviderDeleteView.as_view(), name='provider_delete'),
     path('providers/<int:pk>/', include(get_model_urls('circuits', 'provider'))),
 
     # Provider networks
@@ -25,9 +20,6 @@ urlpatterns = [
     path('provider-networks/import/', views.ProviderNetworkBulkImportView.as_view(), name='providernetwork_import'),
     path('provider-networks/edit/', views.ProviderNetworkBulkEditView.as_view(), name='providernetwork_bulk_edit'),
     path('provider-networks/delete/', views.ProviderNetworkBulkDeleteView.as_view(), name='providernetwork_bulk_delete'),
-    path('provider-networks/<int:pk>/', views.ProviderNetworkView.as_view(), name='providernetwork'),
-    path('provider-networks/<int:pk>/edit/', views.ProviderNetworkEditView.as_view(), name='providernetwork_edit'),
-    path('provider-networks/<int:pk>/delete/', views.ProviderNetworkDeleteView.as_view(), name='providernetwork_delete'),
     path('provider-networks/<int:pk>/', include(get_model_urls('circuits', 'providernetwork'))),
 
     # Circuit types
@@ -36,9 +28,6 @@ urlpatterns = [
     path('circuit-types/import/', views.CircuitTypeBulkImportView.as_view(), name='circuittype_import'),
     path('circuit-types/edit/', views.CircuitTypeBulkEditView.as_view(), name='circuittype_bulk_edit'),
     path('circuit-types/delete/', views.CircuitTypeBulkDeleteView.as_view(), name='circuittype_bulk_delete'),
-    path('circuit-types/<int:pk>/', views.CircuitTypeView.as_view(), name='circuittype'),
-    path('circuit-types/<int:pk>/edit/', views.CircuitTypeEditView.as_view(), name='circuittype_edit'),
-    path('circuit-types/<int:pk>/delete/', views.CircuitTypeDeleteView.as_view(), name='circuittype_delete'),
     path('circuit-types/<int:pk>/', include(get_model_urls('circuits', 'circuittype'))),
 
     # Circuits
@@ -47,16 +36,11 @@ urlpatterns = [
     path('circuits/import/', views.CircuitBulkImportView.as_view(), name='circuit_import'),
     path('circuits/edit/', views.CircuitBulkEditView.as_view(), name='circuit_bulk_edit'),
     path('circuits/delete/', views.CircuitBulkDeleteView.as_view(), name='circuit_bulk_delete'),
-    path('circuits/<int:pk>/', views.CircuitView.as_view(), name='circuit'),
-    path('circuits/<int:pk>/edit/', views.CircuitEditView.as_view(), name='circuit_edit'),
-    path('circuits/<int:pk>/delete/', views.CircuitDeleteView.as_view(), name='circuit_delete'),
     path('circuits/<int:pk>/terminations/swap/', views.CircuitSwapTerminations.as_view(), name='circuit_terminations_swap'),
     path('circuits/<int:pk>/', include(get_model_urls('circuits', 'circuit'))),
 
     # Circuit terminations
     path('circuit-terminations/add/', views.CircuitTerminationEditView.as_view(), name='circuittermination_add'),
-    path('circuit-terminations/<int:pk>/edit/', views.CircuitTerminationEditView.as_view(), name='circuittermination_edit'),
-    path('circuit-terminations/<int:pk>/delete/', views.CircuitTerminationDeleteView.as_view(), name='circuittermination_delete'),
-    path('circuit-terminations/<int:pk>/trace/', PathTraceView.as_view(), name='circuittermination_trace', kwargs={'model': CircuitTermination}),
+    path('circuit-terminations/<int:pk>/', include(get_model_urls('circuits', 'circuittermination'))),
 
 ]

+ 20 - 0
netbox/circuits/views.py

@@ -3,9 +3,11 @@ from django.db import transaction
 from django.db.models import Q
 from django.shortcuts import get_object_or_404, redirect, render
 
+from dcim.views import PathTraceView
 from netbox.views import generic
 from utilities.forms import ConfirmationForm
 from utilities.utils import count_related
+from utilities.views import register_model_view
 from . import filtersets, forms, tables
 from .models import *
 
@@ -23,6 +25,7 @@ class ProviderListView(generic.ObjectListView):
     table = tables.ProviderTable
 
 
+@register_model_view(Provider)
 class ProviderView(generic.ObjectView):
     queryset = Provider.objects.all()
 
@@ -41,11 +44,13 @@ class ProviderView(generic.ObjectView):
         }
 
 
+@register_model_view(Provider, 'edit')
 class ProviderEditView(generic.ObjectEditView):
     queryset = Provider.objects.all()
     form = forms.ProviderForm
 
 
+@register_model_view(Provider, 'delete')
 class ProviderDeleteView(generic.ObjectDeleteView):
     queryset = Provider.objects.all()
 
@@ -84,6 +89,7 @@ class ProviderNetworkListView(generic.ObjectListView):
     table = tables.ProviderNetworkTable
 
 
+@register_model_view(ProviderNetwork)
 class ProviderNetworkView(generic.ObjectView):
     queryset = ProviderNetwork.objects.all()
 
@@ -103,11 +109,13 @@ class ProviderNetworkView(generic.ObjectView):
         }
 
 
+@register_model_view(ProviderNetwork, 'edit')
 class ProviderNetworkEditView(generic.ObjectEditView):
     queryset = ProviderNetwork.objects.all()
     form = forms.ProviderNetworkForm
 
 
+@register_model_view(ProviderNetwork, 'delete')
 class ProviderNetworkDeleteView(generic.ObjectDeleteView):
     queryset = ProviderNetwork.objects.all()
 
@@ -144,6 +152,7 @@ class CircuitTypeListView(generic.ObjectListView):
     table = tables.CircuitTypeTable
 
 
+@register_model_view(CircuitType)
 class CircuitTypeView(generic.ObjectView):
     queryset = CircuitType.objects.all()
 
@@ -157,11 +166,13 @@ class CircuitTypeView(generic.ObjectView):
         }
 
 
+@register_model_view(CircuitType, 'edit')
 class CircuitTypeEditView(generic.ObjectEditView):
     queryset = CircuitType.objects.all()
     form = forms.CircuitTypeForm
 
 
+@register_model_view(CircuitType, 'delete')
 class CircuitTypeDeleteView(generic.ObjectDeleteView):
     queryset = CircuitType.objects.all()
 
@@ -202,15 +213,18 @@ class CircuitListView(generic.ObjectListView):
     table = tables.CircuitTable
 
 
+@register_model_view(Circuit)
 class CircuitView(generic.ObjectView):
     queryset = Circuit.objects.all()
 
 
+@register_model_view(Circuit, 'edit')
 class CircuitEditView(generic.ObjectEditView):
     queryset = Circuit.objects.all()
     form = forms.CircuitForm
 
 
+@register_model_view(Circuit, 'delete')
 class CircuitDeleteView(generic.ObjectDeleteView):
     queryset = Circuit.objects.all()
 
@@ -318,11 +332,17 @@ class CircuitSwapTerminations(generic.ObjectEditView):
 # Circuit terminations
 #
 
+@register_model_view(CircuitTermination, 'edit')
 class CircuitTerminationEditView(generic.ObjectEditView):
     queryset = CircuitTermination.objects.all()
     form = forms.CircuitTerminationForm
     template_name = 'circuits/circuittermination_edit.html'
 
 
+@register_model_view(CircuitTermination, 'delete')
 class CircuitTerminationDeleteView(generic.ObjectDeleteView):
     queryset = CircuitTermination.objects.all()
+
+
+# Trace view
+register_model_view(CircuitTermination, 'trace', kwargs={'model': CircuitTermination})(PathTraceView)

+ 10 - 121
netbox/dcim/urls.py

@@ -2,9 +2,6 @@ from django.urls import include, path
 
 from utilities.urls import get_model_urls
 from . import views
-from .models import (
-    ConsolePort, ConsoleServerPort, FrontPort, Interface, PowerFeed, PowerPort, PowerOutlet, RearPort,
-)
 
 app_name = 'dcim'
 urlpatterns = [
@@ -15,9 +12,6 @@ urlpatterns = [
     path('regions/import/', views.RegionBulkImportView.as_view(), name='region_import'),
     path('regions/edit/', views.RegionBulkEditView.as_view(), name='region_bulk_edit'),
     path('regions/delete/', views.RegionBulkDeleteView.as_view(), name='region_bulk_delete'),
-    path('regions/<int:pk>/', views.RegionView.as_view(), name='region'),
-    path('regions/<int:pk>/edit/', views.RegionEditView.as_view(), name='region_edit'),
-    path('regions/<int:pk>/delete/', views.RegionDeleteView.as_view(), name='region_delete'),
     path('regions/<int:pk>/', include(get_model_urls('dcim', 'region'))),
 
     # Site groups
@@ -26,9 +20,6 @@ urlpatterns = [
     path('site-groups/import/', views.SiteGroupBulkImportView.as_view(), name='sitegroup_import'),
     path('site-groups/edit/', views.SiteGroupBulkEditView.as_view(), name='sitegroup_bulk_edit'),
     path('site-groups/delete/', views.SiteGroupBulkDeleteView.as_view(), name='sitegroup_bulk_delete'),
-    path('site-groups/<int:pk>/', views.SiteGroupView.as_view(), name='sitegroup'),
-    path('site-groups/<int:pk>/edit/', views.SiteGroupEditView.as_view(), name='sitegroup_edit'),
-    path('site-groups/<int:pk>/delete/', views.SiteGroupDeleteView.as_view(), name='sitegroup_delete'),
     path('site-groups/<int:pk>/', include(get_model_urls('dcim', 'sitegroup'))),
 
     # Sites
@@ -37,9 +28,6 @@ urlpatterns = [
     path('sites/import/', views.SiteBulkImportView.as_view(), name='site_import'),
     path('sites/edit/', views.SiteBulkEditView.as_view(), name='site_bulk_edit'),
     path('sites/delete/', views.SiteBulkDeleteView.as_view(), name='site_bulk_delete'),
-    path('sites/<int:pk>/', views.SiteView.as_view(), name='site'),
-    path('sites/<int:pk>/edit/', views.SiteEditView.as_view(), name='site_edit'),
-    path('sites/<int:pk>/delete/', views.SiteDeleteView.as_view(), name='site_delete'),
     path('sites/<int:pk>/', include(get_model_urls('dcim', 'site'))),
 
     # Locations
@@ -48,9 +36,6 @@ urlpatterns = [
     path('locations/import/', views.LocationBulkImportView.as_view(), name='location_import'),
     path('locations/edit/', views.LocationBulkEditView.as_view(), name='location_bulk_edit'),
     path('locations/delete/', views.LocationBulkDeleteView.as_view(), name='location_bulk_delete'),
-    path('locations/<int:pk>/', views.LocationView.as_view(), name='location'),
-    path('locations/<int:pk>/edit/', views.LocationEditView.as_view(), name='location_edit'),
-    path('locations/<int:pk>/delete/', views.LocationDeleteView.as_view(), name='location_delete'),
     path('locations/<int:pk>/', include(get_model_urls('dcim', 'location'))),
 
     # Rack roles
@@ -59,9 +44,6 @@ urlpatterns = [
     path('rack-roles/import/', views.RackRoleBulkImportView.as_view(), name='rackrole_import'),
     path('rack-roles/edit/', views.RackRoleBulkEditView.as_view(), name='rackrole_bulk_edit'),
     path('rack-roles/delete/', views.RackRoleBulkDeleteView.as_view(), name='rackrole_bulk_delete'),
-    path('rack-roles/<int:pk>/', views.RackRoleView.as_view(), name='rackrole'),
-    path('rack-roles/<int:pk>/edit/', views.RackRoleEditView.as_view(), name='rackrole_edit'),
-    path('rack-roles/<int:pk>/delete/', views.RackRoleDeleteView.as_view(), name='rackrole_delete'),
     path('rack-roles/<int:pk>/', include(get_model_urls('dcim', 'rackrole'))),
 
     # Rack reservations
@@ -70,9 +52,6 @@ urlpatterns = [
     path('rack-reservations/import/', views.RackReservationImportView.as_view(), name='rackreservation_import'),
     path('rack-reservations/edit/', views.RackReservationBulkEditView.as_view(), name='rackreservation_bulk_edit'),
     path('rack-reservations/delete/', views.RackReservationBulkDeleteView.as_view(), name='rackreservation_bulk_delete'),
-    path('rack-reservations/<int:pk>/', views.RackReservationView.as_view(), name='rackreservation'),
-    path('rack-reservations/<int:pk>/edit/', views.RackReservationEditView.as_view(), name='rackreservation_edit'),
-    path('rack-reservations/<int:pk>/delete/', views.RackReservationDeleteView.as_view(), name='rackreservation_delete'),
     path('rack-reservations/<int:pk>/', include(get_model_urls('dcim', 'rackreservation'))),
 
     # Racks
@@ -82,9 +61,6 @@ urlpatterns = [
     path('racks/import/', views.RackBulkImportView.as_view(), name='rack_import'),
     path('racks/edit/', views.RackBulkEditView.as_view(), name='rack_bulk_edit'),
     path('racks/delete/', views.RackBulkDeleteView.as_view(), name='rack_bulk_delete'),
-    path('racks/<int:pk>/', views.RackView.as_view(), name='rack'),
-    path('racks/<int:pk>/edit/', views.RackEditView.as_view(), name='rack_edit'),
-    path('racks/<int:pk>/delete/', views.RackDeleteView.as_view(), name='rack_delete'),
     path('racks/<int:pk>/', include(get_model_urls('dcim', 'rack'))),
 
     # Manufacturers
@@ -93,9 +69,6 @@ urlpatterns = [
     path('manufacturers/import/', views.ManufacturerBulkImportView.as_view(), name='manufacturer_import'),
     path('manufacturers/edit/', views.ManufacturerBulkEditView.as_view(), name='manufacturer_bulk_edit'),
     path('manufacturers/delete/', views.ManufacturerBulkDeleteView.as_view(), name='manufacturer_bulk_delete'),
-    path('manufacturers/<int:pk>/', views.ManufacturerView.as_view(), name='manufacturer'),
-    path('manufacturers/<int:pk>/edit/', views.ManufacturerEditView.as_view(), name='manufacturer_edit'),
-    path('manufacturers/<int:pk>/delete/', views.ManufacturerDeleteView.as_view(), name='manufacturer_delete'),
     path('manufacturers/<int:pk>/', include(get_model_urls('dcim', 'manufacturer'))),
 
     # Device types
@@ -104,9 +77,6 @@ urlpatterns = [
     path('device-types/import/', views.DeviceTypeImportView.as_view(), name='devicetype_import'),
     path('device-types/edit/', views.DeviceTypeBulkEditView.as_view(), name='devicetype_bulk_edit'),
     path('device-types/delete/', views.DeviceTypeBulkDeleteView.as_view(), name='devicetype_bulk_delete'),
-    path('device-types/<int:pk>/', views.DeviceTypeView.as_view(), name='devicetype'),
-    path('device-types/<int:pk>/edit/', views.DeviceTypeEditView.as_view(), name='devicetype_edit'),
-    path('device-types/<int:pk>/delete/', views.DeviceTypeDeleteView.as_view(), name='devicetype_delete'),
     path('device-types/<int:pk>/', include(get_model_urls('dcim', 'devicetype'))),
 
     # Module types
@@ -115,9 +85,6 @@ urlpatterns = [
     path('module-types/import/', views.ModuleTypeImportView.as_view(), name='moduletype_import'),
     path('module-types/edit/', views.ModuleTypeBulkEditView.as_view(), name='moduletype_bulk_edit'),
     path('module-types/delete/', views.ModuleTypeBulkDeleteView.as_view(), name='moduletype_bulk_delete'),
-    path('module-types/<int:pk>/', views.ModuleTypeView.as_view(), name='moduletype'),
-    path('module-types/<int:pk>/edit/', views.ModuleTypeEditView.as_view(), name='moduletype_edit'),
-    path('module-types/<int:pk>/delete/', views.ModuleTypeDeleteView.as_view(), name='moduletype_delete'),
     path('module-types/<int:pk>/', include(get_model_urls('dcim', 'moduletype'))),
 
     # Console port templates
@@ -125,80 +92,70 @@ urlpatterns = [
     path('console-port-templates/edit/', views.ConsolePortTemplateBulkEditView.as_view(), name='consoleporttemplate_bulk_edit'),
     path('console-port-templates/rename/', views.ConsolePortTemplateBulkRenameView.as_view(), name='consoleporttemplate_bulk_rename'),
     path('console-port-templates/delete/', views.ConsolePortTemplateBulkDeleteView.as_view(), name='consoleporttemplate_bulk_delete'),
-    path('console-port-templates/<int:pk>/edit/', views.ConsolePortTemplateEditView.as_view(), name='consoleporttemplate_edit'),
-    path('console-port-templates/<int:pk>/delete/', views.ConsolePortTemplateDeleteView.as_view(), name='consoleporttemplate_delete'),
+    path('console-port-templates/<int:pk>/', include(get_model_urls('dcim', 'consoleporttemplate'))),
 
     # Console server port templates
     path('console-server-port-templates/add/', views.ConsoleServerPortTemplateCreateView.as_view(), name='consoleserverporttemplate_add'),
     path('console-server-port-templates/edit/', views.ConsoleServerPortTemplateBulkEditView.as_view(), name='consoleserverporttemplate_bulk_edit'),
     path('console-server-port-templates/rename/', views.ConsoleServerPortTemplateBulkRenameView.as_view(), name='consoleserverporttemplate_bulk_rename'),
     path('console-server-port-templates/delete/', views.ConsoleServerPortTemplateBulkDeleteView.as_view(), name='consoleserverporttemplate_bulk_delete'),
-    path('console-server-port-templates/<int:pk>/edit/', views.ConsoleServerPortTemplateEditView.as_view(), name='consoleserverporttemplate_edit'),
-    path('console-server-port-templates/<int:pk>/delete/', views.ConsoleServerPortTemplateDeleteView.as_view(), name='consoleserverporttemplate_delete'),
+    path('console-server-port-templates/<int:pk>/', include(get_model_urls('dcim', 'consoleserverporttemplate'))),
 
     # Power port templates
     path('power-port-templates/add/', views.PowerPortTemplateCreateView.as_view(), name='powerporttemplate_add'),
     path('power-port-templates/edit/', views.PowerPortTemplateBulkEditView.as_view(), name='powerporttemplate_bulk_edit'),
     path('power-port-templates/rename/', views.PowerPortTemplateBulkRenameView.as_view(), name='powerporttemplate_bulk_rename'),
     path('power-port-templates/delete/', views.PowerPortTemplateBulkDeleteView.as_view(), name='powerporttemplate_bulk_delete'),
-    path('power-port-templates/<int:pk>/edit/', views.PowerPortTemplateEditView.as_view(), name='powerporttemplate_edit'),
-    path('power-port-templates/<int:pk>/delete/', views.PowerPortTemplateDeleteView.as_view(), name='powerporttemplate_delete'),
+    path('power-port-templates/<int:pk>/', include(get_model_urls('dcim', 'powerporttemplate'))),
 
     # Power outlet templates
     path('power-outlet-templates/add/', views.PowerOutletTemplateCreateView.as_view(), name='poweroutlettemplate_add'),
     path('power-outlet-templates/edit/', views.PowerOutletTemplateBulkEditView.as_view(), name='poweroutlettemplate_bulk_edit'),
     path('power-outlet-templates/rename/', views.PowerOutletTemplateBulkRenameView.as_view(), name='poweroutlettemplate_bulk_rename'),
     path('power-outlet-templates/delete/', views.PowerOutletTemplateBulkDeleteView.as_view(), name='poweroutlettemplate_bulk_delete'),
-    path('power-outlet-templates/<int:pk>/edit/', views.PowerOutletTemplateEditView.as_view(), name='poweroutlettemplate_edit'),
-    path('power-outlet-templates/<int:pk>/delete/', views.PowerOutletTemplateDeleteView.as_view(), name='poweroutlettemplate_delete'),
+    path('power-outlet-templates/<int:pk>/', include(get_model_urls('dcim', 'poweroutlettemplate'))),
 
     # Interface templates
     path('interface-templates/add/', views.InterfaceTemplateCreateView.as_view(), name='interfacetemplate_add'),
     path('interface-templates/edit/', views.InterfaceTemplateBulkEditView.as_view(), name='interfacetemplate_bulk_edit'),
     path('interface-templates/rename/', views.InterfaceTemplateBulkRenameView.as_view(), name='interfacetemplate_bulk_rename'),
     path('interface-templates/delete/', views.InterfaceTemplateBulkDeleteView.as_view(), name='interfacetemplate_bulk_delete'),
-    path('interface-templates/<int:pk>/edit/', views.InterfaceTemplateEditView.as_view(), name='interfacetemplate_edit'),
-    path('interface-templates/<int:pk>/delete/', views.InterfaceTemplateDeleteView.as_view(), name='interfacetemplate_delete'),
+    path('interface-templates/<int:pk>/', include(get_model_urls('dcim', 'interfacetemplate'))),
 
     # Front port templates
     path('front-port-templates/add/', views.FrontPortTemplateCreateView.as_view(), name='frontporttemplate_add'),
     path('front-port-templates/edit/', views.FrontPortTemplateBulkEditView.as_view(), name='frontporttemplate_bulk_edit'),
     path('front-port-templates/rename/', views.FrontPortTemplateBulkRenameView.as_view(), name='frontporttemplate_bulk_rename'),
     path('front-port-templates/delete/', views.FrontPortTemplateBulkDeleteView.as_view(), name='frontporttemplate_bulk_delete'),
-    path('front-port-templates/<int:pk>/edit/', views.FrontPortTemplateEditView.as_view(), name='frontporttemplate_edit'),
-    path('front-port-templates/<int:pk>/delete/', views.FrontPortTemplateDeleteView.as_view(), name='frontporttemplate_delete'),
+    path('front-port-templates/<int:pk>/', include(get_model_urls('dcim', 'frontporttemplate'))),
 
     # Rear port templates
     path('rear-port-templates/add/', views.RearPortTemplateCreateView.as_view(), name='rearporttemplate_add'),
     path('rear-port-templates/edit/', views.RearPortTemplateBulkEditView.as_view(), name='rearporttemplate_bulk_edit'),
     path('rear-port-templates/rename/', views.RearPortTemplateBulkRenameView.as_view(), name='rearporttemplate_bulk_rename'),
     path('rear-port-templates/delete/', views.RearPortTemplateBulkDeleteView.as_view(), name='rearporttemplate_bulk_delete'),
-    path('rear-port-templates/<int:pk>/edit/', views.RearPortTemplateEditView.as_view(), name='rearporttemplate_edit'),
-    path('rear-port-templates/<int:pk>/delete/', views.RearPortTemplateDeleteView.as_view(), name='rearporttemplate_delete'),
+    path('rear-port-templates/<int:pk>/', include(get_model_urls('dcim', 'rearporttemplate'))),
 
     # Device bay templates
     path('device-bay-templates/add/', views.DeviceBayTemplateCreateView.as_view(), name='devicebaytemplate_add'),
     path('device-bay-templates/edit/', views.DeviceBayTemplateBulkEditView.as_view(), name='devicebaytemplate_bulk_edit'),
     path('device-bay-templates/rename/', views.DeviceBayTemplateBulkRenameView.as_view(), name='devicebaytemplate_bulk_rename'),
     path('device-bay-templates/delete/', views.DeviceBayTemplateBulkDeleteView.as_view(), name='devicebaytemplate_bulk_delete'),
-    path('device-bay-templates/<int:pk>/edit/', views.DeviceBayTemplateEditView.as_view(), name='devicebaytemplate_edit'),
-    path('device-bay-templates/<int:pk>/delete/', views.DeviceBayTemplateDeleteView.as_view(), name='devicebaytemplate_delete'),
+    path('device-bay-templates/<int:pk>/', include(get_model_urls('dcim', 'devicebaytemplate'))),
 
     # Module bay templates
     path('module-bay-templates/add/', views.ModuleBayTemplateCreateView.as_view(), name='modulebaytemplate_add'),
     path('module-bay-templates/edit/', views.ModuleBayTemplateBulkEditView.as_view(), name='modulebaytemplate_bulk_edit'),
     path('module-bay-templates/rename/', views.ModuleBayTemplateBulkRenameView.as_view(), name='modulebaytemplate_bulk_rename'),
     path('module-bay-templates/delete/', views.ModuleBayTemplateBulkDeleteView.as_view(), name='modulebaytemplate_bulk_delete'),
-    path('module-bay-templates/<int:pk>/edit/', views.ModuleBayTemplateEditView.as_view(), name='modulebaytemplate_edit'),
-    path('module-bay-templates/<int:pk>/delete/', views.ModuleBayTemplateDeleteView.as_view(), name='modulebaytemplate_delete'),
+    path('module-bay-templates/<int:pk>/', include(get_model_urls('dcim', 'modulebaytemplate'))),
 
     # Inventory item templates
     path('inventory-item-templates/add/', views.InventoryItemTemplateCreateView.as_view(), name='inventoryitemtemplate_add'),
     path('inventory-item-templates/edit/', views.InventoryItemTemplateBulkEditView.as_view(), name='inventoryitemtemplate_bulk_edit'),
     path('inventory-item-templates/rename/', views.InventoryItemTemplateBulkRenameView.as_view(), name='inventoryitemtemplate_bulk_rename'),
     path('inventory-item-templates/delete/', views.InventoryItemTemplateBulkDeleteView.as_view(), name='inventoryitemtemplate_bulk_delete'),
-    path('inventory-item-templates/<int:pk>/edit/', views.InventoryItemTemplateEditView.as_view(), name='inventoryitemtemplate_edit'),
-    path('inventory-item-templates/<int:pk>/delete/', views.InventoryItemTemplateDeleteView.as_view(), name='inventoryitemtemplate_delete'),
+    path('inventory-item-templates/<int:pk>/', include(get_model_urls('dcim', 'inventoryitemtemplate'))),
 
     # Device roles
     path('device-roles/', views.DeviceRoleListView.as_view(), name='devicerole_list'),
@@ -206,9 +163,6 @@ urlpatterns = [
     path('device-roles/import/', views.DeviceRoleBulkImportView.as_view(), name='devicerole_import'),
     path('device-roles/edit/', views.DeviceRoleBulkEditView.as_view(), name='devicerole_bulk_edit'),
     path('device-roles/delete/', views.DeviceRoleBulkDeleteView.as_view(), name='devicerole_bulk_delete'),
-    path('device-roles/<int:pk>/', views.DeviceRoleView.as_view(), name='devicerole'),
-    path('device-roles/<int:pk>/edit/', views.DeviceRoleEditView.as_view(), name='devicerole_edit'),
-    path('device-roles/<int:pk>/delete/', views.DeviceRoleDeleteView.as_view(), name='devicerole_delete'),
     path('device-roles/<int:pk>/', include(get_model_urls('dcim', 'devicerole'))),
 
     # Platforms
@@ -217,9 +171,6 @@ urlpatterns = [
     path('platforms/import/', views.PlatformBulkImportView.as_view(), name='platform_import'),
     path('platforms/edit/', views.PlatformBulkEditView.as_view(), name='platform_bulk_edit'),
     path('platforms/delete/', views.PlatformBulkDeleteView.as_view(), name='platform_bulk_delete'),
-    path('platforms/<int:pk>/', views.PlatformView.as_view(), name='platform'),
-    path('platforms/<int:pk>/edit/', views.PlatformEditView.as_view(), name='platform_edit'),
-    path('platforms/<int:pk>/delete/', views.PlatformDeleteView.as_view(), name='platform_delete'),
     path('platforms/<int:pk>/', include(get_model_urls('dcim', 'platform'))),
 
     # Devices
@@ -230,9 +181,6 @@ urlpatterns = [
     path('devices/edit/', views.DeviceBulkEditView.as_view(), name='device_bulk_edit'),
     path('devices/rename/', views.DeviceBulkRenameView.as_view(), name='device_bulk_rename'),
     path('devices/delete/', views.DeviceBulkDeleteView.as_view(), name='device_bulk_delete'),
-    path('devices/<int:pk>/', views.DeviceView.as_view(), name='device'),
-    path('devices/<int:pk>/edit/', views.DeviceEditView.as_view(), name='device_edit'),
-    path('devices/<int:pk>/delete/', views.DeviceDeleteView.as_view(), name='device_delete'),
     path('devices/<int:pk>/', include(get_model_urls('dcim', 'device'))),
 
     # Modules
@@ -241,9 +189,6 @@ urlpatterns = [
     path('modules/import/', views.ModuleBulkImportView.as_view(), name='module_import'),
     path('modules/edit/', views.ModuleBulkEditView.as_view(), name='module_bulk_edit'),
     path('modules/delete/', views.ModuleBulkDeleteView.as_view(), name='module_bulk_delete'),
-    path('modules/<int:pk>/', views.ModuleView.as_view(), name='module'),
-    path('modules/<int:pk>/edit/', views.ModuleEditView.as_view(), name='module_edit'),
-    path('modules/<int:pk>/delete/', views.ModuleDeleteView.as_view(), name='module_delete'),
     path('modules/<int:pk>/', include(get_model_urls('dcim', 'module'))),
 
     # Console ports
@@ -254,10 +199,6 @@ urlpatterns = [
     path('console-ports/rename/', views.ConsolePortBulkRenameView.as_view(), name='consoleport_bulk_rename'),
     path('console-ports/disconnect/', views.ConsolePortBulkDisconnectView.as_view(), name='consoleport_bulk_disconnect'),
     path('console-ports/delete/', views.ConsolePortBulkDeleteView.as_view(), name='consoleport_bulk_delete'),
-    path('console-ports/<int:pk>/', views.ConsolePortView.as_view(), name='consoleport'),
-    path('console-ports/<int:pk>/edit/', views.ConsolePortEditView.as_view(), name='consoleport_edit'),
-    path('console-ports/<int:pk>/delete/', views.ConsolePortDeleteView.as_view(), name='consoleport_delete'),
-    path('console-ports/<int:pk>/trace/', views.PathTraceView.as_view(), name='consoleport_trace', kwargs={'model': ConsolePort}),
     path('console-ports/<int:pk>/', include(get_model_urls('dcim', 'consoleport'))),
     path('devices/console-ports/add/', views.DeviceBulkAddConsolePortView.as_view(), name='device_bulk_add_consoleport'),
 
@@ -269,10 +210,6 @@ urlpatterns = [
     path('console-server-ports/rename/', views.ConsoleServerPortBulkRenameView.as_view(), name='consoleserverport_bulk_rename'),
     path('console-server-ports/disconnect/', views.ConsoleServerPortBulkDisconnectView.as_view(), name='consoleserverport_bulk_disconnect'),
     path('console-server-ports/delete/', views.ConsoleServerPortBulkDeleteView.as_view(), name='consoleserverport_bulk_delete'),
-    path('console-server-ports/<int:pk>/', views.ConsoleServerPortView.as_view(), name='consoleserverport'),
-    path('console-server-ports/<int:pk>/edit/', views.ConsoleServerPortEditView.as_view(), name='consoleserverport_edit'),
-    path('console-server-ports/<int:pk>/delete/', views.ConsoleServerPortDeleteView.as_view(), name='consoleserverport_delete'),
-    path('console-server-ports/<int:pk>/trace/', views.PathTraceView.as_view(), name='consoleserverport_trace', kwargs={'model': ConsoleServerPort}),
     path('console-server-ports/<int:pk>/', include(get_model_urls('dcim', 'consoleserverport'))),
     path('devices/console-server-ports/add/', views.DeviceBulkAddConsoleServerPortView.as_view(), name='device_bulk_add_consoleserverport'),
 
@@ -284,10 +221,6 @@ urlpatterns = [
     path('power-ports/rename/', views.PowerPortBulkRenameView.as_view(), name='powerport_bulk_rename'),
     path('power-ports/disconnect/', views.PowerPortBulkDisconnectView.as_view(), name='powerport_bulk_disconnect'),
     path('power-ports/delete/', views.PowerPortBulkDeleteView.as_view(), name='powerport_bulk_delete'),
-    path('power-ports/<int:pk>/', views.PowerPortView.as_view(), name='powerport'),
-    path('power-ports/<int:pk>/edit/', views.PowerPortEditView.as_view(), name='powerport_edit'),
-    path('power-ports/<int:pk>/delete/', views.PowerPortDeleteView.as_view(), name='powerport_delete'),
-    path('power-ports/<int:pk>/trace/', views.PathTraceView.as_view(), name='powerport_trace', kwargs={'model': PowerPort}),
     path('power-ports/<int:pk>/', include(get_model_urls('dcim', 'powerport'))),
     path('devices/power-ports/add/', views.DeviceBulkAddPowerPortView.as_view(), name='device_bulk_add_powerport'),
 
@@ -299,10 +232,6 @@ urlpatterns = [
     path('power-outlets/rename/', views.PowerOutletBulkRenameView.as_view(), name='poweroutlet_bulk_rename'),
     path('power-outlets/disconnect/', views.PowerOutletBulkDisconnectView.as_view(), name='poweroutlet_bulk_disconnect'),
     path('power-outlets/delete/', views.PowerOutletBulkDeleteView.as_view(), name='poweroutlet_bulk_delete'),
-    path('power-outlets/<int:pk>/', views.PowerOutletView.as_view(), name='poweroutlet'),
-    path('power-outlets/<int:pk>/edit/', views.PowerOutletEditView.as_view(), name='poweroutlet_edit'),
-    path('power-outlets/<int:pk>/delete/', views.PowerOutletDeleteView.as_view(), name='poweroutlet_delete'),
-    path('power-outlets/<int:pk>/trace/', views.PathTraceView.as_view(), name='poweroutlet_trace', kwargs={'model': PowerOutlet}),
     path('power-outlets/<int:pk>/', include(get_model_urls('dcim', 'poweroutlet'))),
     path('devices/power-outlets/add/', views.DeviceBulkAddPowerOutletView.as_view(), name='device_bulk_add_poweroutlet'),
 
@@ -314,10 +243,6 @@ urlpatterns = [
     path('interfaces/rename/', views.InterfaceBulkRenameView.as_view(), name='interface_bulk_rename'),
     path('interfaces/disconnect/', views.InterfaceBulkDisconnectView.as_view(), name='interface_bulk_disconnect'),
     path('interfaces/delete/', views.InterfaceBulkDeleteView.as_view(), name='interface_bulk_delete'),
-    path('interfaces/<int:pk>/', views.InterfaceView.as_view(), name='interface'),
-    path('interfaces/<int:pk>/edit/', views.InterfaceEditView.as_view(), name='interface_edit'),
-    path('interfaces/<int:pk>/delete/', views.InterfaceDeleteView.as_view(), name='interface_delete'),
-    path('interfaces/<int:pk>/trace/', views.PathTraceView.as_view(), name='interface_trace', kwargs={'model': Interface}),
     path('interfaces/<int:pk>/', include(get_model_urls('dcim', 'interface'))),
     path('devices/interfaces/add/', views.DeviceBulkAddInterfaceView.as_view(), name='device_bulk_add_interface'),
 
@@ -329,10 +254,6 @@ urlpatterns = [
     path('front-ports/rename/', views.FrontPortBulkRenameView.as_view(), name='frontport_bulk_rename'),
     path('front-ports/disconnect/', views.FrontPortBulkDisconnectView.as_view(), name='frontport_bulk_disconnect'),
     path('front-ports/delete/', views.FrontPortBulkDeleteView.as_view(), name='frontport_bulk_delete'),
-    path('front-ports/<int:pk>/', views.FrontPortView.as_view(), name='frontport'),
-    path('front-ports/<int:pk>/edit/', views.FrontPortEditView.as_view(), name='frontport_edit'),
-    path('front-ports/<int:pk>/delete/', views.FrontPortDeleteView.as_view(), name='frontport_delete'),
-    path('front-ports/<int:pk>/trace/', views.PathTraceView.as_view(), name='frontport_trace', kwargs={'model': FrontPort}),
     path('front-ports/<int:pk>/', include(get_model_urls('dcim', 'frontport'))),
     # path('devices/front-ports/add/', views.DeviceBulkAddFrontPortView.as_view(), name='device_bulk_add_frontport'),
 
@@ -344,10 +265,6 @@ urlpatterns = [
     path('rear-ports/rename/', views.RearPortBulkRenameView.as_view(), name='rearport_bulk_rename'),
     path('rear-ports/disconnect/', views.RearPortBulkDisconnectView.as_view(), name='rearport_bulk_disconnect'),
     path('rear-ports/delete/', views.RearPortBulkDeleteView.as_view(), name='rearport_bulk_delete'),
-    path('rear-ports/<int:pk>/', views.RearPortView.as_view(), name='rearport'),
-    path('rear-ports/<int:pk>/edit/', views.RearPortEditView.as_view(), name='rearport_edit'),
-    path('rear-ports/<int:pk>/delete/', views.RearPortDeleteView.as_view(), name='rearport_delete'),
-    path('rear-ports/<int:pk>/trace/', views.PathTraceView.as_view(), name='rearport_trace', kwargs={'model': RearPort}),
     path('rear-ports/<int:pk>/', include(get_model_urls('dcim', 'rearport'))),
     path('devices/rear-ports/add/', views.DeviceBulkAddRearPortView.as_view(), name='device_bulk_add_rearport'),
 
@@ -358,9 +275,6 @@ urlpatterns = [
     path('module-bays/edit/', views.ModuleBayBulkEditView.as_view(), name='modulebay_bulk_edit'),
     path('module-bays/rename/', views.ModuleBayBulkRenameView.as_view(), name='modulebay_bulk_rename'),
     path('module-bays/delete/', views.ModuleBayBulkDeleteView.as_view(), name='modulebay_bulk_delete'),
-    path('module-bays/<int:pk>/', views.ModuleBayView.as_view(), name='modulebay'),
-    path('module-bays/<int:pk>/edit/', views.ModuleBayEditView.as_view(), name='modulebay_edit'),
-    path('module-bays/<int:pk>/delete/', views.ModuleBayDeleteView.as_view(), name='modulebay_delete'),
     path('module-bays/<int:pk>/', include(get_model_urls('dcim', 'modulebay'))),
     path('devices/module-bays/add/', views.DeviceBulkAddModuleBayView.as_view(), name='device_bulk_add_modulebay'),
 
@@ -371,11 +285,6 @@ urlpatterns = [
     path('device-bays/edit/', views.DeviceBayBulkEditView.as_view(), name='devicebay_bulk_edit'),
     path('device-bays/rename/', views.DeviceBayBulkRenameView.as_view(), name='devicebay_bulk_rename'),
     path('device-bays/delete/', views.DeviceBayBulkDeleteView.as_view(), name='devicebay_bulk_delete'),
-    path('device-bays/<int:pk>/', views.DeviceBayView.as_view(), name='devicebay'),
-    path('device-bays/<int:pk>/edit/', views.DeviceBayEditView.as_view(), name='devicebay_edit'),
-    path('device-bays/<int:pk>/delete/', views.DeviceBayDeleteView.as_view(), name='devicebay_delete'),
-    path('device-bays/<int:pk>/populate/', views.DeviceBayPopulateView.as_view(), name='devicebay_populate'),
-    path('device-bays/<int:pk>/depopulate/', views.DeviceBayDepopulateView.as_view(), name='devicebay_depopulate'),
     path('device-bays/<int:pk>/', include(get_model_urls('dcim', 'devicebay'))),
     path('devices/device-bays/add/', views.DeviceBulkAddDeviceBayView.as_view(), name='device_bulk_add_devicebay'),
 
@@ -386,9 +295,6 @@ urlpatterns = [
     path('inventory-items/edit/', views.InventoryItemBulkEditView.as_view(), name='inventoryitem_bulk_edit'),
     path('inventory-items/rename/', views.InventoryItemBulkRenameView.as_view(), name='inventoryitem_bulk_rename'),
     path('inventory-items/delete/', views.InventoryItemBulkDeleteView.as_view(), name='inventoryitem_bulk_delete'),
-    path('inventory-items/<int:pk>/', views.InventoryItemView.as_view(), name='inventoryitem'),
-    path('inventory-items/<int:pk>/edit/', views.InventoryItemEditView.as_view(), name='inventoryitem_edit'),
-    path('inventory-items/<int:pk>/delete/', views.InventoryItemDeleteView.as_view(), name='inventoryitem_delete'),
     path('inventory-items/<int:pk>/', include(get_model_urls('dcim', 'inventoryitem'))),
     path('devices/inventory-items/add/', views.DeviceBulkAddInventoryItemView.as_view(), name='device_bulk_add_inventoryitem'),
 
@@ -398,9 +304,6 @@ urlpatterns = [
     path('inventory-item-roles/import/', views.InventoryItemRoleBulkImportView.as_view(), name='inventoryitemrole_import'),
     path('inventory-item-roles/edit/', views.InventoryItemRoleBulkEditView.as_view(), name='inventoryitemrole_bulk_edit'),
     path('inventory-item-roles/delete/', views.InventoryItemRoleBulkDeleteView.as_view(), name='inventoryitemrole_bulk_delete'),
-    path('inventory-item-roles/<int:pk>/', views.InventoryItemRoleView.as_view(), name='inventoryitemrole'),
-    path('inventory-item-roles/<int:pk>/edit/', views.InventoryItemRoleEditView.as_view(), name='inventoryitemrole_edit'),
-    path('inventory-item-roles/<int:pk>/delete/', views.InventoryItemRoleDeleteView.as_view(), name='inventoryitemrole_delete'),
     path('inventory-item-roles/<int:pk>/', include(get_model_urls('dcim', 'inventoryitemrole'))),
 
     # Cables
@@ -409,9 +312,6 @@ urlpatterns = [
     path('cables/import/', views.CableBulkImportView.as_view(), name='cable_import'),
     path('cables/edit/', views.CableBulkEditView.as_view(), name='cable_bulk_edit'),
     path('cables/delete/', views.CableBulkDeleteView.as_view(), name='cable_bulk_delete'),
-    path('cables/<int:pk>/', views.CableView.as_view(), name='cable'),
-    path('cables/<int:pk>/edit/', views.CableEditView.as_view(), name='cable_edit'),
-    path('cables/<int:pk>/delete/', views.CableDeleteView.as_view(), name='cable_delete'),
     path('cables/<int:pk>/', include(get_model_urls('dcim', 'cable'))),
 
     # Console/power/interface connections (read-only)
@@ -425,10 +325,6 @@ urlpatterns = [
     path('virtual-chassis/import/', views.VirtualChassisBulkImportView.as_view(), name='virtualchassis_import'),
     path('virtual-chassis/edit/', views.VirtualChassisBulkEditView.as_view(), name='virtualchassis_bulk_edit'),
     path('virtual-chassis/delete/', views.VirtualChassisBulkDeleteView.as_view(), name='virtualchassis_bulk_delete'),
-    path('virtual-chassis/<int:pk>/', views.VirtualChassisView.as_view(), name='virtualchassis'),
-    path('virtual-chassis/<int:pk>/edit/', views.VirtualChassisEditView.as_view(), name='virtualchassis_edit'),
-    path('virtual-chassis/<int:pk>/delete/', views.VirtualChassisDeleteView.as_view(), name='virtualchassis_delete'),
-    path('virtual-chassis/<int:pk>/add-member/', views.VirtualChassisAddMemberView.as_view(), name='virtualchassis_add_member'),
     path('virtual-chassis/<int:pk>/', include(get_model_urls('dcim', 'virtualchassis'))),
     path('virtual-chassis-members/<int:pk>/delete/', views.VirtualChassisRemoveMemberView.as_view(), name='virtualchassis_remove_member'),
 
@@ -438,9 +334,6 @@ urlpatterns = [
     path('power-panels/import/', views.PowerPanelBulkImportView.as_view(), name='powerpanel_import'),
     path('power-panels/edit/', views.PowerPanelBulkEditView.as_view(), name='powerpanel_bulk_edit'),
     path('power-panels/delete/', views.PowerPanelBulkDeleteView.as_view(), name='powerpanel_bulk_delete'),
-    path('power-panels/<int:pk>/', views.PowerPanelView.as_view(), name='powerpanel'),
-    path('power-panels/<int:pk>/edit/', views.PowerPanelEditView.as_view(), name='powerpanel_edit'),
-    path('power-panels/<int:pk>/delete/', views.PowerPanelDeleteView.as_view(), name='powerpanel_delete'),
     path('power-panels/<int:pk>/', include(get_model_urls('dcim', 'powerpanel'))),
 
     # Power feeds
@@ -450,10 +343,6 @@ urlpatterns = [
     path('power-feeds/edit/', views.PowerFeedBulkEditView.as_view(), name='powerfeed_bulk_edit'),
     path('power-feeds/disconnect/', views.PowerFeedBulkDisconnectView.as_view(), name='powerfeed_bulk_disconnect'),
     path('power-feeds/delete/', views.PowerFeedBulkDeleteView.as_view(), name='powerfeed_bulk_delete'),
-    path('power-feeds/<int:pk>/', views.PowerFeedView.as_view(), name='powerfeed'),
-    path('power-feeds/<int:pk>/edit/', views.PowerFeedEditView.as_view(), name='powerfeed_edit'),
-    path('power-feeds/<int:pk>/delete/', views.PowerFeedDeleteView.as_view(), name='powerfeed_delete'),
-    path('power-feeds/<int:pk>/trace/', views.PathTraceView.as_view(), name='powerfeed_trace', kwargs={'model': PowerFeed}),
     path('power-feeds/<int:pk>/', include(get_model_urls('dcim', 'powerfeed'))),
 
 ]

File diff ditekan karena terlalu besar
+ 215 - 83
netbox/dcim/views.py


+ 2 - 24
netbox/extras/urls.py

@@ -13,9 +13,6 @@ urlpatterns = [
     path('custom-fields/import/', views.CustomFieldBulkImportView.as_view(), name='customfield_import'),
     path('custom-fields/edit/', views.CustomFieldBulkEditView.as_view(), name='customfield_bulk_edit'),
     path('custom-fields/delete/', views.CustomFieldBulkDeleteView.as_view(), name='customfield_bulk_delete'),
-    path('custom-fields/<int:pk>/', views.CustomFieldView.as_view(), name='customfield'),
-    path('custom-fields/<int:pk>/edit/', views.CustomFieldEditView.as_view(), name='customfield_edit'),
-    path('custom-fields/<int:pk>/delete/', views.CustomFieldDeleteView.as_view(), name='customfield_delete'),
     path('custom-fields/<int:pk>/', include(get_model_urls('extras', 'customfield'))),
 
     # Custom links
@@ -24,9 +21,6 @@ urlpatterns = [
     path('custom-links/import/', views.CustomLinkBulkImportView.as_view(), name='customlink_import'),
     path('custom-links/edit/', views.CustomLinkBulkEditView.as_view(), name='customlink_bulk_edit'),
     path('custom-links/delete/', views.CustomLinkBulkDeleteView.as_view(), name='customlink_bulk_delete'),
-    path('custom-links/<int:pk>/', views.CustomLinkView.as_view(), name='customlink'),
-    path('custom-links/<int:pk>/edit/', views.CustomLinkEditView.as_view(), name='customlink_edit'),
-    path('custom-links/<int:pk>/delete/', views.CustomLinkDeleteView.as_view(), name='customlink_delete'),
     path('custom-links/<int:pk>/', include(get_model_urls('extras', 'customlink'))),
 
     # Export templates
@@ -35,9 +29,6 @@ urlpatterns = [
     path('export-templates/import/', views.ExportTemplateBulkImportView.as_view(), name='exporttemplate_import'),
     path('export-templates/edit/', views.ExportTemplateBulkEditView.as_view(), name='exporttemplate_bulk_edit'),
     path('export-templates/delete/', views.ExportTemplateBulkDeleteView.as_view(), name='exporttemplate_bulk_delete'),
-    path('export-templates/<int:pk>/', views.ExportTemplateView.as_view(), name='exporttemplate'),
-    path('export-templates/<int:pk>/edit/', views.ExportTemplateEditView.as_view(), name='exporttemplate_edit'),
-    path('export-templates/<int:pk>/delete/', views.ExportTemplateDeleteView.as_view(), name='exporttemplate_delete'),
     path('export-templates/<int:pk>/', include(get_model_urls('extras', 'exporttemplate'))),
 
     # Webhooks
@@ -46,9 +37,6 @@ urlpatterns = [
     path('webhooks/import/', views.WebhookBulkImportView.as_view(), name='webhook_import'),
     path('webhooks/edit/', views.WebhookBulkEditView.as_view(), name='webhook_bulk_edit'),
     path('webhooks/delete/', views.WebhookBulkDeleteView.as_view(), name='webhook_bulk_delete'),
-    path('webhooks/<int:pk>/', views.WebhookView.as_view(), name='webhook'),
-    path('webhooks/<int:pk>/edit/', views.WebhookEditView.as_view(), name='webhook_edit'),
-    path('webhooks/<int:pk>/delete/', views.WebhookDeleteView.as_view(), name='webhook_delete'),
     path('webhooks/<int:pk>/', include(get_model_urls('extras', 'webhook'))),
 
     # Tags
@@ -57,9 +45,6 @@ urlpatterns = [
     path('tags/import/', views.TagBulkImportView.as_view(), name='tag_import'),
     path('tags/edit/', views.TagBulkEditView.as_view(), name='tag_bulk_edit'),
     path('tags/delete/', views.TagBulkDeleteView.as_view(), name='tag_bulk_delete'),
-    path('tags/<int:pk>/', views.TagView.as_view(), name='tag'),
-    path('tags/<int:pk>/edit/', views.TagEditView.as_view(), name='tag_edit'),
-    path('tags/<int:pk>/delete/', views.TagDeleteView.as_view(), name='tag_delete'),
     path('tags/<int:pk>/', include(get_model_urls('extras', 'tag'))),
 
     # Config contexts
@@ -67,29 +52,22 @@ urlpatterns = [
     path('config-contexts/add/', views.ConfigContextEditView.as_view(), name='configcontext_add'),
     path('config-contexts/edit/', views.ConfigContextBulkEditView.as_view(), name='configcontext_bulk_edit'),
     path('config-contexts/delete/', views.ConfigContextBulkDeleteView.as_view(), name='configcontext_bulk_delete'),
-    path('config-contexts/<int:pk>/', views.ConfigContextView.as_view(), name='configcontext'),
-    path('config-contexts/<int:pk>/edit/', views.ConfigContextEditView.as_view(), name='configcontext_edit'),
-    path('config-contexts/<int:pk>/delete/', views.ConfigContextDeleteView.as_view(), name='configcontext_delete'),
     path('config-contexts/<int:pk>/', include(get_model_urls('extras', 'configcontext'))),
 
     # Image attachments
     path('image-attachments/add/', views.ImageAttachmentEditView.as_view(), name='imageattachment_add'),
-    path('image-attachments/<int:pk>/edit/', views.ImageAttachmentEditView.as_view(), name='imageattachment_edit'),
-    path('image-attachments/<int:pk>/delete/', views.ImageAttachmentDeleteView.as_view(), name='imageattachment_delete'),
+    path('image-attachments/<int:pk>/', include(get_model_urls('extras', 'imageattachment'))),
 
     # Journal entries
     path('journal-entries/', views.JournalEntryListView.as_view(), name='journalentry_list'),
     path('journal-entries/add/', views.JournalEntryEditView.as_view(), name='journalentry_add'),
     path('journal-entries/edit/', views.JournalEntryBulkEditView.as_view(), name='journalentry_bulk_edit'),
     path('journal-entries/delete/', views.JournalEntryBulkDeleteView.as_view(), name='journalentry_bulk_delete'),
-    path('journal-entries/<int:pk>/', views.JournalEntryView.as_view(), name='journalentry'),
-    path('journal-entries/<int:pk>/edit/', views.JournalEntryEditView.as_view(), name='journalentry_edit'),
-    path('journal-entries/<int:pk>/delete/', views.JournalEntryDeleteView.as_view(), name='journalentry_delete'),
     path('journal-entries/<int:pk>/', include(get_model_urls('extras', 'journalentry'))),
 
     # Change logging
     path('changelog/', views.ObjectChangeListView.as_view(), name='objectchange_list'),
-    path('changelog/<int:pk>/', views.ObjectChangeView.as_view(), name='objectchange'),
+    path('changelog/<int:pk>/', include(get_model_urls('extras', 'objectchange'))),
 
     # Reports
     path('reports/', views.ReportListView.as_view(), name='report_list'),

+ 25 - 1
netbox/extras/views.py

@@ -12,7 +12,7 @@ from netbox.views import generic
 from utilities.forms import ConfirmationForm
 from utilities.htmx import is_htmx
 from utilities.utils import copy_safe_request, count_related, get_viewname, normalize_querydict, shallow_compare_dict
-from utilities.views import ContentTypePermissionRequiredMixin
+from utilities.views import ContentTypePermissionRequiredMixin, register_model_view
 from . import filtersets, forms, tables
 from .choices import JobResultStatusChoices
 from .models import *
@@ -31,15 +31,18 @@ class CustomFieldListView(generic.ObjectListView):
     table = tables.CustomFieldTable
 
 
+@register_model_view(CustomField)
 class CustomFieldView(generic.ObjectView):
     queryset = CustomField.objects.all()
 
 
+@register_model_view(CustomField, 'edit')
 class CustomFieldEditView(generic.ObjectEditView):
     queryset = CustomField.objects.all()
     form = forms.CustomFieldForm
 
 
+@register_model_view(CustomField, 'delete')
 class CustomFieldDeleteView(generic.ObjectDeleteView):
     queryset = CustomField.objects.all()
 
@@ -74,15 +77,18 @@ class CustomLinkListView(generic.ObjectListView):
     table = tables.CustomLinkTable
 
 
+@register_model_view(CustomLink)
 class CustomLinkView(generic.ObjectView):
     queryset = CustomLink.objects.all()
 
 
+@register_model_view(CustomLink, 'edit')
 class CustomLinkEditView(generic.ObjectEditView):
     queryset = CustomLink.objects.all()
     form = forms.CustomLinkForm
 
 
+@register_model_view(CustomLink, 'delete')
 class CustomLinkDeleteView(generic.ObjectDeleteView):
     queryset = CustomLink.objects.all()
 
@@ -117,15 +123,18 @@ class ExportTemplateListView(generic.ObjectListView):
     table = tables.ExportTemplateTable
 
 
+@register_model_view(ExportTemplate)
 class ExportTemplateView(generic.ObjectView):
     queryset = ExportTemplate.objects.all()
 
 
+@register_model_view(ExportTemplate, 'edit')
 class ExportTemplateEditView(generic.ObjectEditView):
     queryset = ExportTemplate.objects.all()
     form = forms.ExportTemplateForm
 
 
+@register_model_view(ExportTemplate, 'delete')
 class ExportTemplateDeleteView(generic.ObjectDeleteView):
     queryset = ExportTemplate.objects.all()
 
@@ -160,15 +169,18 @@ class WebhookListView(generic.ObjectListView):
     table = tables.WebhookTable
 
 
+@register_model_view(Webhook)
 class WebhookView(generic.ObjectView):
     queryset = Webhook.objects.all()
 
 
+@register_model_view(Webhook, 'edit')
 class WebhookEditView(generic.ObjectEditView):
     queryset = Webhook.objects.all()
     form = forms.WebhookForm
 
 
+@register_model_view(Webhook, 'delete')
 class WebhookDeleteView(generic.ObjectDeleteView):
     queryset = Webhook.objects.all()
 
@@ -205,6 +217,7 @@ class TagListView(generic.ObjectListView):
     table = tables.TagTable
 
 
+@register_model_view(Tag)
 class TagView(generic.ObjectView):
     queryset = Tag.objects.all()
 
@@ -230,11 +243,13 @@ class TagView(generic.ObjectView):
         }
 
 
+@register_model_view(Tag, 'edit')
 class TagEditView(generic.ObjectEditView):
     queryset = Tag.objects.all()
     form = forms.TagForm
 
 
+@register_model_view(Tag, 'delete')
 class TagDeleteView(generic.ObjectDeleteView):
     queryset = Tag.objects.all()
 
@@ -272,6 +287,7 @@ class ConfigContextListView(generic.ObjectListView):
     actions = ('add', 'bulk_edit', 'bulk_delete')
 
 
+@register_model_view(ConfigContext)
 class ConfigContextView(generic.ObjectView):
     queryset = ConfigContext.objects.all()
 
@@ -309,6 +325,7 @@ class ConfigContextView(generic.ObjectView):
         }
 
 
+@register_model_view(ConfigContext, 'edit')
 class ConfigContextEditView(generic.ObjectEditView):
     queryset = ConfigContext.objects.all()
     form = forms.ConfigContextForm
@@ -321,6 +338,7 @@ class ConfigContextBulkEditView(generic.BulkEditView):
     form = forms.ConfigContextBulkEditForm
 
 
+@register_model_view(ConfigContext, 'delete')
 class ConfigContextDeleteView(generic.ObjectDeleteView):
     queryset = ConfigContext.objects.all()
 
@@ -368,6 +386,7 @@ class ObjectChangeListView(generic.ObjectListView):
     actions = ('export',)
 
 
+@register_model_view(ObjectChange)
 class ObjectChangeView(generic.ObjectView):
     queryset = ObjectChange.objects.all()
 
@@ -425,6 +444,7 @@ class ObjectChangeView(generic.ObjectView):
 # Image attachments
 #
 
+@register_model_view(ImageAttachment, 'edit')
 class ImageAttachmentEditView(generic.ObjectEditView):
     queryset = ImageAttachment.objects.all()
     form = forms.ImageAttachmentForm
@@ -447,6 +467,7 @@ class ImageAttachmentEditView(generic.ObjectEditView):
         }
 
 
+@register_model_view(ImageAttachment, 'delete')
 class ImageAttachmentDeleteView(generic.ObjectDeleteView):
     queryset = ImageAttachment.objects.all()
 
@@ -466,10 +487,12 @@ class JournalEntryListView(generic.ObjectListView):
     actions = ('export', 'bulk_edit', 'bulk_delete')
 
 
+@register_model_view(JournalEntry)
 class JournalEntryView(generic.ObjectView):
     queryset = JournalEntry.objects.all()
 
 
+@register_model_view(JournalEntry, 'edit')
 class JournalEntryEditView(generic.ObjectEditView):
     queryset = JournalEntry.objects.all()
     form = forms.JournalEntryForm
@@ -487,6 +510,7 @@ class JournalEntryEditView(generic.ObjectEditView):
         return reverse(viewname, kwargs={'pk': obj.pk})
 
 
+@register_model_view(JournalEntry, 'delete')
 class JournalEntryDeleteView(generic.ObjectDeleteView):
     queryset = JournalEntry.objects.all()
 

+ 1 - 50
netbox/ipam/urls.py

@@ -12,9 +12,6 @@ urlpatterns = [
     path('asns/import/', views.ASNBulkImportView.as_view(), name='asn_import'),
     path('asns/edit/', views.ASNBulkEditView.as_view(), name='asn_bulk_edit'),
     path('asns/delete/', views.ASNBulkDeleteView.as_view(), name='asn_bulk_delete'),
-    path('asns/<int:pk>/', views.ASNView.as_view(), name='asn'),
-    path('asns/<int:pk>/edit/', views.ASNEditView.as_view(), name='asn_edit'),
-    path('asns/<int:pk>/delete/', views.ASNDeleteView.as_view(), name='asn_delete'),
     path('asns/<int:pk>/', include(get_model_urls('ipam', 'asn'))),
 
     # VRFs
@@ -23,9 +20,6 @@ urlpatterns = [
     path('vrfs/import/', views.VRFBulkImportView.as_view(), name='vrf_import'),
     path('vrfs/edit/', views.VRFBulkEditView.as_view(), name='vrf_bulk_edit'),
     path('vrfs/delete/', views.VRFBulkDeleteView.as_view(), name='vrf_bulk_delete'),
-    path('vrfs/<int:pk>/', views.VRFView.as_view(), name='vrf'),
-    path('vrfs/<int:pk>/edit/', views.VRFEditView.as_view(), name='vrf_edit'),
-    path('vrfs/<int:pk>/delete/', views.VRFDeleteView.as_view(), name='vrf_delete'),
     path('vrfs/<int:pk>/', include(get_model_urls('ipam', 'vrf'))),
 
     # Route targets
@@ -34,9 +28,6 @@ urlpatterns = [
     path('route-targets/import/', views.RouteTargetBulkImportView.as_view(), name='routetarget_import'),
     path('route-targets/edit/', views.RouteTargetBulkEditView.as_view(), name='routetarget_bulk_edit'),
     path('route-targets/delete/', views.RouteTargetBulkDeleteView.as_view(), name='routetarget_bulk_delete'),
-    path('route-targets/<int:pk>/', views.RouteTargetView.as_view(), name='routetarget'),
-    path('route-targets/<int:pk>/edit/', views.RouteTargetEditView.as_view(), name='routetarget_edit'),
-    path('route-targets/<int:pk>/delete/', views.RouteTargetDeleteView.as_view(), name='routetarget_delete'),
     path('route-targets/<int:pk>/', include(get_model_urls('ipam', 'routetarget'))),
 
     # RIRs
@@ -45,9 +36,6 @@ urlpatterns = [
     path('rirs/import/', views.RIRBulkImportView.as_view(), name='rir_import'),
     path('rirs/edit/', views.RIRBulkEditView.as_view(), name='rir_bulk_edit'),
     path('rirs/delete/', views.RIRBulkDeleteView.as_view(), name='rir_bulk_delete'),
-    path('rirs/<int:pk>/', views.RIRView.as_view(), name='rir'),
-    path('rirs/<int:pk>/edit/', views.RIREditView.as_view(), name='rir_edit'),
-    path('rirs/<int:pk>/delete/', views.RIRDeleteView.as_view(), name='rir_delete'),
     path('rirs/<int:pk>/', include(get_model_urls('ipam', 'rir'))),
 
     # Aggregates
@@ -56,9 +44,6 @@ urlpatterns = [
     path('aggregates/import/', views.AggregateBulkImportView.as_view(), name='aggregate_import'),
     path('aggregates/edit/', views.AggregateBulkEditView.as_view(), name='aggregate_bulk_edit'),
     path('aggregates/delete/', views.AggregateBulkDeleteView.as_view(), name='aggregate_bulk_delete'),
-    path('aggregates/<int:pk>/', views.AggregateView.as_view(), name='aggregate'),
-    path('aggregates/<int:pk>/edit/', views.AggregateEditView.as_view(), name='aggregate_edit'),
-    path('aggregates/<int:pk>/delete/', views.AggregateDeleteView.as_view(), name='aggregate_delete'),
     path('aggregates/<int:pk>/', include(get_model_urls('ipam', 'aggregate'))),
 
     # Roles
@@ -67,9 +52,6 @@ urlpatterns = [
     path('roles/import/', views.RoleBulkImportView.as_view(), name='role_import'),
     path('roles/edit/', views.RoleBulkEditView.as_view(), name='role_bulk_edit'),
     path('roles/delete/', views.RoleBulkDeleteView.as_view(), name='role_bulk_delete'),
-    path('roles/<int:pk>/', views.RoleView.as_view(), name='role'),
-    path('roles/<int:pk>/edit/', views.RoleEditView.as_view(), name='role_edit'),
-    path('roles/<int:pk>/delete/', views.RoleDeleteView.as_view(), name='role_delete'),
     path('roles/<int:pk>/', include(get_model_urls('ipam', 'role'))),
 
     # Prefixes
@@ -78,9 +60,6 @@ urlpatterns = [
     path('prefixes/import/', views.PrefixBulkImportView.as_view(), name='prefix_import'),
     path('prefixes/edit/', views.PrefixBulkEditView.as_view(), name='prefix_bulk_edit'),
     path('prefixes/delete/', views.PrefixBulkDeleteView.as_view(), name='prefix_bulk_delete'),
-    path('prefixes/<int:pk>/', views.PrefixView.as_view(), name='prefix'),
-    path('prefixes/<int:pk>/edit/', views.PrefixEditView.as_view(), name='prefix_edit'),
-    path('prefixes/<int:pk>/delete/', views.PrefixDeleteView.as_view(), name='prefix_delete'),
     path('prefixes/<int:pk>/', include(get_model_urls('ipam', 'prefix'))),
 
     # IP ranges
@@ -89,9 +68,6 @@ urlpatterns = [
     path('ip-ranges/import/', views.IPRangeBulkImportView.as_view(), name='iprange_import'),
     path('ip-ranges/edit/', views.IPRangeBulkEditView.as_view(), name='iprange_bulk_edit'),
     path('ip-ranges/delete/', views.IPRangeBulkDeleteView.as_view(), name='iprange_bulk_delete'),
-    path('ip-ranges/<int:pk>/', views.IPRangeView.as_view(), name='iprange'),
-    path('ip-ranges/<int:pk>/edit/', views.IPRangeEditView.as_view(), name='iprange_edit'),
-    path('ip-ranges/<int:pk>/delete/', views.IPRangeDeleteView.as_view(), name='iprange_delete'),
     path('ip-ranges/<int:pk>/', include(get_model_urls('ipam', 'iprange'))),
 
     # IP addresses
@@ -102,9 +78,6 @@ urlpatterns = [
     path('ip-addresses/edit/', views.IPAddressBulkEditView.as_view(), name='ipaddress_bulk_edit'),
     path('ip-addresses/delete/', views.IPAddressBulkDeleteView.as_view(), name='ipaddress_bulk_delete'),
     path('ip-addresses/assign/', views.IPAddressAssignView.as_view(), name='ipaddress_assign'),
-    path('ip-addresses/<int:pk>/', views.IPAddressView.as_view(), name='ipaddress'),
-    path('ip-addresses/<int:pk>/edit/', views.IPAddressEditView.as_view(), name='ipaddress_edit'),
-    path('ip-addresses/<int:pk>/delete/', views.IPAddressDeleteView.as_view(), name='ipaddress_delete'),
     path('ip-addresses/<int:pk>/', include(get_model_urls('ipam', 'ipaddress'))),
 
     # FHRP groups
@@ -113,15 +86,11 @@ urlpatterns = [
     path('fhrp-groups/import/', views.FHRPGroupBulkImportView.as_view(), name='fhrpgroup_import'),
     path('fhrp-groups/edit/', views.FHRPGroupBulkEditView.as_view(), name='fhrpgroup_bulk_edit'),
     path('fhrp-groups/delete/', views.FHRPGroupBulkDeleteView.as_view(), name='fhrpgroup_bulk_delete'),
-    path('fhrp-groups/<int:pk>/', views.FHRPGroupView.as_view(), name='fhrpgroup'),
-    path('fhrp-groups/<int:pk>/edit/', views.FHRPGroupEditView.as_view(), name='fhrpgroup_edit'),
-    path('fhrp-groups/<int:pk>/delete/', views.FHRPGroupDeleteView.as_view(), name='fhrpgroup_delete'),
     path('fhrp-groups/<int:pk>/', include(get_model_urls('ipam', 'fhrpgroup'))),
 
     # FHRP group assignments
     path('fhrp-group-assignments/add/', views.FHRPGroupAssignmentEditView.as_view(), name='fhrpgroupassignment_add'),
-    path('fhrp-group-assignments/<int:pk>/edit/', views.FHRPGroupAssignmentEditView.as_view(), name='fhrpgroupassignment_edit'),
-    path('fhrp-group-assignments/<int:pk>/delete/', views.FHRPGroupAssignmentDeleteView.as_view(), name='fhrpgroupassignment_delete'),
+    path('fhrp-group-assignments/<int:pk>/', include(get_model_urls('ipam', 'fhrpgroupassignment'))),
 
     # VLAN groups
     path('vlan-groups/', views.VLANGroupListView.as_view(), name='vlangroup_list'),
@@ -129,9 +98,6 @@ urlpatterns = [
     path('vlan-groups/import/', views.VLANGroupBulkImportView.as_view(), name='vlangroup_import'),
     path('vlan-groups/edit/', views.VLANGroupBulkEditView.as_view(), name='vlangroup_bulk_edit'),
     path('vlan-groups/delete/', views.VLANGroupBulkDeleteView.as_view(), name='vlangroup_bulk_delete'),
-    path('vlan-groups/<int:pk>/', views.VLANGroupView.as_view(), name='vlangroup'),
-    path('vlan-groups/<int:pk>/edit/', views.VLANGroupEditView.as_view(), name='vlangroup_edit'),
-    path('vlan-groups/<int:pk>/delete/', views.VLANGroupDeleteView.as_view(), name='vlangroup_delete'),
     path('vlan-groups/<int:pk>/', include(get_model_urls('ipam', 'vlangroup'))),
 
     # VLANs
@@ -140,9 +106,6 @@ urlpatterns = [
     path('vlans/import/', views.VLANBulkImportView.as_view(), name='vlan_import'),
     path('vlans/edit/', views.VLANBulkEditView.as_view(), name='vlan_bulk_edit'),
     path('vlans/delete/', views.VLANBulkDeleteView.as_view(), name='vlan_bulk_delete'),
-    path('vlans/<int:pk>/', views.VLANView.as_view(), name='vlan'),
-    path('vlans/<int:pk>/edit/', views.VLANEditView.as_view(), name='vlan_edit'),
-    path('vlans/<int:pk>/delete/', views.VLANDeleteView.as_view(), name='vlan_delete'),
     path('vlans/<int:pk>/', include(get_model_urls('ipam', 'vlan'))),
 
     # Service templates
@@ -151,9 +114,6 @@ urlpatterns = [
     path('service-templates/import/', views.ServiceTemplateBulkImportView.as_view(), name='servicetemplate_import'),
     path('service-templates/edit/', views.ServiceTemplateBulkEditView.as_view(), name='servicetemplate_bulk_edit'),
     path('service-templates/delete/', views.ServiceTemplateBulkDeleteView.as_view(), name='servicetemplate_bulk_delete'),
-    path('service-templates/<int:pk>/', views.ServiceTemplateView.as_view(), name='servicetemplate'),
-    path('service-templates/<int:pk>/edit/', views.ServiceTemplateEditView.as_view(), name='servicetemplate_edit'),
-    path('service-templates/<int:pk>/delete/', views.ServiceTemplateDeleteView.as_view(), name='servicetemplate_delete'),
     path('service-templates/<int:pk>/', include(get_model_urls('ipam', 'servicetemplate'))),
 
     # Services
@@ -162,9 +122,6 @@ urlpatterns = [
     path('services/import/', views.ServiceBulkImportView.as_view(), name='service_import'),
     path('services/edit/', views.ServiceBulkEditView.as_view(), name='service_bulk_edit'),
     path('services/delete/', views.ServiceBulkDeleteView.as_view(), name='service_bulk_delete'),
-    path('services/<int:pk>/', views.ServiceView.as_view(), name='service'),
-    path('services/<int:pk>/edit/', views.ServiceEditView.as_view(), name='service_edit'),
-    path('services/<int:pk>/delete/', views.ServiceDeleteView.as_view(), name='service_delete'),
     path('services/<int:pk>/', include(get_model_urls('ipam', 'service'))),
 
     # L2VPN
@@ -173,9 +130,6 @@ urlpatterns = [
     path('l2vpns/import/', views.L2VPNBulkImportView.as_view(), name='l2vpn_import'),
     path('l2vpns/edit/', views.L2VPNBulkEditView.as_view(), name='l2vpn_bulk_edit'),
     path('l2vpns/delete/', views.L2VPNBulkDeleteView.as_view(), name='l2vpn_bulk_delete'),
-    path('l2vpns/<int:pk>/', views.L2VPNView.as_view(), name='l2vpn'),
-    path('l2vpns/<int:pk>/edit/', views.L2VPNEditView.as_view(), name='l2vpn_edit'),
-    path('l2vpns/<int:pk>/delete/', views.L2VPNDeleteView.as_view(), name='l2vpn_delete'),
     path('l2vpns/<int:pk>/', include(get_model_urls('ipam', 'l2vpn'))),
 
     # L2VPN terminations
@@ -184,8 +138,5 @@ urlpatterns = [
     path('l2vpn-terminations/import/', views.L2VPNTerminationBulkImportView.as_view(), name='l2vpntermination_import'),
     path('l2vpn-terminations/edit/', views.L2VPNTerminationBulkEditView.as_view(), name='l2vpntermination_bulk_edit'),
     path('l2vpn-terminations/delete/', views.L2VPNTerminationBulkDeleteView.as_view(), name='l2vpntermination_bulk_delete'),
-    path('l2vpn-terminations/<int:pk>/', views.L2VPNTerminationView.as_view(), name='l2vpntermination'),
-    path('l2vpn-terminations/<int:pk>/edit/', views.L2VPNTerminationEditView.as_view(), name='l2vpntermination_edit'),
-    path('l2vpn-terminations/<int:pk>/delete/', views.L2VPNTerminationDeleteView.as_view(), name='l2vpntermination_delete'),
     path('l2vpn-terminations/<int:pk>/', include(get_model_urls('ipam', 'l2vpntermination'))),
 ]

+ 54 - 1
netbox/ipam/views.py

@@ -34,6 +34,7 @@ class VRFListView(generic.ObjectListView):
     table = tables.VRFTable
 
 
+@register_model_view(VRF)
 class VRFView(generic.ObjectView):
     queryset = VRF.objects.all()
 
@@ -58,11 +59,13 @@ class VRFView(generic.ObjectView):
         }
 
 
+@register_model_view(VRF, 'edit')
 class VRFEditView(generic.ObjectEditView):
     queryset = VRF.objects.all()
     form = forms.VRFForm
 
 
+@register_model_view(VRF, 'delete')
 class VRFDeleteView(generic.ObjectDeleteView):
     queryset = VRF.objects.all()
 
@@ -97,6 +100,7 @@ class RouteTargetListView(generic.ObjectListView):
     table = tables.RouteTargetTable
 
 
+@register_model_view(RouteTarget)
 class RouteTargetView(generic.ObjectView):
     queryset = RouteTarget.objects.all()
 
@@ -116,11 +120,13 @@ class RouteTargetView(generic.ObjectView):
         }
 
 
+@register_model_view(RouteTarget, 'edit')
 class RouteTargetEditView(generic.ObjectEditView):
     queryset = RouteTarget.objects.all()
     form = forms.RouteTargetForm
 
 
+@register_model_view(RouteTarget, 'delete')
 class RouteTargetDeleteView(generic.ObjectDeleteView):
     queryset = RouteTarget.objects.all()
 
@@ -157,6 +163,7 @@ class RIRListView(generic.ObjectListView):
     table = tables.RIRTable
 
 
+@register_model_view(RIR)
 class RIRView(generic.ObjectView):
     queryset = RIR.objects.all()
 
@@ -172,11 +179,13 @@ class RIRView(generic.ObjectView):
         }
 
 
+@register_model_view(RIR, 'edit')
 class RIREditView(generic.ObjectEditView):
     queryset = RIR.objects.all()
     form = forms.RIRForm
 
 
+@register_model_view(RIR, 'delete')
 class RIRDeleteView(generic.ObjectDeleteView):
     queryset = RIR.objects.all()
 
@@ -218,6 +227,7 @@ class ASNListView(generic.ObjectListView):
     table = tables.ASNTable
 
 
+@register_model_view(ASN)
 class ASNView(generic.ObjectView):
     queryset = ASN.objects.all()
 
@@ -242,11 +252,13 @@ class ASNView(generic.ObjectView):
         }
 
 
+@register_model_view(ASN, 'edit')
 class ASNEditView(generic.ObjectEditView):
     queryset = ASN.objects.all()
     form = forms.ASNForm
 
 
+@register_model_view(ASN, 'delete')
 class ASNDeleteView(generic.ObjectDeleteView):
     queryset = ASN.objects.all()
 
@@ -287,6 +299,7 @@ class AggregateListView(generic.ObjectListView):
     table = tables.AggregateTable
 
 
+@register_model_view(Aggregate)
 class AggregateView(generic.ObjectView):
     queryset = Aggregate.objects.all()
 
@@ -325,11 +338,13 @@ class AggregatePrefixesView(generic.ObjectChildrenView):
         }
 
 
+@register_model_view(Aggregate, 'edit')
 class AggregateEditView(generic.ObjectEditView):
     queryset = Aggregate.objects.all()
     form = forms.AggregateForm
 
 
+@register_model_view(Aggregate, 'delete')
 class AggregateDeleteView(generic.ObjectDeleteView):
     queryset = Aggregate.objects.all()
 
@@ -372,6 +387,7 @@ class RoleListView(generic.ObjectListView):
     table = tables.RoleTable
 
 
+@register_model_view(Role)
 class RoleView(generic.ObjectView):
     queryset = Role.objects.all()
 
@@ -388,11 +404,13 @@ class RoleView(generic.ObjectView):
         }
 
 
+@register_model_view(Role, 'edit')
 class RoleEditView(generic.ObjectEditView):
     queryset = Role.objects.all()
     form = forms.RoleForm
 
 
+@register_model_view(Role, 'delete')
 class RoleDeleteView(generic.ObjectDeleteView):
     queryset = Role.objects.all()
 
@@ -427,6 +445,7 @@ class PrefixListView(generic.ObjectListView):
     template_name = 'ipam/prefix_list.html'
 
 
+@register_model_view(Prefix)
 class PrefixView(generic.ObjectView):
     queryset = Prefix.objects.all()
 
@@ -560,11 +579,13 @@ class PrefixIPAddressesView(generic.ObjectChildrenView):
         }
 
 
+@register_model_view(Prefix, 'edit')
 class PrefixEditView(generic.ObjectEditView):
     queryset = Prefix.objects.all()
     form = forms.PrefixForm
 
 
+@register_model_view(Prefix, 'delete')
 class PrefixDeleteView(generic.ObjectDeleteView):
     queryset = Prefix.objects.all()
 
@@ -599,6 +620,7 @@ class IPRangeListView(generic.ObjectListView):
     table = tables.IPRangeTable
 
 
+@register_model_view(IPRange)
 class IPRangeView(generic.ObjectView):
     queryset = IPRange.objects.all()
 
@@ -620,11 +642,13 @@ class IPRangeIPAddressesView(generic.ObjectChildrenView):
         return parent.get_child_ips().restrict(request.user, 'view')
 
 
+@register_model_view(IPRange, 'edit')
 class IPRangeEditView(generic.ObjectEditView):
     queryset = IPRange.objects.all()
     form = forms.IPRangeForm
 
 
+@register_model_view(IPRange, 'delete')
 class IPRangeDeleteView(generic.ObjectDeleteView):
     queryset = IPRange.objects.all()
 
@@ -659,6 +683,7 @@ class IPAddressListView(generic.ObjectListView):
     table = tables.IPAddressTable
 
 
+@register_model_view(IPAddress)
 class IPAddressView(generic.ObjectView):
     queryset = IPAddress.objects.prefetch_related('vrf__tenant', 'tenant')
 
@@ -726,6 +751,7 @@ class IPAddressView(generic.ObjectView):
         }
 
 
+@register_model_view(IPAddress, 'edit')
 class IPAddressEditView(generic.ObjectEditView):
     queryset = IPAddress.objects.all()
     form = forms.IPAddressForm
@@ -795,6 +821,7 @@ class IPAddressAssignView(generic.ObjectView):
         })
 
 
+@register_model_view(IPAddress, 'delete')
 class IPAddressDeleteView(generic.ObjectDeleteView):
     queryset = IPAddress.objects.all()
 
@@ -839,6 +866,7 @@ class VLANGroupListView(generic.ObjectListView):
     table = tables.VLANGroupTable
 
 
+@register_model_view(VLANGroup)
 class VLANGroupView(generic.ObjectView):
     queryset = VLANGroup.objects.all()
 
@@ -869,11 +897,13 @@ class VLANGroupView(generic.ObjectView):
         }
 
 
+@register_model_view(VLANGroup, 'edit')
 class VLANGroupEditView(generic.ObjectEditView):
     queryset = VLANGroup.objects.all()
     form = forms.VLANGroupForm
 
 
+@register_model_view(VLANGroup, 'delete')
 class VLANGroupDeleteView(generic.ObjectDeleteView):
     queryset = VLANGroup.objects.all()
 
@@ -914,6 +944,7 @@ class FHRPGroupListView(generic.ObjectListView):
     table = tables.FHRPGroupTable
 
 
+@register_model_view(FHRPGroup)
 class FHRPGroupView(generic.ObjectView):
     queryset = FHRPGroup.objects.all()
 
@@ -938,6 +969,7 @@ class FHRPGroupView(generic.ObjectView):
         }
 
 
+@register_model_view(FHRPGroup, 'edit')
 class FHRPGroupEditView(generic.ObjectEditView):
     queryset = FHRPGroup.objects.all()
     form = forms.FHRPGroupForm
@@ -954,6 +986,7 @@ class FHRPGroupEditView(generic.ObjectEditView):
         return return_url
 
 
+@register_model_view(FHRPGroup, 'delete')
 class FHRPGroupDeleteView(generic.ObjectDeleteView):
     queryset = FHRPGroup.objects.all()
 
@@ -981,6 +1014,7 @@ class FHRPGroupBulkDeleteView(generic.BulkDeleteView):
 # FHRP group assignments
 #
 
+@register_model_view(FHRPGroupAssignment, 'edit')
 class FHRPGroupAssignmentEditView(generic.ObjectEditView):
     queryset = FHRPGroupAssignment.objects.all()
     form = forms.FHRPGroupAssignmentForm
@@ -994,6 +1028,7 @@ class FHRPGroupAssignmentEditView(generic.ObjectEditView):
         return instance
 
 
+@register_model_view(FHRPGroupAssignment, 'delete')
 class FHRPGroupAssignmentDeleteView(generic.ObjectDeleteView):
     queryset = FHRPGroupAssignment.objects.all()
 
@@ -1009,6 +1044,7 @@ class VLANListView(generic.ObjectListView):
     table = tables.VLANTable
 
 
+@register_model_view(VLAN)
 class VLANView(generic.ObjectView):
     queryset = VLAN.objects.all()
 
@@ -1057,12 +1093,14 @@ class VLANVMInterfacesView(generic.ObjectChildrenView):
         return parent.get_vminterfaces().restrict(request.user, 'view')
 
 
+@register_model_view(VLAN, 'edit')
 class VLANEditView(generic.ObjectEditView):
     queryset = VLAN.objects.all()
     form = forms.VLANForm
     template_name = 'ipam/vlan_edit.html'
 
 
+@register_model_view(VLAN, 'delete')
 class VLANDeleteView(generic.ObjectDeleteView):
     queryset = VLAN.objects.all()
 
@@ -1097,15 +1135,18 @@ class ServiceTemplateListView(generic.ObjectListView):
     table = tables.ServiceTemplateTable
 
 
+@register_model_view(ServiceTemplate)
 class ServiceTemplateView(generic.ObjectView):
     queryset = ServiceTemplate.objects.all()
 
 
+@register_model_view(ServiceTemplate, 'edit')
 class ServiceTemplateEditView(generic.ObjectEditView):
     queryset = ServiceTemplate.objects.all()
     form = forms.ServiceTemplateForm
 
 
+@register_model_view(ServiceTemplate, 'delete')
 class ServiceTemplateDeleteView(generic.ObjectDeleteView):
     queryset = ServiceTemplate.objects.all()
 
@@ -1140,6 +1181,7 @@ class ServiceListView(generic.ObjectListView):
     table = tables.ServiceTable
 
 
+@register_model_view(Service)
 class ServiceView(generic.ObjectView):
     queryset = Service.objects.all()
 
@@ -1150,12 +1192,14 @@ class ServiceCreateView(generic.ObjectEditView):
     template_name = 'ipam/service_create.html'
 
 
+@register_model_view(Service, 'edit')
 class ServiceEditView(generic.ObjectEditView):
     queryset = Service.objects.all()
     form = forms.ServiceForm
     template_name = 'ipam/service_edit.html'
 
 
+@register_model_view(Service, 'delete')
 class ServiceDeleteView(generic.ObjectDeleteView):
     queryset = Service.objects.all()
 
@@ -1181,7 +1225,6 @@ class ServiceBulkDeleteView(generic.BulkDeleteView):
 
 # L2VPN
 
-
 class L2VPNListView(generic.ObjectListView):
     queryset = L2VPN.objects.all()
     table = L2VPNTable
@@ -1189,6 +1232,7 @@ class L2VPNListView(generic.ObjectListView):
     filterset_form = forms.L2VPNFilterForm
 
 
+@register_model_view(L2VPN)
 class L2VPNView(generic.ObjectView):
     queryset = L2VPN.objects.all()
 
@@ -1213,11 +1257,13 @@ class L2VPNView(generic.ObjectView):
         }
 
 
+@register_model_view(L2VPN, 'edit')
 class L2VPNEditView(generic.ObjectEditView):
     queryset = L2VPN.objects.all()
     form = forms.L2VPNForm
 
 
+@register_model_view(L2VPN, 'delete')
 class L2VPNDeleteView(generic.ObjectDeleteView):
     queryset = L2VPN.objects.all()
 
@@ -1241,6 +1287,10 @@ class L2VPNBulkDeleteView(generic.BulkDeleteView):
     table = tables.L2VPNTable
 
 
+#
+# L2VPN terminations
+#
+
 class L2VPNTerminationListView(generic.ObjectListView):
     queryset = L2VPNTermination.objects.all()
     table = L2VPNTerminationTable
@@ -1248,16 +1298,19 @@ class L2VPNTerminationListView(generic.ObjectListView):
     filterset_form = forms.L2VPNTerminationFilterForm
 
 
+@register_model_view(L2VPNTermination)
 class L2VPNTerminationView(generic.ObjectView):
     queryset = L2VPNTermination.objects.all()
 
 
+@register_model_view(L2VPNTermination, 'edit')
 class L2VPNTerminationEditView(generic.ObjectEditView):
     queryset = L2VPNTermination.objects.all()
     form = forms.L2VPNTerminationForm
     template_name = 'ipam/l2vpntermination_edit.html'
 
 
+@register_model_view(L2VPNTermination, 'delete')
 class L2VPNTerminationDeleteView(generic.ObjectDeleteView):
     queryset = L2VPNTermination.objects.all()
 

+ 1 - 17
netbox/tenancy/urls.py

@@ -12,9 +12,6 @@ urlpatterns = [
     path('tenant-groups/import/', views.TenantGroupBulkImportView.as_view(), name='tenantgroup_import'),
     path('tenant-groups/edit/', views.TenantGroupBulkEditView.as_view(), name='tenantgroup_bulk_edit'),
     path('tenant-groups/delete/', views.TenantGroupBulkDeleteView.as_view(), name='tenantgroup_bulk_delete'),
-    path('tenant-groups/<int:pk>/', views.TenantGroupView.as_view(), name='tenantgroup'),
-    path('tenant-groups/<int:pk>/edit/', views.TenantGroupEditView.as_view(), name='tenantgroup_edit'),
-    path('tenant-groups/<int:pk>/delete/', views.TenantGroupDeleteView.as_view(), name='tenantgroup_delete'),
     path('tenant-groups/<int:pk>/', include(get_model_urls('tenancy', 'tenantgroup'))),
 
     # Tenants
@@ -23,9 +20,6 @@ urlpatterns = [
     path('tenants/import/', views.TenantBulkImportView.as_view(), name='tenant_import'),
     path('tenants/edit/', views.TenantBulkEditView.as_view(), name='tenant_bulk_edit'),
     path('tenants/delete/', views.TenantBulkDeleteView.as_view(), name='tenant_bulk_delete'),
-    path('tenants/<int:pk>/', views.TenantView.as_view(), name='tenant'),
-    path('tenants/<int:pk>/edit/', views.TenantEditView.as_view(), name='tenant_edit'),
-    path('tenants/<int:pk>/delete/', views.TenantDeleteView.as_view(), name='tenant_delete'),
     path('tenants/<int:pk>/', include(get_model_urls('tenancy', 'tenant'))),
 
     # Contact groups
@@ -34,9 +28,6 @@ urlpatterns = [
     path('contact-groups/import/', views.ContactGroupBulkImportView.as_view(), name='contactgroup_import'),
     path('contact-groups/edit/', views.ContactGroupBulkEditView.as_view(), name='contactgroup_bulk_edit'),
     path('contact-groups/delete/', views.ContactGroupBulkDeleteView.as_view(), name='contactgroup_bulk_delete'),
-    path('contact-groups/<int:pk>/', views.ContactGroupView.as_view(), name='contactgroup'),
-    path('contact-groups/<int:pk>/edit/', views.ContactGroupEditView.as_view(), name='contactgroup_edit'),
-    path('contact-groups/<int:pk>/delete/', views.ContactGroupDeleteView.as_view(), name='contactgroup_delete'),
     path('contact-groups/<int:pk>/', include(get_model_urls('tenancy', 'contactgroup'))),
 
     # Contact roles
@@ -45,9 +36,6 @@ urlpatterns = [
     path('contact-roles/import/', views.ContactRoleBulkImportView.as_view(), name='contactrole_import'),
     path('contact-roles/edit/', views.ContactRoleBulkEditView.as_view(), name='contactrole_bulk_edit'),
     path('contact-roles/delete/', views.ContactRoleBulkDeleteView.as_view(), name='contactrole_bulk_delete'),
-    path('contact-roles/<int:pk>/', views.ContactRoleView.as_view(), name='contactrole'),
-    path('contact-roles/<int:pk>/edit/', views.ContactRoleEditView.as_view(), name='contactrole_edit'),
-    path('contact-roles/<int:pk>/delete/', views.ContactRoleDeleteView.as_view(), name='contactrole_delete'),
     path('contact-roles/<int:pk>/', include(get_model_urls('tenancy', 'contactrole'))),
 
     # Contacts
@@ -56,14 +44,10 @@ urlpatterns = [
     path('contacts/import/', views.ContactBulkImportView.as_view(), name='contact_import'),
     path('contacts/edit/', views.ContactBulkEditView.as_view(), name='contact_bulk_edit'),
     path('contacts/delete/', views.ContactBulkDeleteView.as_view(), name='contact_bulk_delete'),
-    path('contacts/<int:pk>/', views.ContactView.as_view(), name='contact'),
-    path('contacts/<int:pk>/edit/', views.ContactEditView.as_view(), name='contact_edit'),
-    path('contacts/<int:pk>/delete/', views.ContactDeleteView.as_view(), name='contact_delete'),
     path('contacts/<int:pk>/', include(get_model_urls('tenancy', 'contact'))),
 
     # Contact assignments
     path('contact-assignments/add/', views.ContactAssignmentEditView.as_view(), name='contactassignment_add'),
-    path('contact-assignments/<int:pk>/edit/', views.ContactAssignmentEditView.as_view(), name='contactassignment_edit'),
-    path('contact-assignments/<int:pk>/delete/', views.ContactAssignmentDeleteView.as_view(), name='contactassignment_delete'),
+    path('contact-assignments/<int:pk>/', include(get_model_urls('tenancy', 'contactassignment'))),
 
 ]

+ 18 - 1
netbox/tenancy/views.py

@@ -1,5 +1,4 @@
 from django.contrib.contenttypes.models import ContentType
-from django.http import QueryDict
 from django.shortcuts import get_object_or_404
 
 from circuits.models import Circuit
@@ -7,6 +6,7 @@ from dcim.models import Cable, Device, Location, Rack, RackReservation, Site
 from ipam.models import Aggregate, IPAddress, IPRange, Prefix, VLAN, VRF, ASN
 from netbox.views import generic
 from utilities.utils import count_related
+from utilities.views import register_model_view
 from virtualization.models import VirtualMachine, Cluster
 from wireless.models import WirelessLAN, WirelessLink
 from . import filtersets, forms, tables
@@ -30,6 +30,7 @@ class TenantGroupListView(generic.ObjectListView):
     table = tables.TenantGroupTable
 
 
+@register_model_view(TenantGroup)
 class TenantGroupView(generic.ObjectView):
     queryset = TenantGroup.objects.all()
 
@@ -45,11 +46,13 @@ class TenantGroupView(generic.ObjectView):
         }
 
 
+@register_model_view(TenantGroup, 'edit')
 class TenantGroupEditView(generic.ObjectEditView):
     queryset = TenantGroup.objects.all()
     form = forms.TenantGroupForm
 
 
+@register_model_view(TenantGroup, 'delete')
 class TenantGroupDeleteView(generic.ObjectDeleteView):
     queryset = TenantGroup.objects.all()
 
@@ -95,6 +98,7 @@ class TenantListView(generic.ObjectListView):
     table = tables.TenantTable
 
 
+@register_model_view(Tenant)
 class TenantView(generic.ObjectView):
     queryset = Tenant.objects.all()
 
@@ -125,11 +129,13 @@ class TenantView(generic.ObjectView):
         }
 
 
+@register_model_view(Tenant, 'edit')
 class TenantEditView(generic.ObjectEditView):
     queryset = Tenant.objects.all()
     form = forms.TenantForm
 
 
+@register_model_view(Tenant, 'delete')
 class TenantDeleteView(generic.ObjectDeleteView):
     queryset = Tenant.objects.all()
 
@@ -170,6 +176,7 @@ class ContactGroupListView(generic.ObjectListView):
     table = tables.ContactGroupTable
 
 
+@register_model_view(ContactGroup)
 class ContactGroupView(generic.ObjectView):
     queryset = ContactGroup.objects.all()
 
@@ -198,11 +205,13 @@ class ContactGroupView(generic.ObjectView):
         }
 
 
+@register_model_view(ContactGroup, 'edit')
 class ContactGroupEditView(generic.ObjectEditView):
     queryset = ContactGroup.objects.all()
     form = forms.ContactGroupForm
 
 
+@register_model_view(ContactGroup, 'delete')
 class ContactGroupDeleteView(generic.ObjectDeleteView):
     queryset = ContactGroup.objects.all()
 
@@ -248,6 +257,7 @@ class ContactRoleListView(generic.ObjectListView):
     table = tables.ContactRoleTable
 
 
+@register_model_view(ContactRole)
 class ContactRoleView(generic.ObjectView):
     queryset = ContactRole.objects.all()
 
@@ -265,11 +275,13 @@ class ContactRoleView(generic.ObjectView):
         }
 
 
+@register_model_view(ContactRole, 'edit')
 class ContactRoleEditView(generic.ObjectEditView):
     queryset = ContactRole.objects.all()
     form = forms.ContactRoleForm
 
 
+@register_model_view(ContactRole, 'delete')
 class ContactRoleDeleteView(generic.ObjectDeleteView):
     queryset = ContactRole.objects.all()
 
@@ -305,6 +317,7 @@ class ContactListView(generic.ObjectListView):
     table = tables.ContactTable
 
 
+@register_model_view(Contact)
 class ContactView(generic.ObjectView):
     queryset = Contact.objects.all()
 
@@ -322,11 +335,13 @@ class ContactView(generic.ObjectView):
         }
 
 
+@register_model_view(Contact, 'edit')
 class ContactEditView(generic.ObjectEditView):
     queryset = Contact.objects.all()
     form = forms.ContactForm
 
 
+@register_model_view(Contact, 'delete')
 class ContactDeleteView(generic.ObjectDeleteView):
     queryset = Contact.objects.all()
 
@@ -354,6 +369,7 @@ class ContactBulkDeleteView(generic.BulkDeleteView):
 # Contact assignments
 #
 
+@register_model_view(ContactAssignment, 'edit')
 class ContactAssignmentEditView(generic.ObjectEditView):
     queryset = ContactAssignment.objects.all()
     form = forms.ContactAssignmentForm
@@ -373,5 +389,6 @@ class ContactAssignmentEditView(generic.ObjectEditView):
         }
 
 
+@register_model_view(ContactAssignment, 'delete')
 class ContactAssignmentDeleteView(generic.ObjectDeleteView):
     queryset = ContactAssignment.objects.all()

+ 3 - 1
netbox/utilities/urls.py

@@ -32,8 +32,10 @@ def get_model_urls(app_label, model_name):
             view_ = view_.as_view()
 
         # Create a path to the view
+        name = f"{model_name}_{config['name']}" if config['name'] else model_name
+        url_path = f"{config['path']}/" if config['path'] else ''
         paths.append(
-            path(f"{config['path']}/", view_, name=f"{model_name}_{config['name']}", kwargs=config['kwargs'])
+            path(url_path, view_, name=name, kwargs=config['kwargs'])
         )
 
     return paths

+ 2 - 2
netbox/utilities/views.py

@@ -167,7 +167,7 @@ class ViewTab:
         return self.badge
 
 
-def register_model_view(model, name, path=None, kwargs=None):
+def register_model_view(model, name='', path=None, kwargs=None):
     """
     This decorator can be used to "attach" a view to any model in NetBox. This is typically used to inject
     additional tabs within a model's detail view. For example, to add a custom tab to NetBox's dcim.Site model:
@@ -182,7 +182,7 @@ def register_model_view(model, name, path=None, kwargs=None):
     Args:
         model: The Django model class with which this view will be associated.
         name: The string used to form the view's name for URL resolution (e.g. via `reverse()`). This will be appended
-            to the name of the base view for the model using an underscore.
+            to the name of the base view for the model using an underscore. If blank, the model name will be used.
         path: The URL path by which the view can be reached (optional). If not provided, `name` will be used.
         kwargs: A dictionary of keyword arguments for the view to include when registering its URL path (optional).
     """

+ 0 - 17
netbox/virtualization/urls.py

@@ -12,9 +12,6 @@ urlpatterns = [
     path('cluster-types/import/', views.ClusterTypeBulkImportView.as_view(), name='clustertype_import'),
     path('cluster-types/edit/', views.ClusterTypeBulkEditView.as_view(), name='clustertype_bulk_edit'),
     path('cluster-types/delete/', views.ClusterTypeBulkDeleteView.as_view(), name='clustertype_bulk_delete'),
-    path('cluster-types/<int:pk>/', views.ClusterTypeView.as_view(), name='clustertype'),
-    path('cluster-types/<int:pk>/edit/', views.ClusterTypeEditView.as_view(), name='clustertype_edit'),
-    path('cluster-types/<int:pk>/delete/', views.ClusterTypeDeleteView.as_view(), name='clustertype_delete'),
     path('cluster-types/<int:pk>/', include(get_model_urls('virtualization', 'clustertype'))),
 
     # Cluster groups
@@ -23,9 +20,6 @@ urlpatterns = [
     path('cluster-groups/import/', views.ClusterGroupBulkImportView.as_view(), name='clustergroup_import'),
     path('cluster-groups/edit/', views.ClusterGroupBulkEditView.as_view(), name='clustergroup_bulk_edit'),
     path('cluster-groups/delete/', views.ClusterGroupBulkDeleteView.as_view(), name='clustergroup_bulk_delete'),
-    path('cluster-groups/<int:pk>/', views.ClusterGroupView.as_view(), name='clustergroup'),
-    path('cluster-groups/<int:pk>/edit/', views.ClusterGroupEditView.as_view(), name='clustergroup_edit'),
-    path('cluster-groups/<int:pk>/delete/', views.ClusterGroupDeleteView.as_view(), name='clustergroup_delete'),
     path('cluster-groups/<int:pk>/', include(get_model_urls('virtualization', 'clustergroup'))),
 
     # Clusters
@@ -34,11 +28,6 @@ urlpatterns = [
     path('clusters/import/', views.ClusterBulkImportView.as_view(), name='cluster_import'),
     path('clusters/edit/', views.ClusterBulkEditView.as_view(), name='cluster_bulk_edit'),
     path('clusters/delete/', views.ClusterBulkDeleteView.as_view(), name='cluster_bulk_delete'),
-    path('clusters/<int:pk>/', views.ClusterView.as_view(), name='cluster'),
-    path('clusters/<int:pk>/edit/', views.ClusterEditView.as_view(), name='cluster_edit'),
-    path('clusters/<int:pk>/delete/', views.ClusterDeleteView.as_view(), name='cluster_delete'),
-    path('clusters/<int:pk>/devices/add/', views.ClusterAddDevicesView.as_view(), name='cluster_add_devices'),
-    path('clusters/<int:pk>/devices/remove/', views.ClusterRemoveDevicesView.as_view(), name='cluster_remove_devices'),
     path('clusters/<int:pk>/', include(get_model_urls('virtualization', 'cluster'))),
 
     # Virtual machines
@@ -47,9 +36,6 @@ urlpatterns = [
     path('virtual-machines/import/', views.VirtualMachineBulkImportView.as_view(), name='virtualmachine_import'),
     path('virtual-machines/edit/', views.VirtualMachineBulkEditView.as_view(), name='virtualmachine_bulk_edit'),
     path('virtual-machines/delete/', views.VirtualMachineBulkDeleteView.as_view(), name='virtualmachine_bulk_delete'),
-    path('virtual-machines/<int:pk>/', views.VirtualMachineView.as_view(), name='virtualmachine'),
-    path('virtual-machines/<int:pk>/edit/', views.VirtualMachineEditView.as_view(), name='virtualmachine_edit'),
-    path('virtual-machines/<int:pk>/delete/', views.VirtualMachineDeleteView.as_view(), name='virtualmachine_delete'),
     path('virtual-machines/<int:pk>/', include(get_model_urls('virtualization', 'virtualmachine'))),
 
     # VM interfaces
@@ -59,9 +45,6 @@ urlpatterns = [
     path('interfaces/edit/', views.VMInterfaceBulkEditView.as_view(), name='vminterface_bulk_edit'),
     path('interfaces/rename/', views.VMInterfaceBulkRenameView.as_view(), name='vminterface_bulk_rename'),
     path('interfaces/delete/', views.VMInterfaceBulkDeleteView.as_view(), name='vminterface_bulk_delete'),
-    path('interfaces/<int:pk>/', views.VMInterfaceView.as_view(), name='vminterface'),
-    path('interfaces/<int:pk>/edit/', views.VMInterfaceEditView.as_view(), name='vminterface_edit'),
-    path('interfaces/<int:pk>/delete/', views.VMInterfaceDeleteView.as_view(), name='vminterface_delete'),
     path('interfaces/<int:pk>/', include(get_model_urls('virtualization', 'vminterface'))),
     path('virtual-machines/interfaces/add/', views.VirtualMachineBulkAddInterfaceView.as_view(), name='virtualmachine_bulk_add_vminterface'),
 

+ 17 - 0
netbox/virtualization/views.py

@@ -31,6 +31,7 @@ class ClusterTypeListView(generic.ObjectListView):
     table = tables.ClusterTypeTable
 
 
+@register_model_view(ClusterType)
 class ClusterTypeView(generic.ObjectView):
     queryset = ClusterType.objects.all()
 
@@ -49,11 +50,13 @@ class ClusterTypeView(generic.ObjectView):
         }
 
 
+@register_model_view(ClusterType, 'edit')
 class ClusterTypeEditView(generic.ObjectEditView):
     queryset = ClusterType.objects.all()
     form = forms.ClusterTypeForm
 
 
+@register_model_view(ClusterType, 'delete')
 class ClusterTypeDeleteView(generic.ObjectDeleteView):
     queryset = ClusterType.objects.all()
 
@@ -93,6 +96,7 @@ class ClusterGroupListView(generic.ObjectListView):
     table = tables.ClusterGroupTable
 
 
+@register_model_view(ClusterGroup)
 class ClusterGroupView(generic.ObjectView):
     queryset = ClusterGroup.objects.all()
 
@@ -111,11 +115,13 @@ class ClusterGroupView(generic.ObjectView):
         }
 
 
+@register_model_view(ClusterGroup, 'edit')
 class ClusterGroupEditView(generic.ObjectEditView):
     queryset = ClusterGroup.objects.all()
     form = forms.ClusterGroupForm
 
 
+@register_model_view(ClusterGroup, 'delete')
 class ClusterGroupDeleteView(generic.ObjectDeleteView):
     queryset = ClusterGroup.objects.all()
 
@@ -159,6 +165,7 @@ class ClusterListView(generic.ObjectListView):
     filterset_form = forms.ClusterFilterForm
 
 
+@register_model_view(Cluster)
 class ClusterView(generic.ObjectView):
     queryset = Cluster.objects.all()
 
@@ -197,11 +204,13 @@ class ClusterDevicesView(generic.ObjectChildrenView):
         return Device.objects.restrict(request.user, 'view').filter(cluster=parent)
 
 
+@register_model_view(Cluster, 'edit')
 class ClusterEditView(generic.ObjectEditView):
     queryset = Cluster.objects.all()
     form = forms.ClusterForm
 
 
+@register_model_view(Cluster, 'delete')
 class ClusterDeleteView(generic.ObjectDeleteView):
     queryset = Cluster.objects.all()
 
@@ -225,6 +234,7 @@ class ClusterBulkDeleteView(generic.BulkDeleteView):
     table = tables.ClusterTable
 
 
+@register_model_view(Cluster, 'add_devices', path='devices/add')
 class ClusterAddDevicesView(generic.ObjectEditView):
     queryset = Cluster.objects.all()
     form = forms.ClusterAddDevicesForm
@@ -266,6 +276,7 @@ class ClusterAddDevicesView(generic.ObjectEditView):
         })
 
 
+@register_model_view(Cluster, 'remove_devices', path='devices/remove')
 class ClusterRemoveDevicesView(generic.ObjectEditView):
     queryset = Cluster.objects.all()
     form = forms.ClusterRemoveDevicesForm
@@ -319,6 +330,7 @@ class VirtualMachineListView(generic.ObjectListView):
     template_name = 'virtualization/virtualmachine_list.html'
 
 
+@register_model_view(VirtualMachine)
 class VirtualMachineView(generic.ObjectView):
     queryset = VirtualMachine.objects.prefetch_related('tenant__group')
 
@@ -378,11 +390,13 @@ class VirtualMachineConfigContextView(ObjectConfigContextView):
     )
 
 
+@register_model_view(VirtualMachine, 'edit')
 class VirtualMachineEditView(generic.ObjectEditView):
     queryset = VirtualMachine.objects.all()
     form = forms.VirtualMachineForm
 
 
+@register_model_view(VirtualMachine, 'delete')
 class VirtualMachineDeleteView(generic.ObjectDeleteView):
     queryset = VirtualMachine.objects.all()
 
@@ -418,6 +432,7 @@ class VMInterfaceListView(generic.ObjectListView):
     actions = ('import', 'export', 'bulk_edit', 'bulk_delete')
 
 
+@register_model_view(VMInterface)
 class VMInterfaceView(generic.ObjectView):
     queryset = VMInterface.objects.all()
 
@@ -463,11 +478,13 @@ class VMInterfaceCreateView(generic.ComponentCreateView):
     model_form = forms.VMInterfaceForm
 
 
+@register_model_view(VMInterface, 'edit')
 class VMInterfaceEditView(generic.ObjectEditView):
     queryset = VMInterface.objects.all()
     form = forms.VMInterfaceForm
 
 
+@register_model_view(VMInterface, 'delete')
 class VMInterfaceDeleteView(generic.ObjectDeleteView):
     queryset = VMInterface.objects.all()
 

+ 0 - 9
netbox/wireless/urls.py

@@ -12,9 +12,6 @@ urlpatterns = (
     path('wireless-lan-groups/import/', views.WirelessLANGroupBulkImportView.as_view(), name='wirelesslangroup_import'),
     path('wireless-lan-groups/edit/', views.WirelessLANGroupBulkEditView.as_view(), name='wirelesslangroup_bulk_edit'),
     path('wireless-lan-groups/delete/', views.WirelessLANGroupBulkDeleteView.as_view(), name='wirelesslangroup_bulk_delete'),
-    path('wireless-lan-groups/<int:pk>/', views.WirelessLANGroupView.as_view(), name='wirelesslangroup'),
-    path('wireless-lan-groups/<int:pk>/edit/', views.WirelessLANGroupEditView.as_view(), name='wirelesslangroup_edit'),
-    path('wireless-lan-groups/<int:pk>/delete/', views.WirelessLANGroupDeleteView.as_view(), name='wirelesslangroup_delete'),
     path('wireless-lan-groups/<int:pk>/', include(get_model_urls('wireless', 'wirelesslangroup'))),
 
     # Wireless LANs
@@ -23,9 +20,6 @@ urlpatterns = (
     path('wireless-lans/import/', views.WirelessLANBulkImportView.as_view(), name='wirelesslan_import'),
     path('wireless-lans/edit/', views.WirelessLANBulkEditView.as_view(), name='wirelesslan_bulk_edit'),
     path('wireless-lans/delete/', views.WirelessLANBulkDeleteView.as_view(), name='wirelesslan_bulk_delete'),
-    path('wireless-lans/<int:pk>/', views.WirelessLANView.as_view(), name='wirelesslan'),
-    path('wireless-lans/<int:pk>/edit/', views.WirelessLANEditView.as_view(), name='wirelesslan_edit'),
-    path('wireless-lans/<int:pk>/delete/', views.WirelessLANDeleteView.as_view(), name='wirelesslan_delete'),
     path('wireless-lans/<int:pk>/', include(get_model_urls('wireless', 'wirelesslan'))),
 
     # Wireless links
@@ -34,9 +28,6 @@ urlpatterns = (
     path('wireless-links/import/', views.WirelessLinkBulkImportView.as_view(), name='wirelesslink_import'),
     path('wireless-links/edit/', views.WirelessLinkBulkEditView.as_view(), name='wirelesslink_bulk_edit'),
     path('wireless-links/delete/', views.WirelessLinkBulkDeleteView.as_view(), name='wirelesslink_bulk_delete'),
-    path('wireless-links/<int:pk>/', views.WirelessLinkView.as_view(), name='wirelesslink'),
-    path('wireless-links/<int:pk>/edit/', views.WirelessLinkEditView.as_view(), name='wirelesslink_edit'),
-    path('wireless-links/<int:pk>/delete/', views.WirelessLinkDeleteView.as_view(), name='wirelesslink_delete'),
     path('wireless-links/<int:pk>/', include(get_model_urls('wireless', 'wirelesslink'))),
 
 )

+ 10 - 0
netbox/wireless/views.py

@@ -1,6 +1,7 @@
 from dcim.models import Interface
 from netbox.views import generic
 from utilities.utils import count_related
+from utilities.views import register_model_view
 from . import filtersets, forms, tables
 from .models import *
 
@@ -22,6 +23,7 @@ class WirelessLANGroupListView(generic.ObjectListView):
     table = tables.WirelessLANGroupTable
 
 
+@register_model_view(WirelessLANGroup)
 class WirelessLANGroupView(generic.ObjectView):
     queryset = WirelessLANGroup.objects.all()
 
@@ -37,11 +39,13 @@ class WirelessLANGroupView(generic.ObjectView):
         }
 
 
+@register_model_view(WirelessLANGroup, 'edit')
 class WirelessLANGroupEditView(generic.ObjectEditView):
     queryset = WirelessLANGroup.objects.all()
     form = forms.WirelessLANGroupForm
 
 
+@register_model_view(WirelessLANGroup, 'delete')
 class WirelessLANGroupDeleteView(generic.ObjectDeleteView):
     queryset = WirelessLANGroup.objects.all()
 
@@ -90,6 +94,7 @@ class WirelessLANListView(generic.ObjectListView):
     table = tables.WirelessLANTable
 
 
+@register_model_view(WirelessLAN)
 class WirelessLANView(generic.ObjectView):
     queryset = WirelessLAN.objects.all()
 
@@ -105,11 +110,13 @@ class WirelessLANView(generic.ObjectView):
         }
 
 
+@register_model_view(WirelessLAN, 'edit')
 class WirelessLANEditView(generic.ObjectEditView):
     queryset = WirelessLAN.objects.all()
     form = forms.WirelessLANForm
 
 
+@register_model_view(WirelessLAN, 'delete')
 class WirelessLANDeleteView(generic.ObjectDeleteView):
     queryset = WirelessLAN.objects.all()
 
@@ -144,15 +151,18 @@ class WirelessLinkListView(generic.ObjectListView):
     table = tables.WirelessLinkTable
 
 
+@register_model_view(WirelessLink)
 class WirelessLinkView(generic.ObjectView):
     queryset = WirelessLink.objects.all()
 
 
+@register_model_view(WirelessLink, 'edit')
 class WirelessLinkEditView(generic.ObjectEditView):
     queryset = WirelessLink.objects.all()
     form = forms.WirelessLinkForm
 
 
+@register_model_view(WirelessLink, 'delete')
 class WirelessLinkDeleteView(generic.ObjectDeleteView):
     queryset = WirelessLink.objects.all()
 

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini