Browse Source

Closes #18023: Employ `register_model_view()` for list views (#18029)

* Extend register_model_view() to enable registering list views

* Register circuits list views with register_model_view()

* Register core list views with register_model_view()

* Fix bulk_edit & bulk_delete URL paths

* Register dcim list views with register_model_view() (WIP)

* Register dcim list views with register_model_view()

* Register extras list views with register_model_view()

* Register ipam list views with register_model_view()

* Register tenancy list views with register_model_view()

* Register users list views with register_model_view()

* Register virtualization list views with register_model_view()

* Register vpn list views with register_model_view()

* Register wireless list views with register_model_view()

* Add change note for register_model_view()
Jeremy Stretch 1 year ago
parent
commit
a0b4b0afe0

+ 3 - 0
docs/plugins/development/views.md

@@ -185,6 +185,9 @@ class MyView(generic.ObjectView):
         )
         )
 ```
 ```
 
 
+!!! note "Changed in NetBox v4.2"
+    The `register_model_view()` function was extended in NetBox v4.2 to support registration of list views by passing `detail=False`.
+
 ::: utilities.views.register_model_view
 ::: utilities.views.register_model_view
 
 
 ::: utilities.views.ViewTab
 ::: utilities.views.ViewTab

+ 13 - 50
netbox/circuits/urls.py

@@ -5,70 +5,33 @@ from . import views
 
 
 app_name = 'circuits'
 app_name = 'circuits'
 urlpatterns = [
 urlpatterns = [
-
-    # Providers
-    path('providers/', views.ProviderListView.as_view(), name='provider_list'),
-    path('providers/add/', views.ProviderEditView.as_view(), name='provider_add'),
-    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/', include(get_model_urls('circuits', 'provider', detail=False))),
     path('providers/<int:pk>/', include(get_model_urls('circuits', 'provider'))),
     path('providers/<int:pk>/', include(get_model_urls('circuits', 'provider'))),
 
 
-    # Provider accounts
-    path('provider-accounts/', views.ProviderAccountListView.as_view(), name='provideraccount_list'),
-    path('provider-accounts/add/', views.ProviderAccountEditView.as_view(), name='provideraccount_add'),
-    path('provider-accounts/import/', views.ProviderAccountBulkImportView.as_view(), name='provideraccount_import'),
-    path('provider-accounts/edit/', views.ProviderAccountBulkEditView.as_view(), name='provideraccount_bulk_edit'),
-    path('provider-accounts/delete/', views.ProviderAccountBulkDeleteView.as_view(), name='provideraccount_bulk_delete'),
+    path('provider-accounts/', include(get_model_urls('circuits', 'provideraccount', detail=False))),
     path('provider-accounts/<int:pk>/', include(get_model_urls('circuits', 'provideraccount'))),
     path('provider-accounts/<int:pk>/', include(get_model_urls('circuits', 'provideraccount'))),
 
 
-    # Provider networks
-    path('provider-networks/', views.ProviderNetworkListView.as_view(), name='providernetwork_list'),
-    path('provider-networks/add/', views.ProviderNetworkEditView.as_view(), name='providernetwork_add'),
-    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/', include(get_model_urls('circuits', 'providernetwork', detail=False))),
     path('provider-networks/<int:pk>/', include(get_model_urls('circuits', 'providernetwork'))),
     path('provider-networks/<int:pk>/', include(get_model_urls('circuits', 'providernetwork'))),
 
 
-    # Circuit types
-    path('circuit-types/', views.CircuitTypeListView.as_view(), name='circuittype_list'),
-    path('circuit-types/add/', views.CircuitTypeEditView.as_view(), name='circuittype_add'),
-    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/', include(get_model_urls('circuits', 'circuittype', detail=False))),
     path('circuit-types/<int:pk>/', include(get_model_urls('circuits', 'circuittype'))),
     path('circuit-types/<int:pk>/', include(get_model_urls('circuits', 'circuittype'))),
 
 
-    # Circuits
-    path('circuits/', views.CircuitListView.as_view(), name='circuit_list'),
-    path('circuits/add/', views.CircuitEditView.as_view(), name='circuit_add'),
-    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>/terminations/swap/', views.CircuitSwapTerminations.as_view(), name='circuit_terminations_swap'),
+    path('circuits/', include(get_model_urls('circuits', 'circuit', detail=False))),
+    path(
+        'circuits/<int:pk>/terminations/swap/',
+        views.CircuitSwapTerminations.as_view(),
+        name='circuit_terminations_swap'
+    ),
     path('circuits/<int:pk>/', include(get_model_urls('circuits', 'circuit'))),
     path('circuits/<int:pk>/', include(get_model_urls('circuits', 'circuit'))),
 
 
-    # Circuit terminations
-    path('circuit-terminations/', views.CircuitTerminationListView.as_view(), name='circuittermination_list'),
-    path('circuit-terminations/add/', views.CircuitTerminationEditView.as_view(), name='circuittermination_add'),
-    path('circuit-terminations/import/', views.CircuitTerminationBulkImportView.as_view(), name='circuittermination_import'),
-    path('circuit-terminations/edit/', views.CircuitTerminationBulkEditView.as_view(), name='circuittermination_bulk_edit'),
-    path('circuit-terminations/delete/', views.CircuitTerminationBulkDeleteView.as_view(), name='circuittermination_bulk_delete'),
+    path('circuit-terminations/', include(get_model_urls('circuits', 'circuittermination', detail=False))),
     path('circuit-terminations/<int:pk>/', include(get_model_urls('circuits', 'circuittermination'))),
     path('circuit-terminations/<int:pk>/', include(get_model_urls('circuits', 'circuittermination'))),
 
 
-    # Circuit Groups
-    path('circuit-groups/', views.CircuitGroupListView.as_view(), name='circuitgroup_list'),
-    path('circuit-groups/add/', views.CircuitGroupEditView.as_view(), name='circuitgroup_add'),
-    path('circuit-groups/import/', views.CircuitGroupBulkImportView.as_view(), name='circuitgroup_import'),
-    path('circuit-groups/edit/', views.CircuitGroupBulkEditView.as_view(), name='circuitgroup_bulk_edit'),
-    path('circuit-groups/delete/', views.CircuitGroupBulkDeleteView.as_view(), name='circuitgroup_bulk_delete'),
+    path('circuit-groups/', include(get_model_urls('circuits', 'circuitgroup', detail=False))),
     path('circuit-groups/<int:pk>/', include(get_model_urls('circuits', 'circuitgroup'))),
     path('circuit-groups/<int:pk>/', include(get_model_urls('circuits', 'circuitgroup'))),
 
 
-    # Circuit Group Assignments
-    path('circuit-group-assignments/', views.CircuitGroupAssignmentListView.as_view(), name='circuitgroupassignment_list'),
-    path('circuit-group-assignments/add/', views.CircuitGroupAssignmentEditView.as_view(), name='circuitgroupassignment_add'),
-    path('circuit-group-assignments/import/', views.CircuitGroupAssignmentBulkImportView.as_view(), name='circuitgroupassignment_import'),
-    path('circuit-group-assignments/edit/', views.CircuitGroupAssignmentBulkEditView.as_view(), name='circuitgroupassignment_bulk_edit'),
-    path('circuit-group-assignments/delete/', views.CircuitGroupAssignmentBulkDeleteView.as_view(), name='circuitgroupassignment_bulk_delete'),
+    path('circuit-group-assignments/', include(get_model_urls('circuits', 'circuitgroupassignment', detail=False))),
     path('circuit-group-assignments/<int:pk>/', include(get_model_urls('circuits', 'circuitgroupassignment'))),
     path('circuit-group-assignments/<int:pk>/', include(get_model_urls('circuits', 'circuitgroupassignment'))),
 
 
     # Virtual circuits
     # Virtual circuits

+ 40 - 0
netbox/circuits/views.py

@@ -17,6 +17,7 @@ from .models import *
 # Providers
 # Providers
 #
 #
 
 
+@register_model_view(Provider, 'list', path='', detail=False)
 class ProviderListView(generic.ObjectListView):
 class ProviderListView(generic.ObjectListView):
     queryset = Provider.objects.annotate(
     queryset = Provider.objects.annotate(
         count_circuits=count_related(Circuit, 'provider')
         count_circuits=count_related(Circuit, 'provider')
@@ -36,6 +37,7 @@ class ProviderView(GetRelatedModelsMixin, generic.ObjectView):
         }
         }
 
 
 
 
+@register_model_view(Provider, 'add', detail=False)
 @register_model_view(Provider, 'edit')
 @register_model_view(Provider, 'edit')
 class ProviderEditView(generic.ObjectEditView):
 class ProviderEditView(generic.ObjectEditView):
     queryset = Provider.objects.all()
     queryset = Provider.objects.all()
@@ -47,11 +49,13 @@ class ProviderDeleteView(generic.ObjectDeleteView):
     queryset = Provider.objects.all()
     queryset = Provider.objects.all()
 
 
 
 
+@register_model_view(Provider, 'import', detail=False)
 class ProviderBulkImportView(generic.BulkImportView):
 class ProviderBulkImportView(generic.BulkImportView):
     queryset = Provider.objects.all()
     queryset = Provider.objects.all()
     model_form = forms.ProviderImportForm
     model_form = forms.ProviderImportForm
 
 
 
 
+@register_model_view(Provider, 'bulk_edit', path='edit', detail=False)
 class ProviderBulkEditView(generic.BulkEditView):
 class ProviderBulkEditView(generic.BulkEditView):
     queryset = Provider.objects.annotate(
     queryset = Provider.objects.annotate(
         count_circuits=count_related(Circuit, 'provider')
         count_circuits=count_related(Circuit, 'provider')
@@ -61,6 +65,7 @@ class ProviderBulkEditView(generic.BulkEditView):
     form = forms.ProviderBulkEditForm
     form = forms.ProviderBulkEditForm
 
 
 
 
+@register_model_view(Provider, 'bulk_delete', path='delete', detail=False)
 class ProviderBulkDeleteView(generic.BulkDeleteView):
 class ProviderBulkDeleteView(generic.BulkDeleteView):
     queryset = Provider.objects.annotate(
     queryset = Provider.objects.annotate(
         count_circuits=count_related(Circuit, 'provider')
         count_circuits=count_related(Circuit, 'provider')
@@ -78,6 +83,7 @@ class ProviderContactsView(ObjectContactsView):
 # ProviderAccounts
 # ProviderAccounts
 #
 #
 
 
+@register_model_view(ProviderAccount, 'list', path='', detail=False)
 class ProviderAccountListView(generic.ObjectListView):
 class ProviderAccountListView(generic.ObjectListView):
     queryset = ProviderAccount.objects.annotate(
     queryset = ProviderAccount.objects.annotate(
         count_circuits=count_related(Circuit, 'provider_account')
         count_circuits=count_related(Circuit, 'provider_account')
@@ -97,6 +103,7 @@ class ProviderAccountView(GetRelatedModelsMixin, generic.ObjectView):
         }
         }
 
 
 
 
+@register_model_view(ProviderAccount, 'add', detail=False)
 @register_model_view(ProviderAccount, 'edit')
 @register_model_view(ProviderAccount, 'edit')
 class ProviderAccountEditView(generic.ObjectEditView):
 class ProviderAccountEditView(generic.ObjectEditView):
     queryset = ProviderAccount.objects.all()
     queryset = ProviderAccount.objects.all()
@@ -108,12 +115,14 @@ class ProviderAccountDeleteView(generic.ObjectDeleteView):
     queryset = ProviderAccount.objects.all()
     queryset = ProviderAccount.objects.all()
 
 
 
 
+@register_model_view(ProviderAccount, 'import', detail=False)
 class ProviderAccountBulkImportView(generic.BulkImportView):
 class ProviderAccountBulkImportView(generic.BulkImportView):
     queryset = ProviderAccount.objects.all()
     queryset = ProviderAccount.objects.all()
     model_form = forms.ProviderAccountImportForm
     model_form = forms.ProviderAccountImportForm
     table = tables.ProviderAccountTable
     table = tables.ProviderAccountTable
 
 
 
 
+@register_model_view(ProviderAccount, 'bulk_edit', path='edit', detail=False)
 class ProviderAccountBulkEditView(generic.BulkEditView):
 class ProviderAccountBulkEditView(generic.BulkEditView):
     queryset = ProviderAccount.objects.annotate(
     queryset = ProviderAccount.objects.annotate(
         count_circuits=count_related(Circuit, 'provider_account')
         count_circuits=count_related(Circuit, 'provider_account')
@@ -123,6 +132,7 @@ class ProviderAccountBulkEditView(generic.BulkEditView):
     form = forms.ProviderAccountBulkEditForm
     form = forms.ProviderAccountBulkEditForm
 
 
 
 
+@register_model_view(ProviderAccount, 'bulk_delete', path='delete', detail=False)
 class ProviderAccountBulkDeleteView(generic.BulkDeleteView):
 class ProviderAccountBulkDeleteView(generic.BulkDeleteView):
     queryset = ProviderAccount.objects.annotate(
     queryset = ProviderAccount.objects.annotate(
         count_circuits=count_related(Circuit, 'provider_account')
         count_circuits=count_related(Circuit, 'provider_account')
@@ -140,6 +150,7 @@ class ProviderAccountContactsView(ObjectContactsView):
 # Provider networks
 # Provider networks
 #
 #
 
 
+@register_model_view(ProviderNetwork, 'list', path='', detail=False)
 class ProviderNetworkListView(generic.ObjectListView):
 class ProviderNetworkListView(generic.ObjectListView):
     queryset = ProviderNetwork.objects.all()
     queryset = ProviderNetwork.objects.all()
     filterset = filtersets.ProviderNetworkFilterSet
     filterset = filtersets.ProviderNetworkFilterSet
@@ -166,6 +177,7 @@ class ProviderNetworkView(GetRelatedModelsMixin, generic.ObjectView):
         }
         }
 
 
 
 
+@register_model_view(ProviderNetwork, 'add', detail=False)
 @register_model_view(ProviderNetwork, 'edit')
 @register_model_view(ProviderNetwork, 'edit')
 class ProviderNetworkEditView(generic.ObjectEditView):
 class ProviderNetworkEditView(generic.ObjectEditView):
     queryset = ProviderNetwork.objects.all()
     queryset = ProviderNetwork.objects.all()
@@ -177,11 +189,13 @@ class ProviderNetworkDeleteView(generic.ObjectDeleteView):
     queryset = ProviderNetwork.objects.all()
     queryset = ProviderNetwork.objects.all()
 
 
 
 
+@register_model_view(ProviderNetwork, 'import', detail=False)
 class ProviderNetworkBulkImportView(generic.BulkImportView):
 class ProviderNetworkBulkImportView(generic.BulkImportView):
     queryset = ProviderNetwork.objects.all()
     queryset = ProviderNetwork.objects.all()
     model_form = forms.ProviderNetworkImportForm
     model_form = forms.ProviderNetworkImportForm
 
 
 
 
+@register_model_view(ProviderNetwork, 'bulk_edit', path='edit', detail=False)
 class ProviderNetworkBulkEditView(generic.BulkEditView):
 class ProviderNetworkBulkEditView(generic.BulkEditView):
     queryset = ProviderNetwork.objects.all()
     queryset = ProviderNetwork.objects.all()
     filterset = filtersets.ProviderNetworkFilterSet
     filterset = filtersets.ProviderNetworkFilterSet
@@ -189,6 +203,7 @@ class ProviderNetworkBulkEditView(generic.BulkEditView):
     form = forms.ProviderNetworkBulkEditForm
     form = forms.ProviderNetworkBulkEditForm
 
 
 
 
+@register_model_view(ProviderNetwork, 'bulk_delete', path='delete', detail=False)
 class ProviderNetworkBulkDeleteView(generic.BulkDeleteView):
 class ProviderNetworkBulkDeleteView(generic.BulkDeleteView):
     queryset = ProviderNetwork.objects.all()
     queryset = ProviderNetwork.objects.all()
     filterset = filtersets.ProviderNetworkFilterSet
     filterset = filtersets.ProviderNetworkFilterSet
@@ -199,6 +214,7 @@ class ProviderNetworkBulkDeleteView(generic.BulkDeleteView):
 # Circuit Types
 # Circuit Types
 #
 #
 
 
+@register_model_view(CircuitType, 'list', path='', detail=False)
 class CircuitTypeListView(generic.ObjectListView):
 class CircuitTypeListView(generic.ObjectListView):
     queryset = CircuitType.objects.annotate(
     queryset = CircuitType.objects.annotate(
         circuit_count=count_related(Circuit, 'type')
         circuit_count=count_related(Circuit, 'type')
@@ -218,6 +234,7 @@ class CircuitTypeView(GetRelatedModelsMixin, generic.ObjectView):
         }
         }
 
 
 
 
+@register_model_view(CircuitType, 'add', detail=False)
 @register_model_view(CircuitType, 'edit')
 @register_model_view(CircuitType, 'edit')
 class CircuitTypeEditView(generic.ObjectEditView):
 class CircuitTypeEditView(generic.ObjectEditView):
     queryset = CircuitType.objects.all()
     queryset = CircuitType.objects.all()
@@ -229,11 +246,13 @@ class CircuitTypeDeleteView(generic.ObjectDeleteView):
     queryset = CircuitType.objects.all()
     queryset = CircuitType.objects.all()
 
 
 
 
+@register_model_view(CircuitType, 'import', detail=False)
 class CircuitTypeBulkImportView(generic.BulkImportView):
 class CircuitTypeBulkImportView(generic.BulkImportView):
     queryset = CircuitType.objects.all()
     queryset = CircuitType.objects.all()
     model_form = forms.CircuitTypeImportForm
     model_form = forms.CircuitTypeImportForm
 
 
 
 
+@register_model_view(CircuitType, 'bulk_edit', path='edit', detail=False)
 class CircuitTypeBulkEditView(generic.BulkEditView):
 class CircuitTypeBulkEditView(generic.BulkEditView):
     queryset = CircuitType.objects.annotate(
     queryset = CircuitType.objects.annotate(
         circuit_count=count_related(Circuit, 'type')
         circuit_count=count_related(Circuit, 'type')
@@ -243,6 +262,7 @@ class CircuitTypeBulkEditView(generic.BulkEditView):
     form = forms.CircuitTypeBulkEditForm
     form = forms.CircuitTypeBulkEditForm
 
 
 
 
+@register_model_view(CircuitType, 'bulk_delete', path='delete', detail=False)
 class CircuitTypeBulkDeleteView(generic.BulkDeleteView):
 class CircuitTypeBulkDeleteView(generic.BulkDeleteView):
     queryset = CircuitType.objects.annotate(
     queryset = CircuitType.objects.annotate(
         circuit_count=count_related(Circuit, 'type')
         circuit_count=count_related(Circuit, 'type')
@@ -255,6 +275,7 @@ class CircuitTypeBulkDeleteView(generic.BulkDeleteView):
 # Circuits
 # Circuits
 #
 #
 
 
+@register_model_view(Circuit, 'list', path='', detail=False)
 class CircuitListView(generic.ObjectListView):
 class CircuitListView(generic.ObjectListView):
     queryset = Circuit.objects.prefetch_related(
     queryset = Circuit.objects.prefetch_related(
         'tenant__group', 'termination_a__termination', 'termination_z__termination',
         'tenant__group', 'termination_a__termination', 'termination_z__termination',
@@ -269,6 +290,7 @@ class CircuitView(generic.ObjectView):
     queryset = Circuit.objects.all()
     queryset = Circuit.objects.all()
 
 
 
 
+@register_model_view(Circuit, 'add', detail=False)
 @register_model_view(Circuit, 'edit')
 @register_model_view(Circuit, 'edit')
 class CircuitEditView(generic.ObjectEditView):
 class CircuitEditView(generic.ObjectEditView):
     queryset = Circuit.objects.all()
     queryset = Circuit.objects.all()
@@ -280,6 +302,7 @@ class CircuitDeleteView(generic.ObjectDeleteView):
     queryset = Circuit.objects.all()
     queryset = Circuit.objects.all()
 
 
 
 
+@register_model_view(Circuit, 'import', detail=False)
 class CircuitBulkImportView(generic.BulkImportView):
 class CircuitBulkImportView(generic.BulkImportView):
     queryset = Circuit.objects.all()
     queryset = Circuit.objects.all()
     model_form = forms.CircuitImportForm
     model_form = forms.CircuitImportForm
@@ -295,6 +318,7 @@ class CircuitBulkImportView(generic.BulkImportView):
         return data
         return data
 
 
 
 
+@register_model_view(Circuit, 'bulk_edit', path='edit', detail=False)
 class CircuitBulkEditView(generic.BulkEditView):
 class CircuitBulkEditView(generic.BulkEditView):
     queryset = Circuit.objects.prefetch_related(
     queryset = Circuit.objects.prefetch_related(
         'tenant__group', 'termination_a__termination', 'termination_z__termination',
         'tenant__group', 'termination_a__termination', 'termination_z__termination',
@@ -304,6 +328,7 @@ class CircuitBulkEditView(generic.BulkEditView):
     form = forms.CircuitBulkEditForm
     form = forms.CircuitBulkEditForm
 
 
 
 
+@register_model_view(Circuit, 'bulk_delete', path='delete', detail=False)
 class CircuitBulkDeleteView(generic.BulkDeleteView):
 class CircuitBulkDeleteView(generic.BulkDeleteView):
     queryset = Circuit.objects.prefetch_related(
     queryset = Circuit.objects.prefetch_related(
         'tenant__group', 'termination_a__termination', 'termination_z__termination',
         'tenant__group', 'termination_a__termination', 'termination_z__termination',
@@ -397,6 +422,7 @@ class CircuitContactsView(ObjectContactsView):
 # Circuit terminations
 # Circuit terminations
 #
 #
 
 
+@register_model_view(CircuitTermination, 'list', path='', detail=False)
 class CircuitTerminationListView(generic.ObjectListView):
 class CircuitTerminationListView(generic.ObjectListView):
     queryset = CircuitTermination.objects.all()
     queryset = CircuitTermination.objects.all()
     filterset = filtersets.CircuitTerminationFilterSet
     filterset = filtersets.CircuitTerminationFilterSet
@@ -409,6 +435,7 @@ class CircuitTerminationView(generic.ObjectView):
     queryset = CircuitTermination.objects.all()
     queryset = CircuitTermination.objects.all()
 
 
 
 
+@register_model_view(CircuitTermination, 'add', detail=False)
 @register_model_view(CircuitTermination, 'edit')
 @register_model_view(CircuitTermination, 'edit')
 class CircuitTerminationEditView(generic.ObjectEditView):
 class CircuitTerminationEditView(generic.ObjectEditView):
     queryset = CircuitTermination.objects.all()
     queryset = CircuitTermination.objects.all()
@@ -420,11 +447,13 @@ class CircuitTerminationDeleteView(generic.ObjectDeleteView):
     queryset = CircuitTermination.objects.all()
     queryset = CircuitTermination.objects.all()
 
 
 
 
+@register_model_view(CircuitTermination, 'import', detail=False)
 class CircuitTerminationBulkImportView(generic.BulkImportView):
 class CircuitTerminationBulkImportView(generic.BulkImportView):
     queryset = CircuitTermination.objects.all()
     queryset = CircuitTermination.objects.all()
     model_form = forms.CircuitTerminationImportForm
     model_form = forms.CircuitTerminationImportForm
 
 
 
 
+@register_model_view(CircuitTermination, 'bulk_edit', path='edit', detail=False)
 class CircuitTerminationBulkEditView(generic.BulkEditView):
 class CircuitTerminationBulkEditView(generic.BulkEditView):
     queryset = CircuitTermination.objects.all()
     queryset = CircuitTermination.objects.all()
     filterset = filtersets.CircuitTerminationFilterSet
     filterset = filtersets.CircuitTerminationFilterSet
@@ -432,6 +461,7 @@ class CircuitTerminationBulkEditView(generic.BulkEditView):
     form = forms.CircuitTerminationBulkEditForm
     form = forms.CircuitTerminationBulkEditForm
 
 
 
 
+@register_model_view(CircuitTermination, 'bulk_delete', path='delete', detail=False)
 class CircuitTerminationBulkDeleteView(generic.BulkDeleteView):
 class CircuitTerminationBulkDeleteView(generic.BulkDeleteView):
     queryset = CircuitTermination.objects.all()
     queryset = CircuitTermination.objects.all()
     filterset = filtersets.CircuitTerminationFilterSet
     filterset = filtersets.CircuitTerminationFilterSet
@@ -446,6 +476,7 @@ register_model_view(CircuitTermination, 'trace', kwargs={'model': CircuitTermina
 # Circuit Groups
 # Circuit Groups
 #
 #
 
 
+@register_model_view(CircuitGroup, 'list', path='', detail=False)
 class CircuitGroupListView(generic.ObjectListView):
 class CircuitGroupListView(generic.ObjectListView):
     queryset = CircuitGroup.objects.annotate(
     queryset = CircuitGroup.objects.annotate(
         circuit_group_assignment_count=count_related(CircuitGroupAssignment, 'group')
         circuit_group_assignment_count=count_related(CircuitGroupAssignment, 'group')
@@ -465,6 +496,7 @@ class CircuitGroupView(GetRelatedModelsMixin, generic.ObjectView):
         }
         }
 
 
 
 
+@register_model_view(CircuitGroup, 'add', detail=False)
 @register_model_view(CircuitGroup, 'edit')
 @register_model_view(CircuitGroup, 'edit')
 class CircuitGroupEditView(generic.ObjectEditView):
 class CircuitGroupEditView(generic.ObjectEditView):
     queryset = CircuitGroup.objects.all()
     queryset = CircuitGroup.objects.all()
@@ -476,11 +508,13 @@ class CircuitGroupDeleteView(generic.ObjectDeleteView):
     queryset = CircuitGroup.objects.all()
     queryset = CircuitGroup.objects.all()
 
 
 
 
+@register_model_view(CircuitGroup, 'import', detail=False)
 class CircuitGroupBulkImportView(generic.BulkImportView):
 class CircuitGroupBulkImportView(generic.BulkImportView):
     queryset = CircuitGroup.objects.all()
     queryset = CircuitGroup.objects.all()
     model_form = forms.CircuitGroupImportForm
     model_form = forms.CircuitGroupImportForm
 
 
 
 
+@register_model_view(CircuitGroup, 'bulk_edit', path='edit', detail=False)
 class CircuitGroupBulkEditView(generic.BulkEditView):
 class CircuitGroupBulkEditView(generic.BulkEditView):
     queryset = CircuitGroup.objects.all()
     queryset = CircuitGroup.objects.all()
     filterset = filtersets.CircuitGroupFilterSet
     filterset = filtersets.CircuitGroupFilterSet
@@ -488,6 +522,7 @@ class CircuitGroupBulkEditView(generic.BulkEditView):
     form = forms.CircuitGroupBulkEditForm
     form = forms.CircuitGroupBulkEditForm
 
 
 
 
+@register_model_view(CircuitGroup, 'bulk_delete', path='delete', detail=False)
 class CircuitGroupBulkDeleteView(generic.BulkDeleteView):
 class CircuitGroupBulkDeleteView(generic.BulkDeleteView):
     queryset = CircuitGroup.objects.all()
     queryset = CircuitGroup.objects.all()
     filterset = filtersets.CircuitGroupFilterSet
     filterset = filtersets.CircuitGroupFilterSet
@@ -498,6 +533,7 @@ class CircuitGroupBulkDeleteView(generic.BulkDeleteView):
 # Circuit Groups
 # Circuit Groups
 #
 #
 
 
+@register_model_view(CircuitGroupAssignment, 'list', path='', detail=False)
 class CircuitGroupAssignmentListView(generic.ObjectListView):
 class CircuitGroupAssignmentListView(generic.ObjectListView):
     queryset = CircuitGroupAssignment.objects.all()
     queryset = CircuitGroupAssignment.objects.all()
     filterset = filtersets.CircuitGroupAssignmentFilterSet
     filterset = filtersets.CircuitGroupAssignmentFilterSet
@@ -510,6 +546,7 @@ class CircuitGroupAssignmentView(generic.ObjectView):
     queryset = CircuitGroupAssignment.objects.all()
     queryset = CircuitGroupAssignment.objects.all()
 
 
 
 
+@register_model_view(CircuitGroupAssignment, 'add', detail=False)
 @register_model_view(CircuitGroupAssignment, 'edit')
 @register_model_view(CircuitGroupAssignment, 'edit')
 class CircuitGroupAssignmentEditView(generic.ObjectEditView):
 class CircuitGroupAssignmentEditView(generic.ObjectEditView):
     queryset = CircuitGroupAssignment.objects.all()
     queryset = CircuitGroupAssignment.objects.all()
@@ -521,11 +558,13 @@ class CircuitGroupAssignmentDeleteView(generic.ObjectDeleteView):
     queryset = CircuitGroupAssignment.objects.all()
     queryset = CircuitGroupAssignment.objects.all()
 
 
 
 
+@register_model_view(CircuitGroupAssignment, 'import', detail=False)
 class CircuitGroupAssignmentBulkImportView(generic.BulkImportView):
 class CircuitGroupAssignmentBulkImportView(generic.BulkImportView):
     queryset = CircuitGroupAssignment.objects.all()
     queryset = CircuitGroupAssignment.objects.all()
     model_form = forms.CircuitGroupAssignmentImportForm
     model_form = forms.CircuitGroupAssignmentImportForm
 
 
 
 
+@register_model_view(CircuitGroupAssignment, 'bulk_edit', path='edit', detail=False)
 class CircuitGroupAssignmentBulkEditView(generic.BulkEditView):
 class CircuitGroupAssignmentBulkEditView(generic.BulkEditView):
     queryset = CircuitGroupAssignment.objects.all()
     queryset = CircuitGroupAssignment.objects.all()
     filterset = filtersets.CircuitGroupAssignmentFilterSet
     filterset = filtersets.CircuitGroupAssignmentFilterSet
@@ -533,6 +572,7 @@ class CircuitGroupAssignmentBulkEditView(generic.BulkEditView):
     form = forms.CircuitGroupAssignmentBulkEditForm
     form = forms.CircuitGroupAssignmentBulkEditForm
 
 
 
 
+@register_model_view(CircuitGroupAssignment, 'bulk_delete', path='delete', detail=False)
 class CircuitGroupAssignmentBulkDeleteView(generic.BulkDeleteView):
 class CircuitGroupAssignmentBulkDeleteView(generic.BulkDeleteView):
     queryset = CircuitGroupAssignment.objects.all()
     queryset = CircuitGroupAssignment.objects.all()
     filterset = filtersets.CircuitGroupAssignmentFilterSet
     filterset = filtersets.CircuitGroupAssignmentFilterSet

+ 6 - 23
netbox/core/urls.py

@@ -6,27 +6,16 @@ from . import views
 app_name = 'core'
 app_name = 'core'
 urlpatterns = (
 urlpatterns = (
 
 
-    # Data sources
-    path('data-sources/', views.DataSourceListView.as_view(), name='datasource_list'),
-    path('data-sources/add/', views.DataSourceEditView.as_view(), name='datasource_add'),
-    path('data-sources/import/', views.DataSourceBulkImportView.as_view(), name='datasource_import'),
-    path('data-sources/edit/', views.DataSourceBulkEditView.as_view(), name='datasource_bulk_edit'),
-    path('data-sources/delete/', views.DataSourceBulkDeleteView.as_view(), name='datasource_bulk_delete'),
+    path('data-sources/', include(get_model_urls('core', 'datasource', detail=False))),
     path('data-sources/<int:pk>/', include(get_model_urls('core', 'datasource'))),
     path('data-sources/<int:pk>/', include(get_model_urls('core', 'datasource'))),
 
 
-    # Data files
-    path('data-files/', views.DataFileListView.as_view(), name='datafile_list'),
-    path('data-files/delete/', views.DataFileBulkDeleteView.as_view(), name='datafile_bulk_delete'),
+    path('data-files/', include(get_model_urls('core', 'datafile', detail=False))),
     path('data-files/<int:pk>/', include(get_model_urls('core', 'datafile'))),
     path('data-files/<int:pk>/', include(get_model_urls('core', 'datafile'))),
 
 
-    # Job results
-    path('jobs/', views.JobListView.as_view(), name='job_list'),
-    path('jobs/delete/', views.JobBulkDeleteView.as_view(), name='job_bulk_delete'),
-    path('jobs/<int:pk>/', views.JobView.as_view(), name='job'),
-    path('jobs/<int:pk>/delete/', views.JobDeleteView.as_view(), name='job_delete'),
+    path('jobs/', include(get_model_urls('core', 'job', detail=False))),
+    path('jobs/<int:pk>/', include(get_model_urls('core', 'job'))),
 
 
-    # Change logging
-    path('changelog/', views.ObjectChangeListView.as_view(), name='objectchange_list'),
+    path('changelog/', include(get_model_urls('core', 'objectchange', detail=False))),
     path('changelog/<int:pk>/', include(get_model_urls('core', 'objectchange'))),
     path('changelog/<int:pk>/', include(get_model_urls('core', 'objectchange'))),
 
 
     # Background Tasks
     # Background Tasks
@@ -40,17 +29,11 @@ urlpatterns = (
     path('background-workers/<int:queue_index>/', views.WorkerListView.as_view(), name='worker_list'),
     path('background-workers/<int:queue_index>/', views.WorkerListView.as_view(), name='worker_list'),
     path('background-workers/<str:key>/', views.WorkerView.as_view(), name='worker'),
     path('background-workers/<str:key>/', views.WorkerView.as_view(), name='worker'),
 
 
-    # Config revisions
-    path('config-revisions/', views.ConfigRevisionListView.as_view(), name='configrevision_list'),
-    path('config-revisions/add/', views.ConfigRevisionEditView.as_view(), name='configrevision_add'),
-    path('config-revisions/delete/', views.ConfigRevisionBulkDeleteView.as_view(), name='configrevision_bulk_delete'),
-    path('config-revisions/<int:pk>/restore/', views.ConfigRevisionRestoreView.as_view(), name='configrevision_restore'),
+    path('config-revisions/', include(get_model_urls('core', 'configrevision', detail=False))),
     path('config-revisions/<int:pk>/', include(get_model_urls('core', 'configrevision'))),
     path('config-revisions/<int:pk>/', include(get_model_urls('core', 'configrevision'))),
 
 
-    # System
     path('system/', views.SystemView.as_view(), name='system'),
     path('system/', views.SystemView.as_view(), name='system'),
 
 
-    # Plugins
     path('plugins/', views.PluginListView.as_view(), name='plugin_list'),
     path('plugins/', views.PluginListView.as_view(), name='plugin_list'),
     path('plugins/<str:name>/', views.PluginView.as_view(), name='plugin'),
     path('plugins/<str:name>/', views.PluginView.as_view(), name='plugin'),
 )
 )

+ 16 - 0
netbox/core/views.py

@@ -46,6 +46,7 @@ from .tables import CatalogPluginTable, PluginVersionTable
 # Data sources
 # Data sources
 #
 #
 
 
+@register_model_view(DataSource, 'list', path='', detail=False)
 class DataSourceListView(generic.ObjectListView):
 class DataSourceListView(generic.ObjectListView):
     queryset = DataSource.objects.annotate(
     queryset = DataSource.objects.annotate(
         file_count=count_related(DataFile, 'source')
         file_count=count_related(DataFile, 'source')
@@ -92,6 +93,7 @@ class DataSourceSyncView(BaseObjectView):
         return redirect(datasource.get_absolute_url())
         return redirect(datasource.get_absolute_url())
 
 
 
 
+@register_model_view(DataSource, 'add', detail=False)
 @register_model_view(DataSource, 'edit')
 @register_model_view(DataSource, 'edit')
 class DataSourceEditView(generic.ObjectEditView):
 class DataSourceEditView(generic.ObjectEditView):
     queryset = DataSource.objects.all()
     queryset = DataSource.objects.all()
@@ -103,11 +105,13 @@ class DataSourceDeleteView(generic.ObjectDeleteView):
     queryset = DataSource.objects.all()
     queryset = DataSource.objects.all()
 
 
 
 
+@register_model_view(DataSource, 'import', detail=False)
 class DataSourceBulkImportView(generic.BulkImportView):
 class DataSourceBulkImportView(generic.BulkImportView):
     queryset = DataSource.objects.all()
     queryset = DataSource.objects.all()
     model_form = forms.DataSourceImportForm
     model_form = forms.DataSourceImportForm
 
 
 
 
+@register_model_view(DataSource, 'bulk_edit', path='edit', detail=False)
 class DataSourceBulkEditView(generic.BulkEditView):
 class DataSourceBulkEditView(generic.BulkEditView):
     queryset = DataSource.objects.annotate(
     queryset = DataSource.objects.annotate(
         count_files=count_related(DataFile, 'source')
         count_files=count_related(DataFile, 'source')
@@ -117,6 +121,7 @@ class DataSourceBulkEditView(generic.BulkEditView):
     form = forms.DataSourceBulkEditForm
     form = forms.DataSourceBulkEditForm
 
 
 
 
+@register_model_view(DataSource, 'bulk_delete', path='delete', detail=False)
 class DataSourceBulkDeleteView(generic.BulkDeleteView):
 class DataSourceBulkDeleteView(generic.BulkDeleteView):
     queryset = DataSource.objects.annotate(
     queryset = DataSource.objects.annotate(
         count_files=count_related(DataFile, 'source')
         count_files=count_related(DataFile, 'source')
@@ -129,6 +134,7 @@ class DataSourceBulkDeleteView(generic.BulkDeleteView):
 # Data files
 # Data files
 #
 #
 
 
+@register_model_view(DataFile, 'list', path='', detail=False)
 class DataFileListView(generic.ObjectListView):
 class DataFileListView(generic.ObjectListView):
     queryset = DataFile.objects.defer('data')
     queryset = DataFile.objects.defer('data')
     filterset = filtersets.DataFileFilterSet
     filterset = filtersets.DataFileFilterSet
@@ -149,6 +155,7 @@ class DataFileDeleteView(generic.ObjectDeleteView):
     queryset = DataFile.objects.all()
     queryset = DataFile.objects.all()
 
 
 
 
+@register_model_view(DataFile, 'bulk_delete', path='delete', detail=False)
 class DataFileBulkDeleteView(generic.BulkDeleteView):
 class DataFileBulkDeleteView(generic.BulkDeleteView):
     queryset = DataFile.objects.defer('data')
     queryset = DataFile.objects.defer('data')
     filterset = filtersets.DataFileFilterSet
     filterset = filtersets.DataFileFilterSet
@@ -159,6 +166,7 @@ class DataFileBulkDeleteView(generic.BulkDeleteView):
 # Jobs
 # Jobs
 #
 #
 
 
+@register_model_view(Job, 'list', path='', detail=False)
 class JobListView(generic.ObjectListView):
 class JobListView(generic.ObjectListView):
     queryset = Job.objects.all()
     queryset = Job.objects.all()
     filterset = filtersets.JobFilterSet
     filterset = filtersets.JobFilterSet
@@ -170,14 +178,17 @@ class JobListView(generic.ObjectListView):
     }
     }
 
 
 
 
+@register_model_view(Job)
 class JobView(generic.ObjectView):
 class JobView(generic.ObjectView):
     queryset = Job.objects.all()
     queryset = Job.objects.all()
 
 
 
 
+@register_model_view(Job, 'delete')
 class JobDeleteView(generic.ObjectDeleteView):
 class JobDeleteView(generic.ObjectDeleteView):
     queryset = Job.objects.all()
     queryset = Job.objects.all()
 
 
 
 
+@register_model_view(Job, 'bulk_delete', path='delete', detail=False)
 class JobBulkDeleteView(generic.BulkDeleteView):
 class JobBulkDeleteView(generic.BulkDeleteView):
     queryset = Job.objects.all()
     queryset = Job.objects.all()
     filterset = filtersets.JobFilterSet
     filterset = filtersets.JobFilterSet
@@ -188,6 +199,7 @@ class JobBulkDeleteView(generic.BulkDeleteView):
 # Change logging
 # Change logging
 #
 #
 
 
+@register_model_view(ObjectChange, 'list', path='', detail=False)
 class ObjectChangeListView(generic.ObjectListView):
 class ObjectChangeListView(generic.ObjectListView):
     queryset = ObjectChange.objects.valid_models()
     queryset = ObjectChange.objects.valid_models()
     filterset = filtersets.ObjectChangeFilterSet
     filterset = filtersets.ObjectChangeFilterSet
@@ -257,6 +269,7 @@ class ObjectChangeView(generic.ObjectView):
 # Config Revisions
 # Config Revisions
 #
 #
 
 
+@register_model_view(ConfigRevision, 'list', path='', detail=False)
 class ConfigRevisionListView(generic.ObjectListView):
 class ConfigRevisionListView(generic.ObjectListView):
     queryset = ConfigRevision.objects.all()
     queryset = ConfigRevision.objects.all()
     filterset = filtersets.ConfigRevisionFilterSet
     filterset = filtersets.ConfigRevisionFilterSet
@@ -269,6 +282,7 @@ class ConfigRevisionView(generic.ObjectView):
     queryset = ConfigRevision.objects.all()
     queryset = ConfigRevision.objects.all()
 
 
 
 
+@register_model_view(ConfigRevision, 'add', detail=False)
 class ConfigRevisionEditView(generic.ObjectEditView):
 class ConfigRevisionEditView(generic.ObjectEditView):
     queryset = ConfigRevision.objects.all()
     queryset = ConfigRevision.objects.all()
     form = forms.ConfigRevisionForm
     form = forms.ConfigRevisionForm
@@ -279,12 +293,14 @@ class ConfigRevisionDeleteView(generic.ObjectDeleteView):
     queryset = ConfigRevision.objects.all()
     queryset = ConfigRevision.objects.all()
 
 
 
 
+@register_model_view(ConfigRevision, 'bulk_delete', path='delete', detail=False)
 class ConfigRevisionBulkDeleteView(generic.BulkDeleteView):
 class ConfigRevisionBulkDeleteView(generic.BulkDeleteView):
     queryset = ConfigRevision.objects.all()
     queryset = ConfigRevision.objects.all()
     filterset = filtersets.ConfigRevisionFilterSet
     filterset = filtersets.ConfigRevisionFilterSet
     table = tables.ConfigRevisionTable
     table = tables.ConfigRevisionTable
 
 
 
 
+@register_model_view(ConfigRevision, 'restore')
 class ConfigRevisionRestoreView(ContentTypePermissionRequiredMixin, View):
 class ConfigRevisionRestoreView(ContentTypePermissionRequiredMixin, View):
 
 
     def get_required_permission(self):
     def get_required_permission(self):

+ 75 - 275
netbox/dcim/urls.py

@@ -6,335 +6,144 @@ from . import views
 app_name = 'dcim'
 app_name = 'dcim'
 urlpatterns = [
 urlpatterns = [
 
 
-    # Regions
-    path('regions/', views.RegionListView.as_view(), name='region_list'),
-    path('regions/add/', views.RegionEditView.as_view(), name='region_add'),
-    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/', include(get_model_urls('dcim', 'region', detail=False))),
     path('regions/<int:pk>/', include(get_model_urls('dcim', 'region'))),
     path('regions/<int:pk>/', include(get_model_urls('dcim', 'region'))),
 
 
-    # Site groups
-    path('site-groups/', views.SiteGroupListView.as_view(), name='sitegroup_list'),
-    path('site-groups/add/', views.SiteGroupEditView.as_view(), name='sitegroup_add'),
-    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/', include(get_model_urls('dcim', 'sitegroup', detail=False))),
     path('site-groups/<int:pk>/', include(get_model_urls('dcim', 'sitegroup'))),
     path('site-groups/<int:pk>/', include(get_model_urls('dcim', 'sitegroup'))),
 
 
-    # Sites
-    path('sites/', views.SiteListView.as_view(), name='site_list'),
-    path('sites/add/', views.SiteEditView.as_view(), name='site_add'),
-    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/', include(get_model_urls('dcim', 'site', detail=False))),
     path('sites/<int:pk>/', include(get_model_urls('dcim', 'site'))),
     path('sites/<int:pk>/', include(get_model_urls('dcim', 'site'))),
 
 
-    # Locations
-    path('locations/', views.LocationListView.as_view(), name='location_list'),
-    path('locations/add/', views.LocationEditView.as_view(), name='location_add'),
-    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/', include(get_model_urls('dcim', 'location', detail=False))),
     path('locations/<int:pk>/', include(get_model_urls('dcim', 'location'))),
     path('locations/<int:pk>/', include(get_model_urls('dcim', 'location'))),
 
 
-    # Rack roles
-    path('rack-roles/', views.RackRoleListView.as_view(), name='rackrole_list'),
-    path('rack-roles/add/', views.RackRoleEditView.as_view(), name='rackrole_add'),
-    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/', include(get_model_urls('dcim', 'rackrole', detail=False))),
     path('rack-roles/<int:pk>/', include(get_model_urls('dcim', 'rackrole'))),
     path('rack-roles/<int:pk>/', include(get_model_urls('dcim', 'rackrole'))),
 
 
-    # Rack reservations
-    path('rack-reservations/', views.RackReservationListView.as_view(), name='rackreservation_list'),
-    path('rack-reservations/add/', views.RackReservationEditView.as_view(), name='rackreservation_add'),
-    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/', include(get_model_urls('dcim', 'rackreservation', detail=False))),
     path('rack-reservations/<int:pk>/', include(get_model_urls('dcim', 'rackreservation'))),
     path('rack-reservations/<int:pk>/', include(get_model_urls('dcim', 'rackreservation'))),
 
 
-    # Racks
-    path('racks/', views.RackListView.as_view(), name='rack_list'),
-    path('rack-elevations/', views.RackElevationListView.as_view(), name='rack_elevation_list'),
-    path('racks/add/', views.RackEditView.as_view(), name='rack_add'),
-    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/', include(get_model_urls('dcim', 'rack', detail=False))),
     path('racks/<int:pk>/', include(get_model_urls('dcim', 'rack'))),
     path('racks/<int:pk>/', include(get_model_urls('dcim', 'rack'))),
+    path('rack-elevations/', views.RackElevationListView.as_view(), name='rack_elevation_list'),
 
 
-    # Rack Types
-    path('rack-types/', views.RackTypeListView.as_view(), name='racktype_list'),
-    path('rack-types/add/', views.RackTypeEditView.as_view(), name='racktype_add'),
-    path('rack-types/import/', views.RackTypeBulkImportView.as_view(), name='racktype_import'),
-    path('rack-types/edit/', views.RackTypeBulkEditView.as_view(), name='racktype_bulk_edit'),
-    path('rack-types/delete/', views.RackTypeBulkDeleteView.as_view(), name='racktype_bulk_delete'),
+    path('rack-types/', include(get_model_urls('dcim', 'racktype', detail=False))),
     path('rack-types/<int:pk>/', include(get_model_urls('dcim', 'racktype'))),
     path('rack-types/<int:pk>/', include(get_model_urls('dcim', 'racktype'))),
 
 
-    # Manufacturers
-    path('manufacturers/', views.ManufacturerListView.as_view(), name='manufacturer_list'),
-    path('manufacturers/add/', views.ManufacturerEditView.as_view(), name='manufacturer_add'),
-    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/', include(get_model_urls('dcim', 'manufacturer', detail=False))),
     path('manufacturers/<int:pk>/', include(get_model_urls('dcim', 'manufacturer'))),
     path('manufacturers/<int:pk>/', include(get_model_urls('dcim', 'manufacturer'))),
 
 
-    # Device types
-    path('device-types/', views.DeviceTypeListView.as_view(), name='devicetype_list'),
-    path('device-types/add/', views.DeviceTypeEditView.as_view(), name='devicetype_add'),
-    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/', include(get_model_urls('dcim', 'devicetype', detail=False))),
     path('device-types/<int:pk>/', include(get_model_urls('dcim', 'devicetype'))),
     path('device-types/<int:pk>/', include(get_model_urls('dcim', 'devicetype'))),
 
 
-    # Module types
-    path('module-types/', views.ModuleTypeListView.as_view(), name='moduletype_list'),
-    path('module-types/add/', views.ModuleTypeEditView.as_view(), name='moduletype_add'),
-    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/', include(get_model_urls('dcim', 'moduletype', detail=False))),
     path('module-types/<int:pk>/', include(get_model_urls('dcim', 'moduletype'))),
     path('module-types/<int:pk>/', include(get_model_urls('dcim', 'moduletype'))),
 
 
-    # Console port templates
-    path('console-port-templates/add/', views.ConsolePortTemplateCreateView.as_view(), name='consoleporttemplate_add'),
-    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/', include(get_model_urls('dcim', 'consoleporttemplate', detail=False))),
     path('console-port-templates/<int:pk>/', include(get_model_urls('dcim', 'consoleporttemplate'))),
     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/', include(get_model_urls('dcim', 'consoleserverporttemplate', detail=False))),
     path('console-server-port-templates/<int:pk>/', include(get_model_urls('dcim', 'consoleserverporttemplate'))),
     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/', include(get_model_urls('dcim', 'powerporttemplate', detail=False))),
     path('power-port-templates/<int:pk>/', include(get_model_urls('dcim', 'powerporttemplate'))),
     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/', include(get_model_urls('dcim', 'poweroutlettemplate', detail=False))),
     path('power-outlet-templates/<int:pk>/', include(get_model_urls('dcim', 'poweroutlettemplate'))),
     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/', include(get_model_urls('dcim', 'interfacetemplate', detail=False))),
     path('interface-templates/<int:pk>/', include(get_model_urls('dcim', 'interfacetemplate'))),
     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/', include(get_model_urls('dcim', 'frontporttemplate', detail=False))),
     path('front-port-templates/<int:pk>/', include(get_model_urls('dcim', 'frontporttemplate'))),
     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/', include(get_model_urls('dcim', 'rearporttemplate', detail=False))),
     path('rear-port-templates/<int:pk>/', include(get_model_urls('dcim', 'rearporttemplate'))),
     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/', include(get_model_urls('dcim', 'devicebaytemplate', detail=False))),
     path('device-bay-templates/<int:pk>/', include(get_model_urls('dcim', 'devicebaytemplate'))),
     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/', include(get_model_urls('dcim', 'modulebaytemplate', detail=False))),
     path('module-bay-templates/<int:pk>/', include(get_model_urls('dcim', 'modulebaytemplate'))),
     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/', include(get_model_urls('dcim', 'inventoryitemtemplate', detail=False))),
     path('inventory-item-templates/<int:pk>/', include(get_model_urls('dcim', 'inventoryitemtemplate'))),
     path('inventory-item-templates/<int:pk>/', include(get_model_urls('dcim', 'inventoryitemtemplate'))),
 
 
-    # Device roles
-    path('device-roles/', views.DeviceRoleListView.as_view(), name='devicerole_list'),
-    path('device-roles/add/', views.DeviceRoleEditView.as_view(), name='devicerole_add'),
-    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/', include(get_model_urls('dcim', 'devicerole', detail=False))),
     path('device-roles/<int:pk>/', include(get_model_urls('dcim', 'devicerole'))),
     path('device-roles/<int:pk>/', include(get_model_urls('dcim', 'devicerole'))),
 
 
-    # Platforms
-    path('platforms/', views.PlatformListView.as_view(), name='platform_list'),
-    path('platforms/add/', views.PlatformEditView.as_view(), name='platform_add'),
-    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/', include(get_model_urls('dcim', 'platform', detail=False))),
     path('platforms/<int:pk>/', include(get_model_urls('dcim', 'platform'))),
     path('platforms/<int:pk>/', include(get_model_urls('dcim', 'platform'))),
 
 
-    # Devices
-    path('devices/', views.DeviceListView.as_view(), name='device_list'),
-    path('devices/add/', views.DeviceEditView.as_view(), name='device_add'),
-    path('devices/import/', views.DeviceBulkImportView.as_view(), name='device_import'),
-    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/', include(get_model_urls('dcim', 'device', detail=False))),
     path('devices/<int:pk>/', include(get_model_urls('dcim', 'device'))),
     path('devices/<int:pk>/', include(get_model_urls('dcim', 'device'))),
 
 
-    # Virtual Device Context
-    path('virtual-device-contexts/', views.VirtualDeviceContextListView.as_view(), name='virtualdevicecontext_list'),
-    path('virtual-device-contexts/add/', views.VirtualDeviceContextEditView.as_view(), name='virtualdevicecontext_add'),
-    path('virtual-device-contexts/import/', views.VirtualDeviceContextBulkImportView.as_view(), name='virtualdevicecontext_import'),
-    path('virtual-device-contexts/edit/', views.VirtualDeviceContextBulkEditView.as_view(), name='virtualdevicecontext_bulk_edit'),
-    path('virtual-device-contexts/delete/', views.VirtualDeviceContextBulkDeleteView.as_view(), name='virtualdevicecontext_bulk_delete'),
+    path('virtual-device-contexts/', include(get_model_urls('dcim', 'virtualdevicecontext', detail=False))),
     path('virtual-device-contexts/<int:pk>/', include(get_model_urls('dcim', 'virtualdevicecontext'))),
     path('virtual-device-contexts/<int:pk>/', include(get_model_urls('dcim', 'virtualdevicecontext'))),
 
 
-    # Modules
-    path('modules/', views.ModuleListView.as_view(), name='module_list'),
-    path('modules/add/', views.ModuleEditView.as_view(), name='module_add'),
-    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/', include(get_model_urls('dcim', 'module', detail=False))),
     path('modules/<int:pk>/', include(get_model_urls('dcim', 'module'))),
     path('modules/<int:pk>/', include(get_model_urls('dcim', 'module'))),
 
 
-    # Console ports
-    path('console-ports/', views.ConsolePortListView.as_view(), name='consoleport_list'),
-    path('console-ports/add/', views.ConsolePortCreateView.as_view(), name='consoleport_add'),
-    path('console-ports/import/', views.ConsolePortBulkImportView.as_view(), name='consoleport_import'),
-    path('console-ports/edit/', views.ConsolePortBulkEditView.as_view(), name='consoleport_bulk_edit'),
-    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/', include(get_model_urls('dcim', 'consoleport', detail=False))),
     path('console-ports/<int:pk>/', include(get_model_urls('dcim', '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'),
-
-    # Console server ports
-    path('console-server-ports/', views.ConsoleServerPortListView.as_view(), name='consoleserverport_list'),
-    path('console-server-ports/add/', views.ConsoleServerPortCreateView.as_view(), name='consoleserverport_add'),
-    path('console-server-ports/import/', views.ConsoleServerPortBulkImportView.as_view(), name='consoleserverport_import'),
-    path('console-server-ports/edit/', views.ConsoleServerPortBulkEditView.as_view(), name='consoleserverport_bulk_edit'),
-    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(
+        'devices/console-ports/add/',
+        views.DeviceBulkAddConsolePortView.as_view(),
+        name='device_bulk_add_consoleport'
+    ),
+
+    path('console-server-ports/', include(get_model_urls('dcim', 'consoleserverport', detail=False))),
     path('console-server-ports/<int:pk>/', include(get_model_urls('dcim', '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'),
-
-    # Power ports
-    path('power-ports/', views.PowerPortListView.as_view(), name='powerport_list'),
-    path('power-ports/add/', views.PowerPortCreateView.as_view(), name='powerport_add'),
-    path('power-ports/import/', views.PowerPortBulkImportView.as_view(), name='powerport_import'),
-    path('power-ports/edit/', views.PowerPortBulkEditView.as_view(), name='powerport_bulk_edit'),
-    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(
+        'devices/console-server-ports/add/',
+        views.DeviceBulkAddConsoleServerPortView.as_view(),
+        name='device_bulk_add_consoleserverport'
+    ),
+
+    path('power-ports/', include(get_model_urls('dcim', 'powerport', detail=False))),
     path('power-ports/<int:pk>/', include(get_model_urls('dcim', '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'),
     path('devices/power-ports/add/', views.DeviceBulkAddPowerPortView.as_view(), name='device_bulk_add_powerport'),
 
 
-    # Power outlets
-    path('power-outlets/', views.PowerOutletListView.as_view(), name='poweroutlet_list'),
-    path('power-outlets/add/', views.PowerOutletCreateView.as_view(), name='poweroutlet_add'),
-    path('power-outlets/import/', views.PowerOutletBulkImportView.as_view(), name='poweroutlet_import'),
-    path('power-outlets/edit/', views.PowerOutletBulkEditView.as_view(), name='poweroutlet_bulk_edit'),
-    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/', include(get_model_urls('dcim', 'poweroutlet', detail=False))),
     path('power-outlets/<int:pk>/', include(get_model_urls('dcim', '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'),
-
-    # MAC addresses
-    path('mac-addresses/', views.MACAddressListView.as_view(), name='macaddress_list'),
-    path('mac-addresses/add/', views.MACAddressEditView.as_view(), name='macaddress_add'),
-    path('mac-addresses/import/', views.MACAddressBulkImportView.as_view(), name='macaddress_import'),
-    path('mac-addresses/edit/', views.MACAddressBulkEditView.as_view(), name='macaddress_bulk_edit'),
-    path('mac-addresses/delete/', views.MACAddressBulkDeleteView.as_view(), name='macaddress_bulk_delete'),
-    path('mac-addresses/<int:pk>/', include(get_model_urls('dcim', 'macaddress'))),
+    path(
+        'devices/power-outlets/add/',
+        views.DeviceBulkAddPowerOutletView.as_view(),
+        name='device_bulk_add_poweroutlet'
+    ),
 
 
-    # Interfaces
-    path('interfaces/', views.InterfaceListView.as_view(), name='interface_list'),
-    path('interfaces/add/', views.InterfaceCreateView.as_view(), name='interface_add'),
-    path('interfaces/import/', views.InterfaceBulkImportView.as_view(), name='interface_import'),
-    path('interfaces/edit/', views.InterfaceBulkEditView.as_view(), name='interface_bulk_edit'),
-    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/', include(get_model_urls('dcim', 'interface', detail=False))),
     path('interfaces/<int:pk>/', include(get_model_urls('dcim', 'interface'))),
     path('interfaces/<int:pk>/', include(get_model_urls('dcim', 'interface'))),
     path('devices/interfaces/add/', views.DeviceBulkAddInterfaceView.as_view(), name='device_bulk_add_interface'),
     path('devices/interfaces/add/', views.DeviceBulkAddInterfaceView.as_view(), name='device_bulk_add_interface'),
 
 
-    # Front ports
-    path('front-ports/', views.FrontPortListView.as_view(), name='frontport_list'),
-    path('front-ports/add/', views.FrontPortCreateView.as_view(), name='frontport_add'),
-    path('front-ports/import/', views.FrontPortBulkImportView.as_view(), name='frontport_import'),
-    path('front-ports/edit/', views.FrontPortBulkEditView.as_view(), name='frontport_bulk_edit'),
-    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/', include(get_model_urls('dcim', 'frontport', detail=False))),
     path('front-ports/<int:pk>/', include(get_model_urls('dcim', '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'),
-
-    # Rear ports
-    path('rear-ports/', views.RearPortListView.as_view(), name='rearport_list'),
-    path('rear-ports/add/', views.RearPortCreateView.as_view(), name='rearport_add'),
-    path('rear-ports/import/', views.RearPortBulkImportView.as_view(), name='rearport_import'),
-    path('rear-ports/edit/', views.RearPortBulkEditView.as_view(), name='rearport_bulk_edit'),
-    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/', include(get_model_urls('dcim', 'rearport', detail=False))),
     path('rear-ports/<int:pk>/', include(get_model_urls('dcim', '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'),
     path('devices/rear-ports/add/', views.DeviceBulkAddRearPortView.as_view(), name='device_bulk_add_rearport'),
 
 
-    # Module bays
-    path('module-bays/', views.ModuleBayListView.as_view(), name='modulebay_list'),
-    path('module-bays/add/', views.ModuleBayCreateView.as_view(), name='modulebay_add'),
-    path('module-bays/import/', views.ModuleBayBulkImportView.as_view(), name='modulebay_import'),
-    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/', include(get_model_urls('dcim', 'modulebay', detail=False))),
     path('module-bays/<int:pk>/', include(get_model_urls('dcim', 'modulebay'))),
     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'),
     path('devices/module-bays/add/', views.DeviceBulkAddModuleBayView.as_view(), name='device_bulk_add_modulebay'),
 
 
-    # Device bays
-    path('device-bays/', views.DeviceBayListView.as_view(), name='devicebay_list'),
-    path('device-bays/add/', views.DeviceBayCreateView.as_view(), name='devicebay_add'),
-    path('device-bays/import/', views.DeviceBayBulkImportView.as_view(), name='devicebay_import'),
-    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/', include(get_model_urls('dcim', 'devicebay', detail=False))),
     path('device-bays/<int:pk>/', include(get_model_urls('dcim', 'devicebay'))),
     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'),
     path('devices/device-bays/add/', views.DeviceBulkAddDeviceBayView.as_view(), name='device_bulk_add_devicebay'),
 
 
-    # Inventory items
-    path('inventory-items/', views.InventoryItemListView.as_view(), name='inventoryitem_list'),
-    path('inventory-items/add/', views.InventoryItemCreateView.as_view(), name='inventoryitem_add'),
-    path('inventory-items/import/', views.InventoryItemBulkImportView.as_view(), name='inventoryitem_import'),
-    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/', include(get_model_urls('dcim', 'inventoryitem', detail=False))),
     path('inventory-items/<int:pk>/', include(get_model_urls('dcim', 'inventoryitem'))),
     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'),
-
-    # Inventory item roles
-    path('inventory-item-roles/', views.InventoryItemRoleListView.as_view(), name='inventoryitemrole_list'),
-    path('inventory-item-roles/add/', views.InventoryItemRoleEditView.as_view(), name='inventoryitemrole_add'),
-    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(
+        'devices/inventory-items/add/',
+        views.DeviceBulkAddInventoryItemView.as_view(),
+        name='device_bulk_add_inventoryitem'
+    ),
+
+    path('inventory-item-roles/', include(get_model_urls('dcim', 'inventoryitemrole', detail=False))),
     path('inventory-item-roles/<int:pk>/', include(get_model_urls('dcim', 'inventoryitemrole'))),
     path('inventory-item-roles/<int:pk>/', include(get_model_urls('dcim', 'inventoryitemrole'))),
 
 
-    # Cables
-    path('cables/', views.CableListView.as_view(), name='cable_list'),
-    path('cables/add/', views.CableEditView.as_view(), name='cable_add'),
-    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/', include(get_model_urls('dcim', 'cable', detail=False))),
     path('cables/<int:pk>/', include(get_model_urls('dcim', 'cable'))),
     path('cables/<int:pk>/', include(get_model_urls('dcim', 'cable'))),
 
 
     # Console/power/interface connections (read-only)
     # Console/power/interface connections (read-only)
@@ -342,30 +151,21 @@ urlpatterns = [
     path('power-connections/', views.PowerConnectionsListView.as_view(), name='power_connections_list'),
     path('power-connections/', views.PowerConnectionsListView.as_view(), name='power_connections_list'),
     path('interface-connections/', views.InterfaceConnectionsListView.as_view(), name='interface_connections_list'),
     path('interface-connections/', views.InterfaceConnectionsListView.as_view(), name='interface_connections_list'),
 
 
-    # Virtual chassis
-    path('virtual-chassis/', views.VirtualChassisListView.as_view(), name='virtualchassis_list'),
-    path('virtual-chassis/add/', views.VirtualChassisCreateView.as_view(), name='virtualchassis_add'),
-    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/', include(get_model_urls('dcim', 'virtualchassis', detail=False))),
     path('virtual-chassis/<int:pk>/', include(get_model_urls('dcim', 'virtualchassis'))),
     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'),
-
-    # Power panels
-    path('power-panels/', views.PowerPanelListView.as_view(), name='powerpanel_list'),
-    path('power-panels/add/', views.PowerPanelEditView.as_view(), name='powerpanel_add'),
-    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(
+        'virtual-chassis-members/<int:pk>/delete/',
+        views.VirtualChassisRemoveMemberView.as_view(),
+        name='virtualchassis_remove_member'
+    ),
+
+    path('power-panels/', include(get_model_urls('dcim', 'powerpanel', detail=False))),
     path('power-panels/<int:pk>/', include(get_model_urls('dcim', 'powerpanel'))),
     path('power-panels/<int:pk>/', include(get_model_urls('dcim', 'powerpanel'))),
 
 
-    # Power feeds
-    path('power-feeds/', views.PowerFeedListView.as_view(), name='powerfeed_list'),
-    path('power-feeds/add/', views.PowerFeedEditView.as_view(), name='powerfeed_add'),
-    path('power-feeds/import/', views.PowerFeedBulkImportView.as_view(), name='powerfeed_import'),
-    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/', include(get_model_urls('dcim', 'powerfeed', detail=False))),
     path('power-feeds/<int:pk>/', include(get_model_urls('dcim', 'powerfeed'))),
     path('power-feeds/<int:pk>/', include(get_model_urls('dcim', 'powerfeed'))),
 
 
+    path('mac-addresses/', include(get_model_urls('dcim', 'macaddress', detail=False))),
+    path('mac-addresses/<int:pk>/', include(get_model_urls('dcim', 'macaddress'))),
+
 ]
 ]

File diff suppressed because it is too large
+ 137 - 0
netbox/dcim/views.py


+ 26 - 86
netbox/extras/urls.py

@@ -7,128 +7,68 @@ from utilities.urls import get_model_urls
 app_name = 'extras'
 app_name = 'extras'
 urlpatterns = [
 urlpatterns = [
 
 
-    # Custom fields
-    path('custom-fields/', views.CustomFieldListView.as_view(), name='customfield_list'),
-    path('custom-fields/add/', views.CustomFieldEditView.as_view(), name='customfield_add'),
-    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/', include(get_model_urls('extras', 'customfield', detail=False))),
     path('custom-fields/<int:pk>/', include(get_model_urls('extras', 'customfield'))),
     path('custom-fields/<int:pk>/', include(get_model_urls('extras', 'customfield'))),
 
 
-    # Custom field choices
-    path('custom-field-choices/', views.CustomFieldChoiceSetListView.as_view(), name='customfieldchoiceset_list'),
-    path('custom-field-choices/add/', views.CustomFieldChoiceSetEditView.as_view(), name='customfieldchoiceset_add'),
-    path('custom-field-choices/import/', views.CustomFieldChoiceSetBulkImportView.as_view(), name='customfieldchoiceset_import'),
-    path('custom-field-choices/edit/', views.CustomFieldChoiceSetBulkEditView.as_view(), name='customfieldchoiceset_bulk_edit'),
-    path('custom-field-choices/delete/', views.CustomFieldChoiceSetBulkDeleteView.as_view(), name='customfieldchoiceset_bulk_delete'),
+    path('custom-field-choices/', include(get_model_urls('extras', 'customfieldchoiceset', detail=False))),
     path('custom-field-choices/<int:pk>/', include(get_model_urls('extras', 'customfieldchoiceset'))),
     path('custom-field-choices/<int:pk>/', include(get_model_urls('extras', 'customfieldchoiceset'))),
 
 
-    # Custom links
-    path('custom-links/', views.CustomLinkListView.as_view(), name='customlink_list'),
-    path('custom-links/add/', views.CustomLinkEditView.as_view(), name='customlink_add'),
-    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/', include(get_model_urls('extras', 'customlink', detail=False))),
     path('custom-links/<int:pk>/', include(get_model_urls('extras', 'customlink'))),
     path('custom-links/<int:pk>/', include(get_model_urls('extras', 'customlink'))),
 
 
-    # Export templates
-    path('export-templates/', views.ExportTemplateListView.as_view(), name='exporttemplate_list'),
-    path('export-templates/add/', views.ExportTemplateEditView.as_view(), name='exporttemplate_add'),
-    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/sync/', views.ExportTemplateBulkSyncDataView.as_view(), name='exporttemplate_bulk_sync'),
+    path('export-templates/', include(get_model_urls('extras', 'exporttemplate', detail=False))),
     path('export-templates/<int:pk>/', include(get_model_urls('extras', 'exporttemplate'))),
     path('export-templates/<int:pk>/', include(get_model_urls('extras', 'exporttemplate'))),
 
 
-    # Saved filters
-    path('saved-filters/', views.SavedFilterListView.as_view(), name='savedfilter_list'),
-    path('saved-filters/add/', views.SavedFilterEditView.as_view(), name='savedfilter_add'),
-    path('saved-filters/import/', views.SavedFilterBulkImportView.as_view(), name='savedfilter_import'),
-    path('saved-filters/edit/', views.SavedFilterBulkEditView.as_view(), name='savedfilter_bulk_edit'),
-    path('saved-filters/delete/', views.SavedFilterBulkDeleteView.as_view(), name='savedfilter_bulk_delete'),
+    path('saved-filters/', include(get_model_urls('extras', 'savedfilter', detail=False))),
     path('saved-filters/<int:pk>/', include(get_model_urls('extras', 'savedfilter'))),
     path('saved-filters/<int:pk>/', include(get_model_urls('extras', 'savedfilter'))),
 
 
-    # Bookmarks
-    path('bookmarks/add/', views.BookmarkCreateView.as_view(), name='bookmark_add'),
-    path('bookmarks/delete/', views.BookmarkBulkDeleteView.as_view(), name='bookmark_bulk_delete'),
+    path('bookmarks/', include(get_model_urls('extras', 'bookmark', detail=False))),
     path('bookmarks/<int:pk>/', include(get_model_urls('extras', 'bookmark'))),
     path('bookmarks/<int:pk>/', include(get_model_urls('extras', 'bookmark'))),
 
 
-    # Notification groups
-    path('notification-groups/', views.NotificationGroupListView.as_view(), name='notificationgroup_list'),
-    path('notification-groups/add/', views.NotificationGroupEditView.as_view(), name='notificationgroup_add'),
-    path('notification-groups/import/', views.NotificationGroupBulkImportView.as_view(), name='notificationgroup_import'),
-    path('notification-groups/edit/', views.NotificationGroupBulkEditView.as_view(), name='notificationgroup_bulk_edit'),
-    path('notification-groups/delete/', views.NotificationGroupBulkDeleteView.as_view(), name='notificationgroup_bulk_delete'),
+    path('notification-groups/', include(get_model_urls('extras', 'notificationgroup', detail=False))),
     path('notification-groups/<int:pk>/', include(get_model_urls('extras', 'notificationgroup'))),
     path('notification-groups/<int:pk>/', include(get_model_urls('extras', 'notificationgroup'))),
 
 
-    # Notifications
     path('notifications/', views.NotificationsView.as_view(), name='notifications'),
     path('notifications/', views.NotificationsView.as_view(), name='notifications'),
-    path('notifications/delete/', views.NotificationBulkDeleteView.as_view(), name='notification_bulk_delete'),
+    path('notifications/', include(get_model_urls('extras', 'notification', detail=False))),
     path('notifications/<int:pk>/', include(get_model_urls('extras', 'notification'))),
     path('notifications/<int:pk>/', include(get_model_urls('extras', 'notification'))),
 
 
-    # Subscriptions
-    path('subscriptions/add/', views.SubscriptionCreateView.as_view(), name='subscription_add'),
-    path('subscriptions/delete/', views.SubscriptionBulkDeleteView.as_view(), name='subscription_bulk_delete'),
+    path('subscriptions/', include(get_model_urls('extras', 'subscription', detail=False))),
     path('subscriptions/<int:pk>/', include(get_model_urls('extras', 'subscription'))),
     path('subscriptions/<int:pk>/', include(get_model_urls('extras', 'subscription'))),
 
 
-    # Webhooks
-    path('webhooks/', views.WebhookListView.as_view(), name='webhook_list'),
-    path('webhooks/add/', views.WebhookEditView.as_view(), name='webhook_add'),
-    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/', include(get_model_urls('extras', 'webhook', detail=False))),
     path('webhooks/<int:pk>/', include(get_model_urls('extras', 'webhook'))),
     path('webhooks/<int:pk>/', include(get_model_urls('extras', 'webhook'))),
 
 
-    # Event rules
-    path('event-rules/', views.EventRuleListView.as_view(), name='eventrule_list'),
-    path('event-rules/add/', views.EventRuleEditView.as_view(), name='eventrule_add'),
-    path('event-rules/import/', views.EventRuleBulkImportView.as_view(), name='eventrule_import'),
-    path('event-rules/edit/', views.EventRuleBulkEditView.as_view(), name='eventrule_bulk_edit'),
-    path('event-rules/delete/', views.EventRuleBulkDeleteView.as_view(), name='eventrule_bulk_delete'),
+    path('event-rules/', include(get_model_urls('extras', 'eventrule', detail=False))),
     path('event-rules/<int:pk>/', include(get_model_urls('extras', 'eventrule'))),
     path('event-rules/<int:pk>/', include(get_model_urls('extras', 'eventrule'))),
 
 
-    # Tags
-    path('tags/', views.TagListView.as_view(), name='tag_list'),
-    path('tags/add/', views.TagEditView.as_view(), name='tag_add'),
-    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/', include(get_model_urls('extras', 'tag', detail=False))),
     path('tags/<int:pk>/', include(get_model_urls('extras', 'tag'))),
     path('tags/<int:pk>/', include(get_model_urls('extras', 'tag'))),
 
 
-    # Config contexts
-    path('config-contexts/', views.ConfigContextListView.as_view(), name='configcontext_list'),
-    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/sync/', views.ConfigContextBulkSyncDataView.as_view(), name='configcontext_bulk_sync'),
+    path('config-contexts/', include(get_model_urls('extras', 'configcontext', detail=False))),
     path('config-contexts/<int:pk>/', include(get_model_urls('extras', 'configcontext'))),
     path('config-contexts/<int:pk>/', include(get_model_urls('extras', 'configcontext'))),
 
 
-    # Config templates
-    path('config-templates/', views.ConfigTemplateListView.as_view(), name='configtemplate_list'),
-    path('config-templates/add/', views.ConfigTemplateEditView.as_view(), name='configtemplate_add'),
-    path('config-templates/edit/', views.ConfigTemplateBulkEditView.as_view(), name='configtemplate_bulk_edit'),
-    path('config-templates/delete/', views.ConfigTemplateBulkDeleteView.as_view(), name='configtemplate_bulk_delete'),
-    path('config-templates/sync/', views.ConfigTemplateBulkSyncDataView.as_view(), name='configtemplate_bulk_sync'),
+    path('config-templates/', include(get_model_urls('extras', 'configtemplate', detail=False))),
     path('config-templates/<int:pk>/', include(get_model_urls('extras', 'configtemplate'))),
     path('config-templates/<int:pk>/', include(get_model_urls('extras', 'configtemplate'))),
 
 
-    # Image attachments
-    path('image-attachments/', views.ImageAttachmentListView.as_view(), name='imageattachment_list'),
-    path('image-attachments/add/', views.ImageAttachmentEditView.as_view(), name='imageattachment_add'),
+    path('image-attachments/', include(get_model_urls('extras', 'imageattachment', detail=False))),
     path('image-attachments/<int:pk>/', include(get_model_urls('extras', 'imageattachment'))),
     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/import/', views.JournalEntryBulkImportView.as_view(), name='journalentry_import'),
+    path('journal-entries/', include(get_model_urls('extras', 'journalentry', detail=False))),
     path('journal-entries/<int:pk>/', include(get_model_urls('extras', 'journalentry'))),
     path('journal-entries/<int:pk>/', include(get_model_urls('extras', 'journalentry'))),
 
 
     # User dashboard
     # User dashboard
     path('dashboard/reset/', views.DashboardResetView.as_view(), name='dashboard_reset'),
     path('dashboard/reset/', views.DashboardResetView.as_view(), name='dashboard_reset'),
     path('dashboard/widgets/add/', views.DashboardWidgetAddView.as_view(), name='dashboardwidget_add'),
     path('dashboard/widgets/add/', views.DashboardWidgetAddView.as_view(), name='dashboardwidget_add'),
-    path('dashboard/widgets/<uuid:id>/configure/', views.DashboardWidgetConfigView.as_view(), name='dashboardwidget_config'),
-    path('dashboard/widgets/<uuid:id>/delete/', views.DashboardWidgetDeleteView.as_view(), name='dashboardwidget_delete'),
+    path(
+        'dashboard/widgets/<uuid:id>/configure/',
+        views.DashboardWidgetConfigView.as_view(),
+        name='dashboardwidget_config'
+    ),
+    path(
+        'dashboard/widgets/<uuid:id>/delete/',
+        views.DashboardWidgetDeleteView.as_view(),
+        name='dashboardwidget_delete'
+    ),
 
 
     # Scripts
     # Scripts
     path('scripts/', views.ScriptListView.as_view(), name='script_list'),
     path('scripts/', views.ScriptListView.as_view(), name='script_list'),

+ 79 - 10
netbox/extras/views.py

@@ -42,6 +42,7 @@ from .tables import ReportResultsTable, ScriptResultsTable
 # Custom fields
 # Custom fields
 #
 #
 
 
+@register_model_view(CustomField, 'list', path='', detail=False)
 class CustomFieldListView(generic.ObjectListView):
 class CustomFieldListView(generic.ObjectListView):
     queryset = CustomField.objects.select_related('choice_set')
     queryset = CustomField.objects.select_related('choice_set')
     filterset = filtersets.CustomFieldFilterSet
     filterset = filtersets.CustomFieldFilterSet
@@ -69,6 +70,7 @@ class CustomFieldView(generic.ObjectView):
         }
         }
 
 
 
 
+@register_model_view(CustomField, 'add', detail=False)
 @register_model_view(CustomField, 'edit')
 @register_model_view(CustomField, 'edit')
 class CustomFieldEditView(generic.ObjectEditView):
 class CustomFieldEditView(generic.ObjectEditView):
     queryset = CustomField.objects.select_related('choice_set')
     queryset = CustomField.objects.select_related('choice_set')
@@ -80,11 +82,13 @@ class CustomFieldDeleteView(generic.ObjectDeleteView):
     queryset = CustomField.objects.select_related('choice_set')
     queryset = CustomField.objects.select_related('choice_set')
 
 
 
 
+@register_model_view(CustomField, 'import', detail=False)
 class CustomFieldBulkImportView(generic.BulkImportView):
 class CustomFieldBulkImportView(generic.BulkImportView):
     queryset = CustomField.objects.select_related('choice_set')
     queryset = CustomField.objects.select_related('choice_set')
     model_form = forms.CustomFieldImportForm
     model_form = forms.CustomFieldImportForm
 
 
 
 
+@register_model_view(CustomField, 'bulk_edit', path='edit', detail=False)
 class CustomFieldBulkEditView(generic.BulkEditView):
 class CustomFieldBulkEditView(generic.BulkEditView):
     queryset = CustomField.objects.select_related('choice_set')
     queryset = CustomField.objects.select_related('choice_set')
     filterset = filtersets.CustomFieldFilterSet
     filterset = filtersets.CustomFieldFilterSet
@@ -92,6 +96,7 @@ class CustomFieldBulkEditView(generic.BulkEditView):
     form = forms.CustomFieldBulkEditForm
     form = forms.CustomFieldBulkEditForm
 
 
 
 
+@register_model_view(CustomField, 'bulk_delete', path='delete', detail=False)
 class CustomFieldBulkDeleteView(generic.BulkDeleteView):
 class CustomFieldBulkDeleteView(generic.BulkDeleteView):
     queryset = CustomField.objects.select_related('choice_set')
     queryset = CustomField.objects.select_related('choice_set')
     filterset = filtersets.CustomFieldFilterSet
     filterset = filtersets.CustomFieldFilterSet
@@ -102,6 +107,7 @@ class CustomFieldBulkDeleteView(generic.BulkDeleteView):
 # Custom field choices
 # Custom field choices
 #
 #
 
 
+@register_model_view(CustomFieldChoiceSet, 'list', path='', detail=False)
 class CustomFieldChoiceSetListView(generic.ObjectListView):
 class CustomFieldChoiceSetListView(generic.ObjectListView):
     queryset = CustomFieldChoiceSet.objects.all()
     queryset = CustomFieldChoiceSet.objects.all()
     filterset = filtersets.CustomFieldChoiceSetFilterSet
     filterset = filtersets.CustomFieldChoiceSetFilterSet
@@ -133,6 +139,7 @@ class CustomFieldChoiceSetView(generic.ObjectView):
         }
         }
 
 
 
 
+@register_model_view(CustomFieldChoiceSet, 'add', detail=False)
 @register_model_view(CustomFieldChoiceSet, 'edit')
 @register_model_view(CustomFieldChoiceSet, 'edit')
 class CustomFieldChoiceSetEditView(generic.ObjectEditView):
 class CustomFieldChoiceSetEditView(generic.ObjectEditView):
     queryset = CustomFieldChoiceSet.objects.all()
     queryset = CustomFieldChoiceSet.objects.all()
@@ -144,11 +151,13 @@ class CustomFieldChoiceSetDeleteView(generic.ObjectDeleteView):
     queryset = CustomFieldChoiceSet.objects.all()
     queryset = CustomFieldChoiceSet.objects.all()
 
 
 
 
+@register_model_view(CustomFieldChoiceSet, 'import', detail=False)
 class CustomFieldChoiceSetBulkImportView(generic.BulkImportView):
 class CustomFieldChoiceSetBulkImportView(generic.BulkImportView):
     queryset = CustomFieldChoiceSet.objects.all()
     queryset = CustomFieldChoiceSet.objects.all()
     model_form = forms.CustomFieldChoiceSetImportForm
     model_form = forms.CustomFieldChoiceSetImportForm
 
 
 
 
+@register_model_view(CustomFieldChoiceSet, 'bulk_edit', path='edit', detail=False)
 class CustomFieldChoiceSetBulkEditView(generic.BulkEditView):
 class CustomFieldChoiceSetBulkEditView(generic.BulkEditView):
     queryset = CustomFieldChoiceSet.objects.all()
     queryset = CustomFieldChoiceSet.objects.all()
     filterset = filtersets.CustomFieldChoiceSetFilterSet
     filterset = filtersets.CustomFieldChoiceSetFilterSet
@@ -156,6 +165,7 @@ class CustomFieldChoiceSetBulkEditView(generic.BulkEditView):
     form = forms.CustomFieldChoiceSetBulkEditForm
     form = forms.CustomFieldChoiceSetBulkEditForm
 
 
 
 
+@register_model_view(CustomFieldChoiceSet, 'bulk_delete', path='delete', detail=False)
 class CustomFieldChoiceSetBulkDeleteView(generic.BulkDeleteView):
 class CustomFieldChoiceSetBulkDeleteView(generic.BulkDeleteView):
     queryset = CustomFieldChoiceSet.objects.all()
     queryset = CustomFieldChoiceSet.objects.all()
     filterset = filtersets.CustomFieldChoiceSetFilterSet
     filterset = filtersets.CustomFieldChoiceSetFilterSet
@@ -166,6 +176,7 @@ class CustomFieldChoiceSetBulkDeleteView(generic.BulkDeleteView):
 # Custom links
 # Custom links
 #
 #
 
 
+@register_model_view(CustomLink, 'list', path='', detail=False)
 class CustomLinkListView(generic.ObjectListView):
 class CustomLinkListView(generic.ObjectListView):
     queryset = CustomLink.objects.all()
     queryset = CustomLink.objects.all()
     filterset = filtersets.CustomLinkFilterSet
     filterset = filtersets.CustomLinkFilterSet
@@ -178,6 +189,7 @@ class CustomLinkView(generic.ObjectView):
     queryset = CustomLink.objects.all()
     queryset = CustomLink.objects.all()
 
 
 
 
+@register_model_view(CustomLink, 'add', detail=False)
 @register_model_view(CustomLink, 'edit')
 @register_model_view(CustomLink, 'edit')
 class CustomLinkEditView(generic.ObjectEditView):
 class CustomLinkEditView(generic.ObjectEditView):
     queryset = CustomLink.objects.all()
     queryset = CustomLink.objects.all()
@@ -189,11 +201,13 @@ class CustomLinkDeleteView(generic.ObjectDeleteView):
     queryset = CustomLink.objects.all()
     queryset = CustomLink.objects.all()
 
 
 
 
+@register_model_view(CustomLink, 'import', detail=False)
 class CustomLinkBulkImportView(generic.BulkImportView):
 class CustomLinkBulkImportView(generic.BulkImportView):
     queryset = CustomLink.objects.all()
     queryset = CustomLink.objects.all()
     model_form = forms.CustomLinkImportForm
     model_form = forms.CustomLinkImportForm
 
 
 
 
+@register_model_view(CustomLink, 'bulk_edit', path='edit', detail=False)
 class CustomLinkBulkEditView(generic.BulkEditView):
 class CustomLinkBulkEditView(generic.BulkEditView):
     queryset = CustomLink.objects.all()
     queryset = CustomLink.objects.all()
     filterset = filtersets.CustomLinkFilterSet
     filterset = filtersets.CustomLinkFilterSet
@@ -201,6 +215,7 @@ class CustomLinkBulkEditView(generic.BulkEditView):
     form = forms.CustomLinkBulkEditForm
     form = forms.CustomLinkBulkEditForm
 
 
 
 
+@register_model_view(CustomLink, 'bulk_delete', path='delete', detail=False)
 class CustomLinkBulkDeleteView(generic.BulkDeleteView):
 class CustomLinkBulkDeleteView(generic.BulkDeleteView):
     queryset = CustomLink.objects.all()
     queryset = CustomLink.objects.all()
     filterset = filtersets.CustomLinkFilterSet
     filterset = filtersets.CustomLinkFilterSet
@@ -211,6 +226,7 @@ class CustomLinkBulkDeleteView(generic.BulkDeleteView):
 # Export templates
 # Export templates
 #
 #
 
 
+@register_model_view(ExportTemplate, 'list', path='', detail=False)
 class ExportTemplateListView(generic.ObjectListView):
 class ExportTemplateListView(generic.ObjectListView):
     queryset = ExportTemplate.objects.all()
     queryset = ExportTemplate.objects.all()
     filterset = filtersets.ExportTemplateFilterSet
     filterset = filtersets.ExportTemplateFilterSet
@@ -228,6 +244,7 @@ class ExportTemplateView(generic.ObjectView):
     queryset = ExportTemplate.objects.all()
     queryset = ExportTemplate.objects.all()
 
 
 
 
+@register_model_view(ExportTemplate, 'add', detail=False)
 @register_model_view(ExportTemplate, 'edit')
 @register_model_view(ExportTemplate, 'edit')
 class ExportTemplateEditView(generic.ObjectEditView):
 class ExportTemplateEditView(generic.ObjectEditView):
     queryset = ExportTemplate.objects.all()
     queryset = ExportTemplate.objects.all()
@@ -239,11 +256,13 @@ class ExportTemplateDeleteView(generic.ObjectDeleteView):
     queryset = ExportTemplate.objects.all()
     queryset = ExportTemplate.objects.all()
 
 
 
 
+@register_model_view(ExportTemplate, 'import', detail=False)
 class ExportTemplateBulkImportView(generic.BulkImportView):
 class ExportTemplateBulkImportView(generic.BulkImportView):
     queryset = ExportTemplate.objects.all()
     queryset = ExportTemplate.objects.all()
     model_form = forms.ExportTemplateImportForm
     model_form = forms.ExportTemplateImportForm
 
 
 
 
+@register_model_view(ExportTemplate, 'bulk_edit', path='edit', detail=False)
 class ExportTemplateBulkEditView(generic.BulkEditView):
 class ExportTemplateBulkEditView(generic.BulkEditView):
     queryset = ExportTemplate.objects.all()
     queryset = ExportTemplate.objects.all()
     filterset = filtersets.ExportTemplateFilterSet
     filterset = filtersets.ExportTemplateFilterSet
@@ -251,12 +270,14 @@ class ExportTemplateBulkEditView(generic.BulkEditView):
     form = forms.ExportTemplateBulkEditForm
     form = forms.ExportTemplateBulkEditForm
 
 
 
 
+@register_model_view(ExportTemplate, 'bulk_delete', path='delete', detail=False)
 class ExportTemplateBulkDeleteView(generic.BulkDeleteView):
 class ExportTemplateBulkDeleteView(generic.BulkDeleteView):
     queryset = ExportTemplate.objects.all()
     queryset = ExportTemplate.objects.all()
     filterset = filtersets.ExportTemplateFilterSet
     filterset = filtersets.ExportTemplateFilterSet
     table = tables.ExportTemplateTable
     table = tables.ExportTemplateTable
 
 
 
 
+@register_model_view(ExportTemplate, 'bulk_sync', path='sync', detail=False)
 class ExportTemplateBulkSyncDataView(generic.BulkSyncDataView):
 class ExportTemplateBulkSyncDataView(generic.BulkSyncDataView):
     queryset = ExportTemplate.objects.all()
     queryset = ExportTemplate.objects.all()
 
 
@@ -283,6 +304,7 @@ class SavedFilterMixin:
         )
         )
 
 
 
 
+@register_model_view(SavedFilter, 'list', path='', detail=False)
 class SavedFilterListView(SavedFilterMixin, generic.ObjectListView):
 class SavedFilterListView(SavedFilterMixin, generic.ObjectListView):
     filterset = filtersets.SavedFilterFilterSet
     filterset = filtersets.SavedFilterFilterSet
     filterset_form = forms.SavedFilterFilterForm
     filterset_form = forms.SavedFilterFilterForm
@@ -294,6 +316,7 @@ class SavedFilterView(SavedFilterMixin, generic.ObjectView):
     queryset = SavedFilter.objects.all()
     queryset = SavedFilter.objects.all()
 
 
 
 
+@register_model_view(SavedFilter, 'add', detail=False)
 @register_model_view(SavedFilter, 'edit')
 @register_model_view(SavedFilter, 'edit')
 class SavedFilterEditView(SavedFilterMixin, generic.ObjectEditView):
 class SavedFilterEditView(SavedFilterMixin, generic.ObjectEditView):
     queryset = SavedFilter.objects.all()
     queryset = SavedFilter.objects.all()
@@ -310,11 +333,13 @@ class SavedFilterDeleteView(SavedFilterMixin, generic.ObjectDeleteView):
     queryset = SavedFilter.objects.all()
     queryset = SavedFilter.objects.all()
 
 
 
 
+@register_model_view(SavedFilter, 'import', detail=False)
 class SavedFilterBulkImportView(SavedFilterMixin, generic.BulkImportView):
 class SavedFilterBulkImportView(SavedFilterMixin, generic.BulkImportView):
     queryset = SavedFilter.objects.all()
     queryset = SavedFilter.objects.all()
     model_form = forms.SavedFilterImportForm
     model_form = forms.SavedFilterImportForm
 
 
 
 
+@register_model_view(SavedFilter, 'bulk_edit', path='edit', detail=False)
 class SavedFilterBulkEditView(SavedFilterMixin, generic.BulkEditView):
 class SavedFilterBulkEditView(SavedFilterMixin, generic.BulkEditView):
     queryset = SavedFilter.objects.all()
     queryset = SavedFilter.objects.all()
     filterset = filtersets.SavedFilterFilterSet
     filterset = filtersets.SavedFilterFilterSet
@@ -322,6 +347,7 @@ class SavedFilterBulkEditView(SavedFilterMixin, generic.BulkEditView):
     form = forms.SavedFilterBulkEditForm
     form = forms.SavedFilterBulkEditForm
 
 
 
 
+@register_model_view(SavedFilter, 'bulk_delete', path='delete', detail=False)
 class SavedFilterBulkDeleteView(SavedFilterMixin, generic.BulkDeleteView):
 class SavedFilterBulkDeleteView(SavedFilterMixin, generic.BulkDeleteView):
     queryset = SavedFilter.objects.all()
     queryset = SavedFilter.objects.all()
     filterset = filtersets.SavedFilterFilterSet
     filterset = filtersets.SavedFilterFilterSet
@@ -332,6 +358,7 @@ class SavedFilterBulkDeleteView(SavedFilterMixin, generic.BulkDeleteView):
 # Bookmarks
 # Bookmarks
 #
 #
 
 
+@register_model_view(Bookmark, 'add', detail=False)
 class BookmarkCreateView(generic.ObjectEditView):
 class BookmarkCreateView(generic.ObjectEditView):
     form = forms.BookmarkForm
     form = forms.BookmarkForm
 
 
@@ -350,6 +377,7 @@ class BookmarkDeleteView(generic.ObjectDeleteView):
         return Bookmark.objects.filter(user=request.user)
         return Bookmark.objects.filter(user=request.user)
 
 
 
 
+@register_model_view(Bookmark, 'bulk_delete', path='delete', detail=False)
 class BookmarkBulkDeleteView(generic.BulkDeleteView):
 class BookmarkBulkDeleteView(generic.BulkDeleteView):
     table = tables.BookmarkTable
     table = tables.BookmarkTable
 
 
@@ -361,6 +389,7 @@ class BookmarkBulkDeleteView(generic.BulkDeleteView):
 # Notification groups
 # Notification groups
 #
 #
 
 
+@register_model_view(NotificationGroup, 'list', path='', detail=False)
 class NotificationGroupListView(generic.ObjectListView):
 class NotificationGroupListView(generic.ObjectListView):
     queryset = NotificationGroup.objects.all()
     queryset = NotificationGroup.objects.all()
     filterset = filtersets.NotificationGroupFilterSet
     filterset = filtersets.NotificationGroupFilterSet
@@ -373,6 +402,7 @@ class NotificationGroupView(generic.ObjectView):
     queryset = NotificationGroup.objects.all()
     queryset = NotificationGroup.objects.all()
 
 
 
 
+@register_model_view(NotificationGroup, 'add', detail=False)
 @register_model_view(NotificationGroup, 'edit')
 @register_model_view(NotificationGroup, 'edit')
 class NotificationGroupEditView(generic.ObjectEditView):
 class NotificationGroupEditView(generic.ObjectEditView):
     queryset = NotificationGroup.objects.all()
     queryset = NotificationGroup.objects.all()
@@ -384,11 +414,13 @@ class NotificationGroupDeleteView(generic.ObjectDeleteView):
     queryset = NotificationGroup.objects.all()
     queryset = NotificationGroup.objects.all()
 
 
 
 
+@register_model_view(NotificationGroup, 'import', detail=False)
 class NotificationGroupBulkImportView(generic.BulkImportView):
 class NotificationGroupBulkImportView(generic.BulkImportView):
     queryset = NotificationGroup.objects.all()
     queryset = NotificationGroup.objects.all()
     model_form = forms.NotificationGroupImportForm
     model_form = forms.NotificationGroupImportForm
 
 
 
 
+@register_model_view(NotificationGroup, 'bulk_edit', path='edit', detail=False)
 class NotificationGroupBulkEditView(generic.BulkEditView):
 class NotificationGroupBulkEditView(generic.BulkEditView):
     queryset = NotificationGroup.objects.all()
     queryset = NotificationGroup.objects.all()
     filterset = filtersets.NotificationGroupFilterSet
     filterset = filtersets.NotificationGroupFilterSet
@@ -396,6 +428,7 @@ class NotificationGroupBulkEditView(generic.BulkEditView):
     form = forms.NotificationGroupBulkEditForm
     form = forms.NotificationGroupBulkEditForm
 
 
 
 
+@register_model_view(NotificationGroup, 'bulk_delete', path='delete', detail=False)
 class NotificationGroupBulkDeleteView(generic.BulkDeleteView):
 class NotificationGroupBulkDeleteView(generic.BulkDeleteView):
     queryset = NotificationGroup.objects.all()
     queryset = NotificationGroup.objects.all()
     filterset = filtersets.NotificationGroupFilterSet
     filterset = filtersets.NotificationGroupFilterSet
@@ -459,6 +492,7 @@ class NotificationDeleteView(generic.ObjectDeleteView):
         return Notification.objects.filter(user=request.user)
         return Notification.objects.filter(user=request.user)
 
 
 
 
+@register_model_view(Notification, 'bulk_delete', path='delete', detail=False)
 class NotificationBulkDeleteView(generic.BulkDeleteView):
 class NotificationBulkDeleteView(generic.BulkDeleteView):
     table = tables.NotificationTable
     table = tables.NotificationTable
 
 
@@ -470,6 +504,7 @@ class NotificationBulkDeleteView(generic.BulkDeleteView):
 # Subscriptions
 # Subscriptions
 #
 #
 
 
+@register_model_view(Subscription, 'add', detail=False)
 class SubscriptionCreateView(generic.ObjectEditView):
 class SubscriptionCreateView(generic.ObjectEditView):
     form = forms.SubscriptionForm
     form = forms.SubscriptionForm
 
 
@@ -488,6 +523,7 @@ class SubscriptionDeleteView(generic.ObjectDeleteView):
         return Subscription.objects.filter(user=request.user)
         return Subscription.objects.filter(user=request.user)
 
 
 
 
+@register_model_view(Subscription, 'bulk_delete', path='delete', detail=False)
 class SubscriptionBulkDeleteView(generic.BulkDeleteView):
 class SubscriptionBulkDeleteView(generic.BulkDeleteView):
     table = tables.SubscriptionTable
     table = tables.SubscriptionTable
 
 
@@ -499,6 +535,7 @@ class SubscriptionBulkDeleteView(generic.BulkDeleteView):
 # Webhooks
 # Webhooks
 #
 #
 
 
+@register_model_view(Webhook, 'list', path='', detail=False)
 class WebhookListView(generic.ObjectListView):
 class WebhookListView(generic.ObjectListView):
     queryset = Webhook.objects.all()
     queryset = Webhook.objects.all()
     filterset = filtersets.WebhookFilterSet
     filterset = filtersets.WebhookFilterSet
@@ -511,6 +548,7 @@ class WebhookView(generic.ObjectView):
     queryset = Webhook.objects.all()
     queryset = Webhook.objects.all()
 
 
 
 
+@register_model_view(Webhook, 'add', detail=False)
 @register_model_view(Webhook, 'edit')
 @register_model_view(Webhook, 'edit')
 class WebhookEditView(generic.ObjectEditView):
 class WebhookEditView(generic.ObjectEditView):
     queryset = Webhook.objects.all()
     queryset = Webhook.objects.all()
@@ -522,11 +560,13 @@ class WebhookDeleteView(generic.ObjectDeleteView):
     queryset = Webhook.objects.all()
     queryset = Webhook.objects.all()
 
 
 
 
+@register_model_view(Webhook, 'import', detail=False)
 class WebhookBulkImportView(generic.BulkImportView):
 class WebhookBulkImportView(generic.BulkImportView):
     queryset = Webhook.objects.all()
     queryset = Webhook.objects.all()
     model_form = forms.WebhookImportForm
     model_form = forms.WebhookImportForm
 
 
 
 
+@register_model_view(Webhook, 'bulk_edit', path='edit', detail=False)
 class WebhookBulkEditView(generic.BulkEditView):
 class WebhookBulkEditView(generic.BulkEditView):
     queryset = Webhook.objects.all()
     queryset = Webhook.objects.all()
     filterset = filtersets.WebhookFilterSet
     filterset = filtersets.WebhookFilterSet
@@ -534,6 +574,7 @@ class WebhookBulkEditView(generic.BulkEditView):
     form = forms.WebhookBulkEditForm
     form = forms.WebhookBulkEditForm
 
 
 
 
+@register_model_view(Webhook, 'bulk_delete', path='delete', detail=False)
 class WebhookBulkDeleteView(generic.BulkDeleteView):
 class WebhookBulkDeleteView(generic.BulkDeleteView):
     queryset = Webhook.objects.all()
     queryset = Webhook.objects.all()
     filterset = filtersets.WebhookFilterSet
     filterset = filtersets.WebhookFilterSet
@@ -544,6 +585,7 @@ class WebhookBulkDeleteView(generic.BulkDeleteView):
 # Event Rules
 # Event Rules
 #
 #
 
 
+@register_model_view(EventRule, 'list', path='', detail=False)
 class EventRuleListView(generic.ObjectListView):
 class EventRuleListView(generic.ObjectListView):
     queryset = EventRule.objects.all()
     queryset = EventRule.objects.all()
     filterset = filtersets.EventRuleFilterSet
     filterset = filtersets.EventRuleFilterSet
@@ -556,6 +598,7 @@ class EventRuleView(generic.ObjectView):
     queryset = EventRule.objects.all()
     queryset = EventRule.objects.all()
 
 
 
 
+@register_model_view(EventRule, 'add', detail=False)
 @register_model_view(EventRule, 'edit')
 @register_model_view(EventRule, 'edit')
 class EventRuleEditView(generic.ObjectEditView):
 class EventRuleEditView(generic.ObjectEditView):
     queryset = EventRule.objects.all()
     queryset = EventRule.objects.all()
@@ -567,11 +610,13 @@ class EventRuleDeleteView(generic.ObjectDeleteView):
     queryset = EventRule.objects.all()
     queryset = EventRule.objects.all()
 
 
 
 
+@register_model_view(EventRule, 'import', detail=False)
 class EventRuleBulkImportView(generic.BulkImportView):
 class EventRuleBulkImportView(generic.BulkImportView):
     queryset = EventRule.objects.all()
     queryset = EventRule.objects.all()
     model_form = forms.EventRuleImportForm
     model_form = forms.EventRuleImportForm
 
 
 
 
+@register_model_view(EventRule, 'bulk_edit', path='edit', detail=False)
 class EventRuleBulkEditView(generic.BulkEditView):
 class EventRuleBulkEditView(generic.BulkEditView):
     queryset = EventRule.objects.all()
     queryset = EventRule.objects.all()
     filterset = filtersets.EventRuleFilterSet
     filterset = filtersets.EventRuleFilterSet
@@ -579,6 +624,7 @@ class EventRuleBulkEditView(generic.BulkEditView):
     form = forms.EventRuleBulkEditForm
     form = forms.EventRuleBulkEditForm
 
 
 
 
+@register_model_view(EventRule, 'bulk_delete', path='delete', detail=False)
 class EventRuleBulkDeleteView(generic.BulkDeleteView):
 class EventRuleBulkDeleteView(generic.BulkDeleteView):
     queryset = EventRule.objects.all()
     queryset = EventRule.objects.all()
     filterset = filtersets.EventRuleFilterSet
     filterset = filtersets.EventRuleFilterSet
@@ -589,6 +635,7 @@ class EventRuleBulkDeleteView(generic.BulkDeleteView):
 # Tags
 # Tags
 #
 #
 
 
+@register_model_view(Tag, 'list', path='', detail=False)
 class TagListView(generic.ObjectListView):
 class TagListView(generic.ObjectListView):
     queryset = Tag.objects.annotate(
     queryset = Tag.objects.annotate(
         items=count_related(TaggedItem, 'tag')
         items=count_related(TaggedItem, 'tag')
@@ -624,6 +671,7 @@ class TagView(generic.ObjectView):
         }
         }
 
 
 
 
+@register_model_view(Tag, 'add', detail=False)
 @register_model_view(Tag, 'edit')
 @register_model_view(Tag, 'edit')
 class TagEditView(generic.ObjectEditView):
 class TagEditView(generic.ObjectEditView):
     queryset = Tag.objects.all()
     queryset = Tag.objects.all()
@@ -635,11 +683,13 @@ class TagDeleteView(generic.ObjectDeleteView):
     queryset = Tag.objects.all()
     queryset = Tag.objects.all()
 
 
 
 
+@register_model_view(Tag, 'import', detail=False)
 class TagBulkImportView(generic.BulkImportView):
 class TagBulkImportView(generic.BulkImportView):
     queryset = Tag.objects.all()
     queryset = Tag.objects.all()
     model_form = forms.TagImportForm
     model_form = forms.TagImportForm
 
 
 
 
+@register_model_view(Tag, 'bulk_edit', path='edit', detail=False)
 class TagBulkEditView(generic.BulkEditView):
 class TagBulkEditView(generic.BulkEditView):
     queryset = Tag.objects.annotate(
     queryset = Tag.objects.annotate(
         items=count_related(TaggedItem, 'tag')
         items=count_related(TaggedItem, 'tag')
@@ -648,6 +698,7 @@ class TagBulkEditView(generic.BulkEditView):
     form = forms.TagBulkEditForm
     form = forms.TagBulkEditForm
 
 
 
 
+@register_model_view(Tag, 'bulk_delete', path='delete', detail=False)
 class TagBulkDeleteView(generic.BulkDeleteView):
 class TagBulkDeleteView(generic.BulkDeleteView):
     queryset = Tag.objects.annotate(
     queryset = Tag.objects.annotate(
         items=count_related(TaggedItem, 'tag')
         items=count_related(TaggedItem, 'tag')
@@ -659,6 +710,7 @@ class TagBulkDeleteView(generic.BulkDeleteView):
 # Config contexts
 # Config contexts
 #
 #
 
 
+@register_model_view(ConfigContext, 'list', path='', detail=False)
 class ConfigContextListView(generic.ObjectListView):
 class ConfigContextListView(generic.ObjectListView):
     queryset = ConfigContext.objects.all()
     queryset = ConfigContext.objects.all()
     filterset = filtersets.ConfigContextFilterSet
     filterset = filtersets.ConfigContextFilterSet
@@ -711,12 +763,19 @@ class ConfigContextView(generic.ObjectView):
         }
         }
 
 
 
 
+@register_model_view(ConfigContext, 'add', detail=False)
 @register_model_view(ConfigContext, 'edit')
 @register_model_view(ConfigContext, 'edit')
 class ConfigContextEditView(generic.ObjectEditView):
 class ConfigContextEditView(generic.ObjectEditView):
     queryset = ConfigContext.objects.all()
     queryset = ConfigContext.objects.all()
     form = forms.ConfigContextForm
     form = forms.ConfigContextForm
 
 
 
 
+@register_model_view(ConfigContext, 'delete')
+class ConfigContextDeleteView(generic.ObjectDeleteView):
+    queryset = ConfigContext.objects.all()
+
+
+@register_model_view(ConfigContext, 'bulk_edit', path='edit', detail=False)
 class ConfigContextBulkEditView(generic.BulkEditView):
 class ConfigContextBulkEditView(generic.BulkEditView):
     queryset = ConfigContext.objects.all()
     queryset = ConfigContext.objects.all()
     filterset = filtersets.ConfigContextFilterSet
     filterset = filtersets.ConfigContextFilterSet
@@ -724,17 +783,14 @@ class ConfigContextBulkEditView(generic.BulkEditView):
     form = forms.ConfigContextBulkEditForm
     form = forms.ConfigContextBulkEditForm
 
 
 
 
-@register_model_view(ConfigContext, 'delete')
-class ConfigContextDeleteView(generic.ObjectDeleteView):
-    queryset = ConfigContext.objects.all()
-
-
+@register_model_view(ConfigContext, 'bulk_delete', path='delete', detail=False)
 class ConfigContextBulkDeleteView(generic.BulkDeleteView):
 class ConfigContextBulkDeleteView(generic.BulkDeleteView):
     queryset = ConfigContext.objects.all()
     queryset = ConfigContext.objects.all()
     filterset = filtersets.ConfigContextFilterSet
     filterset = filtersets.ConfigContextFilterSet
     table = tables.ConfigContextTable
     table = tables.ConfigContextTable
 
 
 
 
+@register_model_view(ConfigContext, 'bulk_sync', path='sync', detail=False)
 class ConfigContextBulkSyncDataView(generic.BulkSyncDataView):
 class ConfigContextBulkSyncDataView(generic.BulkSyncDataView):
     queryset = ConfigContext.objects.all()
     queryset = ConfigContext.objects.all()
 
 
@@ -768,6 +824,7 @@ class ObjectConfigContextView(generic.ObjectView):
 # Config templates
 # Config templates
 #
 #
 
 
+@register_model_view(ConfigTemplate, 'list', path='', detail=False)
 class ConfigTemplateListView(generic.ObjectListView):
 class ConfigTemplateListView(generic.ObjectListView):
     queryset = ConfigTemplate.objects.annotate(
     queryset = ConfigTemplate.objects.annotate(
         device_count=count_related(Device, 'config_template'),
         device_count=count_related(Device, 'config_template'),
@@ -790,6 +847,7 @@ class ConfigTemplateView(generic.ObjectView):
     queryset = ConfigTemplate.objects.all()
     queryset = ConfigTemplate.objects.all()
 
 
 
 
+@register_model_view(ConfigTemplate, 'add', detail=False)
 @register_model_view(ConfigTemplate, 'edit')
 @register_model_view(ConfigTemplate, 'edit')
 class ConfigTemplateEditView(generic.ObjectEditView):
 class ConfigTemplateEditView(generic.ObjectEditView):
     queryset = ConfigTemplate.objects.all()
     queryset = ConfigTemplate.objects.all()
@@ -801,11 +859,13 @@ class ConfigTemplateDeleteView(generic.ObjectDeleteView):
     queryset = ConfigTemplate.objects.all()
     queryset = ConfigTemplate.objects.all()
 
 
 
 
+@register_model_view(ConfigTemplate, 'import', detail=False)
 class ConfigTemplateBulkImportView(generic.BulkImportView):
 class ConfigTemplateBulkImportView(generic.BulkImportView):
     queryset = ConfigTemplate.objects.all()
     queryset = ConfigTemplate.objects.all()
     model_form = forms.ConfigTemplateImportForm
     model_form = forms.ConfigTemplateImportForm
 
 
 
 
+@register_model_view(ConfigTemplate, 'bulk_edit', path='edit', detail=False)
 class ConfigTemplateBulkEditView(generic.BulkEditView):
 class ConfigTemplateBulkEditView(generic.BulkEditView):
     queryset = ConfigTemplate.objects.all()
     queryset = ConfigTemplate.objects.all()
     filterset = filtersets.ConfigTemplateFilterSet
     filterset = filtersets.ConfigTemplateFilterSet
@@ -813,12 +873,14 @@ class ConfigTemplateBulkEditView(generic.BulkEditView):
     form = forms.ConfigTemplateBulkEditForm
     form = forms.ConfigTemplateBulkEditForm
 
 
 
 
+@register_model_view(ConfigTemplate, 'bulk_delete', path='delete', detail=False)
 class ConfigTemplateBulkDeleteView(generic.BulkDeleteView):
 class ConfigTemplateBulkDeleteView(generic.BulkDeleteView):
     queryset = ConfigTemplate.objects.all()
     queryset = ConfigTemplate.objects.all()
     filterset = filtersets.ConfigTemplateFilterSet
     filterset = filtersets.ConfigTemplateFilterSet
     table = tables.ConfigTemplateTable
     table = tables.ConfigTemplateTable
 
 
 
 
+@register_model_view(ConfigTemplate, 'bulk_sync', path='sync', detail=False)
 class ConfigTemplateBulkSyncDataView(generic.BulkSyncDataView):
 class ConfigTemplateBulkSyncDataView(generic.BulkSyncDataView):
     queryset = ConfigTemplate.objects.all()
     queryset = ConfigTemplate.objects.all()
 
 
@@ -827,6 +889,7 @@ class ConfigTemplateBulkSyncDataView(generic.BulkSyncDataView):
 # Image attachments
 # Image attachments
 #
 #
 
 
+@register_model_view(ImageAttachment, 'list', path='', detail=False)
 class ImageAttachmentListView(generic.ObjectListView):
 class ImageAttachmentListView(generic.ObjectListView):
     queryset = ImageAttachment.objects.all()
     queryset = ImageAttachment.objects.all()
     filterset = filtersets.ImageAttachmentFilterSet
     filterset = filtersets.ImageAttachmentFilterSet
@@ -837,6 +900,7 @@ class ImageAttachmentListView(generic.ObjectListView):
     }
     }
 
 
 
 
+@register_model_view(ImageAttachment, 'add', detail=False)
 @register_model_view(ImageAttachment, 'edit')
 @register_model_view(ImageAttachment, 'edit')
 class ImageAttachmentEditView(generic.ObjectEditView):
 class ImageAttachmentEditView(generic.ObjectEditView):
     queryset = ImageAttachment.objects.all()
     queryset = ImageAttachment.objects.all()
@@ -871,6 +935,7 @@ class ImageAttachmentDeleteView(generic.ObjectDeleteView):
 # Journal entries
 # Journal entries
 #
 #
 
 
+@register_model_view(JournalEntry, 'list', path='', detail=False)
 class JournalEntryListView(generic.ObjectListView):
 class JournalEntryListView(generic.ObjectListView):
     queryset = JournalEntry.objects.all()
     queryset = JournalEntry.objects.all()
     filterset = filtersets.JournalEntryFilterSet
     filterset = filtersets.JournalEntryFilterSet
@@ -889,6 +954,7 @@ class JournalEntryView(generic.ObjectView):
     queryset = JournalEntry.objects.all()
     queryset = JournalEntry.objects.all()
 
 
 
 
+@register_model_view(JournalEntry, 'add', detail=False)
 @register_model_view(JournalEntry, 'edit')
 @register_model_view(JournalEntry, 'edit')
 class JournalEntryEditView(generic.ObjectEditView):
 class JournalEntryEditView(generic.ObjectEditView):
     queryset = JournalEntry.objects.all()
     queryset = JournalEntry.objects.all()
@@ -917,6 +983,13 @@ class JournalEntryDeleteView(generic.ObjectDeleteView):
         return reverse(viewname, kwargs={'pk': obj.pk})
         return reverse(viewname, kwargs={'pk': obj.pk})
 
 
 
 
+@register_model_view(JournalEntry, 'import', detail=False)
+class JournalEntryBulkImportView(generic.BulkImportView):
+    queryset = JournalEntry.objects.all()
+    model_form = forms.JournalEntryImportForm
+
+
+@register_model_view(JournalEntry, 'bulk_edit', path='edit', detail=False)
 class JournalEntryBulkEditView(generic.BulkEditView):
 class JournalEntryBulkEditView(generic.BulkEditView):
     queryset = JournalEntry.objects.all()
     queryset = JournalEntry.objects.all()
     filterset = filtersets.JournalEntryFilterSet
     filterset = filtersets.JournalEntryFilterSet
@@ -924,17 +997,13 @@ class JournalEntryBulkEditView(generic.BulkEditView):
     form = forms.JournalEntryBulkEditForm
     form = forms.JournalEntryBulkEditForm
 
 
 
 
+@register_model_view(JournalEntry, 'bulk_delete', path='delete', detail=False)
 class JournalEntryBulkDeleteView(generic.BulkDeleteView):
 class JournalEntryBulkDeleteView(generic.BulkDeleteView):
     queryset = JournalEntry.objects.all()
     queryset = JournalEntry.objects.all()
     filterset = filtersets.JournalEntryFilterSet
     filterset = filtersets.JournalEntryFilterSet
     table = tables.JournalEntryTable
     table = tables.JournalEntryTable
 
 
 
 
-class JournalEntryBulkImportView(generic.BulkImportView):
-    queryset = JournalEntry.objects.all()
-    model_form = forms.JournalEntryImportForm
-
-
 #
 #
 # Dashboard & widgets
 # Dashboard & widgets
 #
 #

+ 19 - 107
netbox/ipam/urls.py

@@ -1,150 +1,62 @@
 from django.urls import include, path
 from django.urls import include, path
 
 
 from utilities.urls import get_model_urls
 from utilities.urls import get_model_urls
-from . import views
+from . import views  # noqa F401
 
 
 app_name = 'ipam'
 app_name = 'ipam'
 urlpatterns = [
 urlpatterns = [
 
 
-    # ASN ranges
-    path('asn-ranges/', views.ASNRangeListView.as_view(), name='asnrange_list'),
-    path('asn-ranges/add/', views.ASNRangeEditView.as_view(), name='asnrange_add'),
-    path('asn-ranges/import/', views.ASNRangeBulkImportView.as_view(), name='asnrange_import'),
-    path('asn-ranges/edit/', views.ASNRangeBulkEditView.as_view(), name='asnrange_bulk_edit'),
-    path('asn-ranges/delete/', views.ASNRangeBulkDeleteView.as_view(), name='asnrange_bulk_delete'),
+    path('asn-ranges/', include(get_model_urls('ipam', 'asnrange', detail=False))),
     path('asn-ranges/<int:pk>/', include(get_model_urls('ipam', 'asnrange'))),
     path('asn-ranges/<int:pk>/', include(get_model_urls('ipam', 'asnrange'))),
 
 
-    # ASNs
-    path('asns/', views.ASNListView.as_view(), name='asn_list'),
-    path('asns/add/', views.ASNEditView.as_view(), name='asn_add'),
-    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/', include(get_model_urls('ipam', 'asn', detail=False))),
     path('asns/<int:pk>/', include(get_model_urls('ipam', 'asn'))),
     path('asns/<int:pk>/', include(get_model_urls('ipam', 'asn'))),
 
 
-    # VRFs
-    path('vrfs/', views.VRFListView.as_view(), name='vrf_list'),
-    path('vrfs/add/', views.VRFEditView.as_view(), name='vrf_add'),
-    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/', include(get_model_urls('ipam', 'vrf', detail=False))),
     path('vrfs/<int:pk>/', include(get_model_urls('ipam', 'vrf'))),
     path('vrfs/<int:pk>/', include(get_model_urls('ipam', 'vrf'))),
 
 
-    # Route targets
-    path('route-targets/', views.RouteTargetListView.as_view(), name='routetarget_list'),
-    path('route-targets/add/', views.RouteTargetEditView.as_view(), name='routetarget_add'),
-    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/', include(get_model_urls('ipam', 'routetarget', detail=False))),
     path('route-targets/<int:pk>/', include(get_model_urls('ipam', 'routetarget'))),
     path('route-targets/<int:pk>/', include(get_model_urls('ipam', 'routetarget'))),
 
 
-    # RIRs
-    path('rirs/', views.RIRListView.as_view(), name='rir_list'),
-    path('rirs/add/', views.RIREditView.as_view(), name='rir_add'),
-    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/', include(get_model_urls('ipam', 'rir', detail=False))),
     path('rirs/<int:pk>/', include(get_model_urls('ipam', 'rir'))),
     path('rirs/<int:pk>/', include(get_model_urls('ipam', 'rir'))),
 
 
-    # Aggregates
-    path('aggregates/', views.AggregateListView.as_view(), name='aggregate_list'),
-    path('aggregates/add/', views.AggregateEditView.as_view(), name='aggregate_add'),
-    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/', include(get_model_urls('ipam', 'aggregate', detail=False))),
     path('aggregates/<int:pk>/', include(get_model_urls('ipam', 'aggregate'))),
     path('aggregates/<int:pk>/', include(get_model_urls('ipam', 'aggregate'))),
 
 
-    # Roles
-    path('roles/', views.RoleListView.as_view(), name='role_list'),
-    path('roles/add/', views.RoleEditView.as_view(), name='role_add'),
-    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/', include(get_model_urls('ipam', 'role', detail=False))),
     path('roles/<int:pk>/', include(get_model_urls('ipam', 'role'))),
     path('roles/<int:pk>/', include(get_model_urls('ipam', 'role'))),
 
 
-    # Prefixes
-    path('prefixes/', views.PrefixListView.as_view(), name='prefix_list'),
-    path('prefixes/add/', views.PrefixEditView.as_view(), name='prefix_add'),
-    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/', include(get_model_urls('ipam', 'prefix', detail=False))),
     path('prefixes/<int:pk>/', include(get_model_urls('ipam', 'prefix'))),
     path('prefixes/<int:pk>/', include(get_model_urls('ipam', 'prefix'))),
 
 
-    # IP ranges
-    path('ip-ranges/', views.IPRangeListView.as_view(), name='iprange_list'),
-    path('ip-ranges/add/', views.IPRangeEditView.as_view(), name='iprange_add'),
-    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/', include(get_model_urls('ipam', 'iprange', detail=False))),
     path('ip-ranges/<int:pk>/', include(get_model_urls('ipam', 'iprange'))),
     path('ip-ranges/<int:pk>/', include(get_model_urls('ipam', 'iprange'))),
 
 
-    # IP addresses
-    path('ip-addresses/', views.IPAddressListView.as_view(), name='ipaddress_list'),
-    path('ip-addresses/add/', views.IPAddressEditView.as_view(), name='ipaddress_add'),
-    path('ip-addresses/bulk-add/', views.IPAddressBulkCreateView.as_view(), name='ipaddress_bulk_add'),
-    path('ip-addresses/import/', views.IPAddressBulkImportView.as_view(), name='ipaddress_import'),
-    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/', include(get_model_urls('ipam', 'ipaddress', detail=False))),
     path('ip-addresses/<int:pk>/', include(get_model_urls('ipam', 'ipaddress'))),
     path('ip-addresses/<int:pk>/', include(get_model_urls('ipam', 'ipaddress'))),
 
 
-    # FHRP groups
-    path('fhrp-groups/', views.FHRPGroupListView.as_view(), name='fhrpgroup_list'),
-    path('fhrp-groups/add/', views.FHRPGroupEditView.as_view(), name='fhrpgroup_add'),
-    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/', include(get_model_urls('ipam', 'fhrpgroup', detail=False))),
     path('fhrp-groups/<int:pk>/', include(get_model_urls('ipam', 'fhrpgroup'))),
     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/', include(get_model_urls('ipam', 'fhrpgroupassignment', detail=False))),
     path('fhrp-group-assignments/<int:pk>/', include(get_model_urls('ipam', 'fhrpgroupassignment'))),
     path('fhrp-group-assignments/<int:pk>/', include(get_model_urls('ipam', 'fhrpgroupassignment'))),
 
 
-    # VLAN groups
-    path('vlan-groups/', views.VLANGroupListView.as_view(), name='vlangroup_list'),
-    path('vlan-groups/add/', views.VLANGroupEditView.as_view(), name='vlangroup_add'),
-    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/', include(get_model_urls('ipam', 'vlangroup', detail=False))),
     path('vlan-groups/<int:pk>/', include(get_model_urls('ipam', 'vlangroup'))),
     path('vlan-groups/<int:pk>/', include(get_model_urls('ipam', 'vlangroup'))),
 
 
-    # VLANs
-    path('vlans/', views.VLANListView.as_view(), name='vlan_list'),
-    path('vlans/add/', views.VLANEditView.as_view(), name='vlan_add'),
-    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/', include(get_model_urls('ipam', 'vlan', detail=False))),
     path('vlans/<int:pk>/', include(get_model_urls('ipam', 'vlan'))),
     path('vlans/<int:pk>/', include(get_model_urls('ipam', 'vlan'))),
 
 
-    # VLAN Translation Policies
-    path('vlan-translation-policies/', views.VLANTranslationPolicyListView.as_view(), name='vlantranslationpolicy_list'),
-    path('vlan-translation-policies/add/', views.VLANTranslationPolicyEditView.as_view(), name='vlantranslationpolicy_add'),
-    path('vlan-translation-policies/import/', views.VLANTranslationPolicyBulkImportView.as_view(), name='vlantranslationpolicy_import'),
-    path('vlan-translation-policies/edit/', views.VLANTranslationPolicyBulkEditView.as_view(), name='vlantranslationpolicy_bulk_edit'),
-    path('vlan-translation-policies/delete/', views.VLANTranslationPolicyBulkDeleteView.as_view(), name='vlantranslationpolicy_bulk_delete'),
+    path('vlan-translation-policies/', include(get_model_urls('ipam', 'vlantranslationpolicy', detail=False))),
     path('vlan-translation-policies/<int:pk>/', include(get_model_urls('ipam', 'vlantranslationpolicy'))),
     path('vlan-translation-policies/<int:pk>/', include(get_model_urls('ipam', 'vlantranslationpolicy'))),
 
 
-    # VLAN Translation Rules
-    path('vlan-translation-rules/', views.VLANTranslationRuleListView.as_view(), name='vlantranslationrule_list'),
-    path('vlan-translation-rules/add/', views.VLANTranslationRuleEditView.as_view(), name='vlantranslationrule_add'),
-    path('vlan-translation-rules/import/', views.VLANTranslationRuleBulkImportView.as_view(), name='vlantranslationrule_import'),
-    path('vlan-translation-rules/edit/', views.VLANTranslationRuleBulkEditView.as_view(), name='vlantranslationrule_bulk_edit'),
-    path('vlan-translation-rules/delete/', views.VLANTranslationRuleBulkDeleteView.as_view(), name='vlantranslationrule_bulk_delete'),
+    path('vlan-translation-rules/', include(get_model_urls('ipam', 'vlantranslationrule', detail=False))),
     path('vlan-translation-rules/<int:pk>/', include(get_model_urls('ipam', 'vlantranslationrule'))),
     path('vlan-translation-rules/<int:pk>/', include(get_model_urls('ipam', 'vlantranslationrule'))),
 
 
-    # Service templates
-    path('service-templates/', views.ServiceTemplateListView.as_view(), name='servicetemplate_list'),
-    path('service-templates/add/', views.ServiceTemplateEditView.as_view(), name='servicetemplate_add'),
-    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/', include(get_model_urls('ipam', 'servicetemplate', detail=False))),
     path('service-templates/<int:pk>/', include(get_model_urls('ipam', 'servicetemplate'))),
     path('service-templates/<int:pk>/', include(get_model_urls('ipam', 'servicetemplate'))),
 
 
-    # Services
-    path('services/', views.ServiceListView.as_view(), name='service_list'),
-    path('services/add/', views.ServiceCreateView.as_view(), name='service_add'),
-    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/', include(get_model_urls('ipam', 'service', detail=False))),
     path('services/<int:pk>/', include(get_model_urls('ipam', 'service'))),
     path('services/<int:pk>/', include(get_model_urls('ipam', 'service'))),
 ]
 ]

+ 88 - 0
netbox/ipam/views.py

@@ -29,6 +29,7 @@ from .utils import add_requested_prefixes, add_available_ipaddresses, add_availa
 # VRFs
 # VRFs
 #
 #
 
 
+@register_model_view(VRF, 'list', path='', detail=False)
 class VRFListView(generic.ObjectListView):
 class VRFListView(generic.ObjectListView):
     queryset = VRF.objects.all()
     queryset = VRF.objects.all()
     filterset = filtersets.VRFFilterSet
     filterset = filtersets.VRFFilterSet
@@ -57,6 +58,7 @@ class VRFView(GetRelatedModelsMixin, generic.ObjectView):
         }
         }
 
 
 
 
+@register_model_view(VRF, 'add', detail=False)
 @register_model_view(VRF, 'edit')
 @register_model_view(VRF, 'edit')
 class VRFEditView(generic.ObjectEditView):
 class VRFEditView(generic.ObjectEditView):
     queryset = VRF.objects.all()
     queryset = VRF.objects.all()
@@ -68,11 +70,13 @@ class VRFDeleteView(generic.ObjectDeleteView):
     queryset = VRF.objects.all()
     queryset = VRF.objects.all()
 
 
 
 
+@register_model_view(VRF, 'import', detail=False)
 class VRFBulkImportView(generic.BulkImportView):
 class VRFBulkImportView(generic.BulkImportView):
     queryset = VRF.objects.all()
     queryset = VRF.objects.all()
     model_form = forms.VRFImportForm
     model_form = forms.VRFImportForm
 
 
 
 
+@register_model_view(VRF, 'bulk_edit', path='edit', detail=False)
 class VRFBulkEditView(generic.BulkEditView):
 class VRFBulkEditView(generic.BulkEditView):
     queryset = VRF.objects.all()
     queryset = VRF.objects.all()
     filterset = filtersets.VRFFilterSet
     filterset = filtersets.VRFFilterSet
@@ -80,6 +84,7 @@ class VRFBulkEditView(generic.BulkEditView):
     form = forms.VRFBulkEditForm
     form = forms.VRFBulkEditForm
 
 
 
 
+@register_model_view(VRF, 'bulk_delete', path='delete', detail=False)
 class VRFBulkDeleteView(generic.BulkDeleteView):
 class VRFBulkDeleteView(generic.BulkDeleteView):
     queryset = VRF.objects.all()
     queryset = VRF.objects.all()
     filterset = filtersets.VRFFilterSet
     filterset = filtersets.VRFFilterSet
@@ -90,6 +95,7 @@ class VRFBulkDeleteView(generic.BulkDeleteView):
 # Route targets
 # Route targets
 #
 #
 
 
+@register_model_view(RouteTarget, 'list', path='', detail=False)
 class RouteTargetListView(generic.ObjectListView):
 class RouteTargetListView(generic.ObjectListView):
     queryset = RouteTarget.objects.all()
     queryset = RouteTarget.objects.all()
     filterset = filtersets.RouteTargetFilterSet
     filterset = filtersets.RouteTargetFilterSet
@@ -102,6 +108,7 @@ class RouteTargetView(generic.ObjectView):
     queryset = RouteTarget.objects.all()
     queryset = RouteTarget.objects.all()
 
 
 
 
+@register_model_view(RouteTarget, 'add', detail=False)
 @register_model_view(RouteTarget, 'edit')
 @register_model_view(RouteTarget, 'edit')
 class RouteTargetEditView(generic.ObjectEditView):
 class RouteTargetEditView(generic.ObjectEditView):
     queryset = RouteTarget.objects.all()
     queryset = RouteTarget.objects.all()
@@ -113,11 +120,13 @@ class RouteTargetDeleteView(generic.ObjectDeleteView):
     queryset = RouteTarget.objects.all()
     queryset = RouteTarget.objects.all()
 
 
 
 
+@register_model_view(RouteTarget, 'import', detail=False)
 class RouteTargetBulkImportView(generic.BulkImportView):
 class RouteTargetBulkImportView(generic.BulkImportView):
     queryset = RouteTarget.objects.all()
     queryset = RouteTarget.objects.all()
     model_form = forms.RouteTargetImportForm
     model_form = forms.RouteTargetImportForm
 
 
 
 
+@register_model_view(RouteTarget, 'bulk_edit', path='edit', detail=False)
 class RouteTargetBulkEditView(generic.BulkEditView):
 class RouteTargetBulkEditView(generic.BulkEditView):
     queryset = RouteTarget.objects.all()
     queryset = RouteTarget.objects.all()
     filterset = filtersets.RouteTargetFilterSet
     filterset = filtersets.RouteTargetFilterSet
@@ -125,6 +134,7 @@ class RouteTargetBulkEditView(generic.BulkEditView):
     form = forms.RouteTargetBulkEditForm
     form = forms.RouteTargetBulkEditForm
 
 
 
 
+@register_model_view(RouteTarget, 'bulk_delete', path='delete', detail=False)
 class RouteTargetBulkDeleteView(generic.BulkDeleteView):
 class RouteTargetBulkDeleteView(generic.BulkDeleteView):
     queryset = RouteTarget.objects.all()
     queryset = RouteTarget.objects.all()
     filterset = filtersets.RouteTargetFilterSet
     filterset = filtersets.RouteTargetFilterSet
@@ -135,6 +145,7 @@ class RouteTargetBulkDeleteView(generic.BulkDeleteView):
 # RIRs
 # RIRs
 #
 #
 
 
+@register_model_view(RIR, 'list', path='', detail=False)
 class RIRListView(generic.ObjectListView):
 class RIRListView(generic.ObjectListView):
     queryset = RIR.objects.annotate(
     queryset = RIR.objects.annotate(
         aggregate_count=count_related(Aggregate, 'rir')
         aggregate_count=count_related(Aggregate, 'rir')
@@ -154,6 +165,7 @@ class RIRView(GetRelatedModelsMixin, generic.ObjectView):
         }
         }
 
 
 
 
+@register_model_view(RIR, 'add', detail=False)
 @register_model_view(RIR, 'edit')
 @register_model_view(RIR, 'edit')
 class RIREditView(generic.ObjectEditView):
 class RIREditView(generic.ObjectEditView):
     queryset = RIR.objects.all()
     queryset = RIR.objects.all()
@@ -165,11 +177,13 @@ class RIRDeleteView(generic.ObjectDeleteView):
     queryset = RIR.objects.all()
     queryset = RIR.objects.all()
 
 
 
 
+@register_model_view(RIR, 'import', detail=False)
 class RIRBulkImportView(generic.BulkImportView):
 class RIRBulkImportView(generic.BulkImportView):
     queryset = RIR.objects.all()
     queryset = RIR.objects.all()
     model_form = forms.RIRImportForm
     model_form = forms.RIRImportForm
 
 
 
 
+@register_model_view(RIR, 'bulk_edit', path='edit', detail=False)
 class RIRBulkEditView(generic.BulkEditView):
 class RIRBulkEditView(generic.BulkEditView):
     queryset = RIR.objects.annotate(
     queryset = RIR.objects.annotate(
         aggregate_count=count_related(Aggregate, 'rir')
         aggregate_count=count_related(Aggregate, 'rir')
@@ -179,6 +193,7 @@ class RIRBulkEditView(generic.BulkEditView):
     form = forms.RIRBulkEditForm
     form = forms.RIRBulkEditForm
 
 
 
 
+@register_model_view(RIR, 'bulk_delete', path='delete', detail=False)
 class RIRBulkDeleteView(generic.BulkDeleteView):
 class RIRBulkDeleteView(generic.BulkDeleteView):
     queryset = RIR.objects.annotate(
     queryset = RIR.objects.annotate(
         aggregate_count=count_related(Aggregate, 'rir')
         aggregate_count=count_related(Aggregate, 'rir')
@@ -191,6 +206,7 @@ class RIRBulkDeleteView(generic.BulkDeleteView):
 # ASN ranges
 # ASN ranges
 #
 #
 
 
+@register_model_view(ASNRange, 'list', path='', detail=False)
 class ASNRangeListView(generic.ObjectListView):
 class ASNRangeListView(generic.ObjectListView):
     queryset = ASNRange.objects.annotate_asn_counts()
     queryset = ASNRange.objects.annotate_asn_counts()
     filterset = filtersets.ASNRangeFilterSet
     filterset = filtersets.ASNRangeFilterSet
@@ -224,6 +240,7 @@ class ASNRangeASNsView(generic.ObjectChildrenView):
         )
         )
 
 
 
 
+@register_model_view(ASNRange, 'add', detail=False)
 @register_model_view(ASNRange, 'edit')
 @register_model_view(ASNRange, 'edit')
 class ASNRangeEditView(generic.ObjectEditView):
 class ASNRangeEditView(generic.ObjectEditView):
     queryset = ASNRange.objects.all()
     queryset = ASNRange.objects.all()
@@ -235,11 +252,13 @@ class ASNRangeDeleteView(generic.ObjectDeleteView):
     queryset = ASNRange.objects.all()
     queryset = ASNRange.objects.all()
 
 
 
 
+@register_model_view(ASNRange, 'import', detail=False)
 class ASNRangeBulkImportView(generic.BulkImportView):
 class ASNRangeBulkImportView(generic.BulkImportView):
     queryset = ASNRange.objects.all()
     queryset = ASNRange.objects.all()
     model_form = forms.ASNRangeImportForm
     model_form = forms.ASNRangeImportForm
 
 
 
 
+@register_model_view(ASNRange, 'bulk_edit', path='edit', detail=False)
 class ASNRangeBulkEditView(generic.BulkEditView):
 class ASNRangeBulkEditView(generic.BulkEditView):
     queryset = ASNRange.objects.annotate_asn_counts()
     queryset = ASNRange.objects.annotate_asn_counts()
     filterset = filtersets.ASNRangeFilterSet
     filterset = filtersets.ASNRangeFilterSet
@@ -247,6 +266,7 @@ class ASNRangeBulkEditView(generic.BulkEditView):
     form = forms.ASNRangeBulkEditForm
     form = forms.ASNRangeBulkEditForm
 
 
 
 
+@register_model_view(ASNRange, 'bulk_delete', path='delete', detail=False)
 class ASNRangeBulkDeleteView(generic.BulkDeleteView):
 class ASNRangeBulkDeleteView(generic.BulkDeleteView):
     queryset = ASNRange.objects.annotate_asn_counts()
     queryset = ASNRange.objects.annotate_asn_counts()
     filterset = filtersets.ASNRangeFilterSet
     filterset = filtersets.ASNRangeFilterSet
@@ -257,6 +277,7 @@ class ASNRangeBulkDeleteView(generic.BulkDeleteView):
 # ASNs
 # ASNs
 #
 #
 
 
+@register_model_view(ASN, 'list', path='', detail=False)
 class ASNListView(generic.ObjectListView):
 class ASNListView(generic.ObjectListView):
     queryset = ASN.objects.annotate(
     queryset = ASN.objects.annotate(
         site_count=count_related(Site, 'asns'),
         site_count=count_related(Site, 'asns'),
@@ -284,6 +305,7 @@ class ASNView(GetRelatedModelsMixin, generic.ObjectView):
         }
         }
 
 
 
 
+@register_model_view(ASN, 'add', detail=False)
 @register_model_view(ASN, 'edit')
 @register_model_view(ASN, 'edit')
 class ASNEditView(generic.ObjectEditView):
 class ASNEditView(generic.ObjectEditView):
     queryset = ASN.objects.all()
     queryset = ASN.objects.all()
@@ -295,11 +317,13 @@ class ASNDeleteView(generic.ObjectDeleteView):
     queryset = ASN.objects.all()
     queryset = ASN.objects.all()
 
 
 
 
+@register_model_view(ASN, 'import', detail=False)
 class ASNBulkImportView(generic.BulkImportView):
 class ASNBulkImportView(generic.BulkImportView):
     queryset = ASN.objects.all()
     queryset = ASN.objects.all()
     model_form = forms.ASNImportForm
     model_form = forms.ASNImportForm
 
 
 
 
+@register_model_view(ASN, 'bulk_edit', path='edit', detail=False)
 class ASNBulkEditView(generic.BulkEditView):
 class ASNBulkEditView(generic.BulkEditView):
     queryset = ASN.objects.annotate(
     queryset = ASN.objects.annotate(
         site_count=count_related(Site, 'asns')
         site_count=count_related(Site, 'asns')
@@ -309,6 +333,7 @@ class ASNBulkEditView(generic.BulkEditView):
     form = forms.ASNBulkEditForm
     form = forms.ASNBulkEditForm
 
 
 
 
+@register_model_view(ASN, 'bulk_delete', path='delete', detail=False)
 class ASNBulkDeleteView(generic.BulkDeleteView):
 class ASNBulkDeleteView(generic.BulkDeleteView):
     queryset = ASN.objects.annotate(
     queryset = ASN.objects.annotate(
         site_count=count_related(Site, 'asns')
         site_count=count_related(Site, 'asns')
@@ -321,6 +346,7 @@ class ASNBulkDeleteView(generic.BulkDeleteView):
 # Aggregates
 # Aggregates
 #
 #
 
 
+@register_model_view(Aggregate, 'list', path='', detail=False)
 class AggregateListView(generic.ObjectListView):
 class AggregateListView(generic.ObjectListView):
     queryset = Aggregate.objects.annotate(
     queryset = Aggregate.objects.annotate(
         child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ())
         child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ())
@@ -371,6 +397,7 @@ class AggregatePrefixesView(generic.ObjectChildrenView):
         }
         }
 
 
 
 
+@register_model_view(Aggregate, 'add', detail=False)
 @register_model_view(Aggregate, 'edit')
 @register_model_view(Aggregate, 'edit')
 class AggregateEditView(generic.ObjectEditView):
 class AggregateEditView(generic.ObjectEditView):
     queryset = Aggregate.objects.all()
     queryset = Aggregate.objects.all()
@@ -382,11 +409,13 @@ class AggregateDeleteView(generic.ObjectDeleteView):
     queryset = Aggregate.objects.all()
     queryset = Aggregate.objects.all()
 
 
 
 
+@register_model_view(Aggregate, 'import', detail=False)
 class AggregateBulkImportView(generic.BulkImportView):
 class AggregateBulkImportView(generic.BulkImportView):
     queryset = Aggregate.objects.all()
     queryset = Aggregate.objects.all()
     model_form = forms.AggregateImportForm
     model_form = forms.AggregateImportForm
 
 
 
 
+@register_model_view(Aggregate, 'bulk_edit', path='edit', detail=False)
 class AggregateBulkEditView(generic.BulkEditView):
 class AggregateBulkEditView(generic.BulkEditView):
     queryset = Aggregate.objects.annotate(
     queryset = Aggregate.objects.annotate(
         child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ())
         child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ())
@@ -396,6 +425,7 @@ class AggregateBulkEditView(generic.BulkEditView):
     form = forms.AggregateBulkEditForm
     form = forms.AggregateBulkEditForm
 
 
 
 
+@register_model_view(Aggregate, 'bulk_delete', path='delete', detail=False)
 class AggregateBulkDeleteView(generic.BulkDeleteView):
 class AggregateBulkDeleteView(generic.BulkDeleteView):
     queryset = Aggregate.objects.annotate(
     queryset = Aggregate.objects.annotate(
         child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ())
         child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ())
@@ -413,6 +443,7 @@ class AggregateContactsView(ObjectContactsView):
 # Prefix/VLAN roles
 # Prefix/VLAN roles
 #
 #
 
 
+@register_model_view(Role, 'list', path='', detail=False)
 class RoleListView(generic.ObjectListView):
 class RoleListView(generic.ObjectListView):
     queryset = Role.objects.annotate(
     queryset = Role.objects.annotate(
         prefix_count=count_related(Prefix, 'role'),
         prefix_count=count_related(Prefix, 'role'),
@@ -434,6 +465,7 @@ class RoleView(GetRelatedModelsMixin, generic.ObjectView):
         }
         }
 
 
 
 
+@register_model_view(Role, 'add', detail=False)
 @register_model_view(Role, 'edit')
 @register_model_view(Role, 'edit')
 class RoleEditView(generic.ObjectEditView):
 class RoleEditView(generic.ObjectEditView):
     queryset = Role.objects.all()
     queryset = Role.objects.all()
@@ -445,11 +477,13 @@ class RoleDeleteView(generic.ObjectDeleteView):
     queryset = Role.objects.all()
     queryset = Role.objects.all()
 
 
 
 
+@register_model_view(Role, 'import', detail=False)
 class RoleBulkImportView(generic.BulkImportView):
 class RoleBulkImportView(generic.BulkImportView):
     queryset = Role.objects.all()
     queryset = Role.objects.all()
     model_form = forms.RoleImportForm
     model_form = forms.RoleImportForm
 
 
 
 
+@register_model_view(Role, 'bulk_edit', path='edit', detail=False)
 class RoleBulkEditView(generic.BulkEditView):
 class RoleBulkEditView(generic.BulkEditView):
     queryset = Role.objects.all()
     queryset = Role.objects.all()
     filterset = filtersets.RoleFilterSet
     filterset = filtersets.RoleFilterSet
@@ -457,6 +491,7 @@ class RoleBulkEditView(generic.BulkEditView):
     form = forms.RoleBulkEditForm
     form = forms.RoleBulkEditForm
 
 
 
 
+@register_model_view(Role, 'bulk_delete', path='delete', detail=False)
 class RoleBulkDeleteView(generic.BulkDeleteView):
 class RoleBulkDeleteView(generic.BulkDeleteView):
     queryset = Role.objects.all()
     queryset = Role.objects.all()
     filterset = filtersets.RoleFilterSet
     filterset = filtersets.RoleFilterSet
@@ -467,6 +502,7 @@ class RoleBulkDeleteView(generic.BulkDeleteView):
 # Prefixes
 # Prefixes
 #
 #
 
 
+@register_model_view(Prefix, 'list', path='', detail=False)
 class PrefixListView(generic.ObjectListView):
 class PrefixListView(generic.ObjectListView):
     queryset = Prefix.objects.all()
     queryset = Prefix.objects.all()
     filterset = filtersets.PrefixFilterSet
     filterset = filtersets.PrefixFilterSet
@@ -615,6 +651,7 @@ class PrefixIPAddressesView(generic.ObjectChildrenView):
         }
         }
 
 
 
 
+@register_model_view(Prefix, 'add', detail=False)
 @register_model_view(Prefix, 'edit')
 @register_model_view(Prefix, 'edit')
 class PrefixEditView(generic.ObjectEditView):
 class PrefixEditView(generic.ObjectEditView):
     queryset = Prefix.objects.all()
     queryset = Prefix.objects.all()
@@ -626,11 +663,13 @@ class PrefixDeleteView(generic.ObjectDeleteView):
     queryset = Prefix.objects.all()
     queryset = Prefix.objects.all()
 
 
 
 
+@register_model_view(Prefix, 'import', detail=False)
 class PrefixBulkImportView(generic.BulkImportView):
 class PrefixBulkImportView(generic.BulkImportView):
     queryset = Prefix.objects.all()
     queryset = Prefix.objects.all()
     model_form = forms.PrefixImportForm
     model_form = forms.PrefixImportForm
 
 
 
 
+@register_model_view(Prefix, 'bulk_edit', path='edit', detail=False)
 class PrefixBulkEditView(generic.BulkEditView):
 class PrefixBulkEditView(generic.BulkEditView):
     queryset = Prefix.objects.prefetch_related('vrf__tenant')
     queryset = Prefix.objects.prefetch_related('vrf__tenant')
     filterset = filtersets.PrefixFilterSet
     filterset = filtersets.PrefixFilterSet
@@ -638,6 +677,7 @@ class PrefixBulkEditView(generic.BulkEditView):
     form = forms.PrefixBulkEditForm
     form = forms.PrefixBulkEditForm
 
 
 
 
+@register_model_view(Prefix, 'bulk_delete', path='delete', detail=False)
 class PrefixBulkDeleteView(generic.BulkDeleteView):
 class PrefixBulkDeleteView(generic.BulkDeleteView):
     queryset = Prefix.objects.prefetch_related('vrf__tenant')
     queryset = Prefix.objects.prefetch_related('vrf__tenant')
     filterset = filtersets.PrefixFilterSet
     filterset = filtersets.PrefixFilterSet
@@ -653,6 +693,7 @@ class PrefixContactsView(ObjectContactsView):
 # IP Ranges
 # IP Ranges
 #
 #
 
 
+@register_model_view(IPRange, 'list', path='', detail=False)
 class IPRangeListView(generic.ObjectListView):
 class IPRangeListView(generic.ObjectListView):
     queryset = IPRange.objects.all()
     queryset = IPRange.objects.all()
     filterset = filtersets.IPRangeFilterSet
     filterset = filtersets.IPRangeFilterSet
@@ -704,6 +745,7 @@ class IPRangeIPAddressesView(generic.ObjectChildrenView):
         return parent.get_child_ips().restrict(request.user, 'view')
         return parent.get_child_ips().restrict(request.user, 'view')
 
 
 
 
+@register_model_view(IPRange, 'add', detail=False)
 @register_model_view(IPRange, 'edit')
 @register_model_view(IPRange, 'edit')
 class IPRangeEditView(generic.ObjectEditView):
 class IPRangeEditView(generic.ObjectEditView):
     queryset = IPRange.objects.all()
     queryset = IPRange.objects.all()
@@ -715,11 +757,13 @@ class IPRangeDeleteView(generic.ObjectDeleteView):
     queryset = IPRange.objects.all()
     queryset = IPRange.objects.all()
 
 
 
 
+@register_model_view(IPRange, 'import', detail=False)
 class IPRangeBulkImportView(generic.BulkImportView):
 class IPRangeBulkImportView(generic.BulkImportView):
     queryset = IPRange.objects.all()
     queryset = IPRange.objects.all()
     model_form = forms.IPRangeImportForm
     model_form = forms.IPRangeImportForm
 
 
 
 
+@register_model_view(IPRange, 'bulk_edit', path='edit', detail=False)
 class IPRangeBulkEditView(generic.BulkEditView):
 class IPRangeBulkEditView(generic.BulkEditView):
     queryset = IPRange.objects.all()
     queryset = IPRange.objects.all()
     filterset = filtersets.IPRangeFilterSet
     filterset = filtersets.IPRangeFilterSet
@@ -727,6 +771,7 @@ class IPRangeBulkEditView(generic.BulkEditView):
     form = forms.IPRangeBulkEditForm
     form = forms.IPRangeBulkEditForm
 
 
 
 
+@register_model_view(IPRange, 'bulk_delete', path='delete', detail=False)
 class IPRangeBulkDeleteView(generic.BulkDeleteView):
 class IPRangeBulkDeleteView(generic.BulkDeleteView):
     queryset = IPRange.objects.all()
     queryset = IPRange.objects.all()
     filterset = filtersets.IPRangeFilterSet
     filterset = filtersets.IPRangeFilterSet
@@ -742,6 +787,7 @@ class IPRangeContactsView(ObjectContactsView):
 # IP addresses
 # IP addresses
 #
 #
 
 
+@register_model_view(IPAddress, 'list', path='', detail=False)
 class IPAddressListView(generic.ObjectListView):
 class IPAddressListView(generic.ObjectListView):
     queryset = IPAddress.objects.all()
     queryset = IPAddress.objects.all()
     filterset = filtersets.IPAddressFilterSet
     filterset = filtersets.IPAddressFilterSet
@@ -788,6 +834,7 @@ class IPAddressView(generic.ObjectView):
         }
         }
 
 
 
 
+@register_model_view(IPAddress, 'add', detail=False)
 @register_model_view(IPAddress, 'edit')
 @register_model_view(IPAddress, 'edit')
 class IPAddressEditView(generic.ObjectEditView):
 class IPAddressEditView(generic.ObjectEditView):
     queryset = IPAddress.objects.all()
     queryset = IPAddress.objects.all()
@@ -818,6 +865,7 @@ class IPAddressEditView(generic.ObjectEditView):
 
 
 
 
 # TODO: Standardize or remove this view
 # TODO: Standardize or remove this view
+@register_model_view(IPAddress, 'assign', path='assign', detail=False)
 class IPAddressAssignView(generic.ObjectView):
 class IPAddressAssignView(generic.ObjectView):
     """
     """
     Search for IPAddresses to be assigned to an Interface.
     Search for IPAddresses to be assigned to an Interface.
@@ -862,6 +910,7 @@ class IPAddressDeleteView(generic.ObjectDeleteView):
     queryset = IPAddress.objects.all()
     queryset = IPAddress.objects.all()
 
 
 
 
+@register_model_view(IPAddress, 'bulk_add', path='bulk-add', detail=False)
 class IPAddressBulkCreateView(generic.BulkCreateView):
 class IPAddressBulkCreateView(generic.BulkCreateView):
     queryset = IPAddress.objects.all()
     queryset = IPAddress.objects.all()
     form = forms.IPAddressBulkCreateForm
     form = forms.IPAddressBulkCreateForm
@@ -870,11 +919,13 @@ class IPAddressBulkCreateView(generic.BulkCreateView):
     template_name = 'ipam/ipaddress_bulk_add.html'
     template_name = 'ipam/ipaddress_bulk_add.html'
 
 
 
 
+@register_model_view(IPAddress, 'import', detail=False)
 class IPAddressBulkImportView(generic.BulkImportView):
 class IPAddressBulkImportView(generic.BulkImportView):
     queryset = IPAddress.objects.all()
     queryset = IPAddress.objects.all()
     model_form = forms.IPAddressImportForm
     model_form = forms.IPAddressImportForm
 
 
 
 
+@register_model_view(IPAddress, 'bulk_edit', path='edit', detail=False)
 class IPAddressBulkEditView(generic.BulkEditView):
 class IPAddressBulkEditView(generic.BulkEditView):
     queryset = IPAddress.objects.prefetch_related('vrf__tenant')
     queryset = IPAddress.objects.prefetch_related('vrf__tenant')
     filterset = filtersets.IPAddressFilterSet
     filterset = filtersets.IPAddressFilterSet
@@ -882,6 +933,7 @@ class IPAddressBulkEditView(generic.BulkEditView):
     form = forms.IPAddressBulkEditForm
     form = forms.IPAddressBulkEditForm
 
 
 
 
+@register_model_view(IPAddress, 'bulk_delete', path='delete', detail=False)
 class IPAddressBulkDeleteView(generic.BulkDeleteView):
 class IPAddressBulkDeleteView(generic.BulkDeleteView):
     queryset = IPAddress.objects.prefetch_related('vrf__tenant')
     queryset = IPAddress.objects.prefetch_related('vrf__tenant')
     filterset = filtersets.IPAddressFilterSet
     filterset = filtersets.IPAddressFilterSet
@@ -915,6 +967,7 @@ class IPAddressContactsView(ObjectContactsView):
 # VLAN groups
 # VLAN groups
 #
 #
 
 
+@register_model_view(VLANGroup, 'list', path='', detail=False)
 class VLANGroupListView(generic.ObjectListView):
 class VLANGroupListView(generic.ObjectListView):
     queryset = VLANGroup.objects.annotate_utilization()
     queryset = VLANGroup.objects.annotate_utilization()
     filterset = filtersets.VLANGroupFilterSet
     filterset = filtersets.VLANGroupFilterSet
@@ -932,6 +985,7 @@ class VLANGroupView(GetRelatedModelsMixin, generic.ObjectView):
         }
         }
 
 
 
 
+@register_model_view(VLANGroup, 'add', detail=False)
 @register_model_view(VLANGroup, 'edit')
 @register_model_view(VLANGroup, 'edit')
 class VLANGroupEditView(generic.ObjectEditView):
 class VLANGroupEditView(generic.ObjectEditView):
     queryset = VLANGroup.objects.all()
     queryset = VLANGroup.objects.all()
@@ -943,11 +997,13 @@ class VLANGroupDeleteView(generic.ObjectDeleteView):
     queryset = VLANGroup.objects.all()
     queryset = VLANGroup.objects.all()
 
 
 
 
+@register_model_view(VLANGroup, 'import', detail=False)
 class VLANGroupBulkImportView(generic.BulkImportView):
 class VLANGroupBulkImportView(generic.BulkImportView):
     queryset = VLANGroup.objects.all()
     queryset = VLANGroup.objects.all()
     model_form = forms.VLANGroupImportForm
     model_form = forms.VLANGroupImportForm
 
 
 
 
+@register_model_view(VLANGroup, 'bulk_edit', path='edit', detail=False)
 class VLANGroupBulkEditView(generic.BulkEditView):
 class VLANGroupBulkEditView(generic.BulkEditView):
     queryset = VLANGroup.objects.annotate_utilization().prefetch_related('tags')
     queryset = VLANGroup.objects.annotate_utilization().prefetch_related('tags')
     filterset = filtersets.VLANGroupFilterSet
     filterset = filtersets.VLANGroupFilterSet
@@ -955,6 +1011,7 @@ class VLANGroupBulkEditView(generic.BulkEditView):
     form = forms.VLANGroupBulkEditForm
     form = forms.VLANGroupBulkEditForm
 
 
 
 
+@register_model_view(VLANGroup, 'bulk_delete', path='delete', detail=False)
 class VLANGroupBulkDeleteView(generic.BulkDeleteView):
 class VLANGroupBulkDeleteView(generic.BulkDeleteView):
     queryset = VLANGroup.objects.annotate_utilization().prefetch_related('tags')
     queryset = VLANGroup.objects.annotate_utilization().prefetch_related('tags')
     filterset = filtersets.VLANGroupFilterSet
     filterset = filtersets.VLANGroupFilterSet
@@ -991,6 +1048,7 @@ class VLANGroupVLANsView(generic.ObjectChildrenView):
 # VLAN Translation Policies
 # VLAN Translation Policies
 #
 #
 
 
+@register_model_view(VLANTranslationPolicy, 'list', path='', detail=False)
 class VLANTranslationPolicyListView(generic.ObjectListView):
 class VLANTranslationPolicyListView(generic.ObjectListView):
     queryset = VLANTranslationPolicy.objects.all()
     queryset = VLANTranslationPolicy.objects.all()
     filterset = filtersets.VLANTranslationPolicyFilterSet
     filterset = filtersets.VLANTranslationPolicyFilterSet
@@ -1012,6 +1070,7 @@ class VLANTranslationPolicyView(GetRelatedModelsMixin, generic.ObjectView):
         }
         }
 
 
 
 
+@register_model_view(VLANTranslationPolicy, 'add', detail=False)
 @register_model_view(VLANTranslationPolicy, 'edit')
 @register_model_view(VLANTranslationPolicy, 'edit')
 class VLANTranslationPolicyEditView(generic.ObjectEditView):
 class VLANTranslationPolicyEditView(generic.ObjectEditView):
     queryset = VLANTranslationPolicy.objects.all()
     queryset = VLANTranslationPolicy.objects.all()
@@ -1023,11 +1082,13 @@ class VLANTranslationPolicyDeleteView(generic.ObjectDeleteView):
     queryset = VLANTranslationPolicy.objects.all()
     queryset = VLANTranslationPolicy.objects.all()
 
 
 
 
+@register_model_view(VLANTranslationPolicy, 'import', detail=False)
 class VLANTranslationPolicyBulkImportView(generic.BulkImportView):
 class VLANTranslationPolicyBulkImportView(generic.BulkImportView):
     queryset = VLANTranslationPolicy.objects.all()
     queryset = VLANTranslationPolicy.objects.all()
     model_form = forms.VLANTranslationPolicyImportForm
     model_form = forms.VLANTranslationPolicyImportForm
 
 
 
 
+@register_model_view(VLANTranslationPolicy, 'bulk_edit', path='edit', detail=False)
 class VLANTranslationPolicyBulkEditView(generic.BulkEditView):
 class VLANTranslationPolicyBulkEditView(generic.BulkEditView):
     queryset = VLANTranslationPolicy.objects.all()
     queryset = VLANTranslationPolicy.objects.all()
     filterset = filtersets.VLANTranslationPolicyFilterSet
     filterset = filtersets.VLANTranslationPolicyFilterSet
@@ -1035,6 +1096,7 @@ class VLANTranslationPolicyBulkEditView(generic.BulkEditView):
     form = forms.VLANTranslationPolicyBulkEditForm
     form = forms.VLANTranslationPolicyBulkEditForm
 
 
 
 
+@register_model_view(VLANTranslationPolicy, 'bulk_delete', path='delete', detail=False)
 class VLANTranslationPolicyBulkDeleteView(generic.BulkDeleteView):
 class VLANTranslationPolicyBulkDeleteView(generic.BulkDeleteView):
     queryset = VLANTranslationPolicy.objects.all()
     queryset = VLANTranslationPolicy.objects.all()
     filterset = filtersets.VLANTranslationPolicyFilterSet
     filterset = filtersets.VLANTranslationPolicyFilterSet
@@ -1045,6 +1107,7 @@ class VLANTranslationPolicyBulkDeleteView(generic.BulkDeleteView):
 # VLAN Translation Rules
 # VLAN Translation Rules
 #
 #
 
 
+@register_model_view(VLANTranslationRule, 'list', path='', detail=False)
 class VLANTranslationRuleListView(generic.ObjectListView):
 class VLANTranslationRuleListView(generic.ObjectListView):
     queryset = VLANTranslationRule.objects.all()
     queryset = VLANTranslationRule.objects.all()
     filterset = filtersets.VLANTranslationRuleFilterSet
     filterset = filtersets.VLANTranslationRuleFilterSet
@@ -1062,6 +1125,7 @@ class VLANTranslationRuleView(GetRelatedModelsMixin, generic.ObjectView):
         }
         }
 
 
 
 
+@register_model_view(VLANTranslationRule, 'add', detail=False)
 @register_model_view(VLANTranslationRule, 'edit')
 @register_model_view(VLANTranslationRule, 'edit')
 class VLANTranslationRuleEditView(generic.ObjectEditView):
 class VLANTranslationRuleEditView(generic.ObjectEditView):
     queryset = VLANTranslationRule.objects.all()
     queryset = VLANTranslationRule.objects.all()
@@ -1073,11 +1137,13 @@ class VLANTranslationRuleDeleteView(generic.ObjectDeleteView):
     queryset = VLANTranslationRule.objects.all()
     queryset = VLANTranslationRule.objects.all()
 
 
 
 
+@register_model_view(VLANTranslationRule, 'import', detail=False)
 class VLANTranslationRuleBulkImportView(generic.BulkImportView):
 class VLANTranslationRuleBulkImportView(generic.BulkImportView):
     queryset = VLANTranslationRule.objects.all()
     queryset = VLANTranslationRule.objects.all()
     model_form = forms.VLANTranslationRuleImportForm
     model_form = forms.VLANTranslationRuleImportForm
 
 
 
 
+@register_model_view(VLANTranslationRule, 'bulk_edit', path='edit', detail=False)
 class VLANTranslationRuleBulkEditView(generic.BulkEditView):
 class VLANTranslationRuleBulkEditView(generic.BulkEditView):
     queryset = VLANTranslationRule.objects.all()
     queryset = VLANTranslationRule.objects.all()
     filterset = filtersets.VLANTranslationRuleFilterSet
     filterset = filtersets.VLANTranslationRuleFilterSet
@@ -1085,6 +1151,7 @@ class VLANTranslationRuleBulkEditView(generic.BulkEditView):
     form = forms.VLANTranslationRuleBulkEditForm
     form = forms.VLANTranslationRuleBulkEditForm
 
 
 
 
+@register_model_view(VLANTranslationRule, 'bulk_delete', path='delete', detail=False)
 class VLANTranslationRuleBulkDeleteView(generic.BulkDeleteView):
 class VLANTranslationRuleBulkDeleteView(generic.BulkDeleteView):
     queryset = VLANTranslationRule.objects.all()
     queryset = VLANTranslationRule.objects.all()
     filterset = filtersets.VLANTranslationRuleFilterSet
     filterset = filtersets.VLANTranslationRuleFilterSet
@@ -1095,6 +1162,7 @@ class VLANTranslationRuleBulkDeleteView(generic.BulkDeleteView):
 # FHRP groups
 # FHRP groups
 #
 #
 
 
+@register_model_view(FHRPGroup, 'list', path='', detail=False)
 class FHRPGroupListView(generic.ObjectListView):
 class FHRPGroupListView(generic.ObjectListView):
     queryset = FHRPGroup.objects.annotate(
     queryset = FHRPGroup.objects.annotate(
         member_count=count_related(FHRPGroupAssignment, 'group')
         member_count=count_related(FHRPGroupAssignment, 'group')
@@ -1122,6 +1190,7 @@ class FHRPGroupView(generic.ObjectView):
         }
         }
 
 
 
 
+@register_model_view(FHRPGroup, 'add', detail=False)
 @register_model_view(FHRPGroup, 'edit')
 @register_model_view(FHRPGroup, 'edit')
 class FHRPGroupEditView(generic.ObjectEditView):
 class FHRPGroupEditView(generic.ObjectEditView):
     queryset = FHRPGroup.objects.all()
     queryset = FHRPGroup.objects.all()
@@ -1149,11 +1218,13 @@ class FHRPGroupDeleteView(generic.ObjectDeleteView):
     queryset = FHRPGroup.objects.all()
     queryset = FHRPGroup.objects.all()
 
 
 
 
+@register_model_view(FHRPGroup, 'import', detail=False)
 class FHRPGroupBulkImportView(generic.BulkImportView):
 class FHRPGroupBulkImportView(generic.BulkImportView):
     queryset = FHRPGroup.objects.all()
     queryset = FHRPGroup.objects.all()
     model_form = forms.FHRPGroupImportForm
     model_form = forms.FHRPGroupImportForm
 
 
 
 
+@register_model_view(FHRPGroup, 'bulk_edit', path='edit', detail=False)
 class FHRPGroupBulkEditView(generic.BulkEditView):
 class FHRPGroupBulkEditView(generic.BulkEditView):
     queryset = FHRPGroup.objects.all()
     queryset = FHRPGroup.objects.all()
     filterset = filtersets.FHRPGroupFilterSet
     filterset = filtersets.FHRPGroupFilterSet
@@ -1161,6 +1232,7 @@ class FHRPGroupBulkEditView(generic.BulkEditView):
     form = forms.FHRPGroupBulkEditForm
     form = forms.FHRPGroupBulkEditForm
 
 
 
 
+@register_model_view(FHRPGroup, 'bulk_delete', path='delete', detail=False)
 class FHRPGroupBulkDeleteView(generic.BulkDeleteView):
 class FHRPGroupBulkDeleteView(generic.BulkDeleteView):
     queryset = FHRPGroup.objects.all()
     queryset = FHRPGroup.objects.all()
     filterset = filtersets.FHRPGroupFilterSet
     filterset = filtersets.FHRPGroupFilterSet
@@ -1171,6 +1243,7 @@ class FHRPGroupBulkDeleteView(generic.BulkDeleteView):
 # FHRP group assignments
 # FHRP group assignments
 #
 #
 
 
+@register_model_view(FHRPGroupAssignment, 'add', detail=False)
 @register_model_view(FHRPGroupAssignment, 'edit')
 @register_model_view(FHRPGroupAssignment, 'edit')
 class FHRPGroupAssignmentEditView(generic.ObjectEditView):
 class FHRPGroupAssignmentEditView(generic.ObjectEditView):
     queryset = FHRPGroupAssignment.objects.all()
     queryset = FHRPGroupAssignment.objects.all()
@@ -1199,6 +1272,7 @@ class FHRPGroupAssignmentDeleteView(generic.ObjectDeleteView):
 # VLANs
 # VLANs
 #
 #
 
 
+@register_model_view(VLAN, 'list', path='', detail=False)
 class VLANListView(generic.ObjectListView):
 class VLANListView(generic.ObjectListView):
     queryset = VLAN.objects.all()
     queryset = VLAN.objects.all()
     filterset = filtersets.VLANFilterSet
     filterset = filtersets.VLANFilterSet
@@ -1257,6 +1331,7 @@ class VLANVMInterfacesView(generic.ObjectChildrenView):
         return parent.get_vminterfaces().restrict(request.user, 'view')
         return parent.get_vminterfaces().restrict(request.user, 'view')
 
 
 
 
+@register_model_view(VLAN, 'add', detail=False)
 @register_model_view(VLAN, 'edit')
 @register_model_view(VLAN, 'edit')
 class VLANEditView(generic.ObjectEditView):
 class VLANEditView(generic.ObjectEditView):
     queryset = VLAN.objects.all()
     queryset = VLAN.objects.all()
@@ -1269,11 +1344,13 @@ class VLANDeleteView(generic.ObjectDeleteView):
     queryset = VLAN.objects.all()
     queryset = VLAN.objects.all()
 
 
 
 
+@register_model_view(VLAN, 'import', detail=False)
 class VLANBulkImportView(generic.BulkImportView):
 class VLANBulkImportView(generic.BulkImportView):
     queryset = VLAN.objects.all()
     queryset = VLAN.objects.all()
     model_form = forms.VLANImportForm
     model_form = forms.VLANImportForm
 
 
 
 
+@register_model_view(VLAN, 'bulk_edit', path='edit', detail=False)
 class VLANBulkEditView(generic.BulkEditView):
 class VLANBulkEditView(generic.BulkEditView):
     queryset = VLAN.objects.all()
     queryset = VLAN.objects.all()
     filterset = filtersets.VLANFilterSet
     filterset = filtersets.VLANFilterSet
@@ -1281,6 +1358,7 @@ class VLANBulkEditView(generic.BulkEditView):
     form = forms.VLANBulkEditForm
     form = forms.VLANBulkEditForm
 
 
 
 
+@register_model_view(VLAN, 'bulk_delete', path='delete', detail=False)
 class VLANBulkDeleteView(generic.BulkDeleteView):
 class VLANBulkDeleteView(generic.BulkDeleteView):
     queryset = VLAN.objects.all()
     queryset = VLAN.objects.all()
     filterset = filtersets.VLANFilterSet
     filterset = filtersets.VLANFilterSet
@@ -1291,6 +1369,7 @@ class VLANBulkDeleteView(generic.BulkDeleteView):
 # Service templates
 # Service templates
 #
 #
 
 
+@register_model_view(ServiceTemplate, 'list', path='', detail=False)
 class ServiceTemplateListView(generic.ObjectListView):
 class ServiceTemplateListView(generic.ObjectListView):
     queryset = ServiceTemplate.objects.all()
     queryset = ServiceTemplate.objects.all()
     filterset = filtersets.ServiceTemplateFilterSet
     filterset = filtersets.ServiceTemplateFilterSet
@@ -1303,6 +1382,7 @@ class ServiceTemplateView(generic.ObjectView):
     queryset = ServiceTemplate.objects.all()
     queryset = ServiceTemplate.objects.all()
 
 
 
 
+@register_model_view(ServiceTemplate, 'add', detail=False)
 @register_model_view(ServiceTemplate, 'edit')
 @register_model_view(ServiceTemplate, 'edit')
 class ServiceTemplateEditView(generic.ObjectEditView):
 class ServiceTemplateEditView(generic.ObjectEditView):
     queryset = ServiceTemplate.objects.all()
     queryset = ServiceTemplate.objects.all()
@@ -1314,11 +1394,13 @@ class ServiceTemplateDeleteView(generic.ObjectDeleteView):
     queryset = ServiceTemplate.objects.all()
     queryset = ServiceTemplate.objects.all()
 
 
 
 
+@register_model_view(ServiceTemplate, 'import', detail=False)
 class ServiceTemplateBulkImportView(generic.BulkImportView):
 class ServiceTemplateBulkImportView(generic.BulkImportView):
     queryset = ServiceTemplate.objects.all()
     queryset = ServiceTemplate.objects.all()
     model_form = forms.ServiceTemplateImportForm
     model_form = forms.ServiceTemplateImportForm
 
 
 
 
+@register_model_view(ServiceTemplate, 'bulk_edit', path='edit', detail=False)
 class ServiceTemplateBulkEditView(generic.BulkEditView):
 class ServiceTemplateBulkEditView(generic.BulkEditView):
     queryset = ServiceTemplate.objects.all()
     queryset = ServiceTemplate.objects.all()
     filterset = filtersets.ServiceTemplateFilterSet
     filterset = filtersets.ServiceTemplateFilterSet
@@ -1326,6 +1408,7 @@ class ServiceTemplateBulkEditView(generic.BulkEditView):
     form = forms.ServiceTemplateBulkEditForm
     form = forms.ServiceTemplateBulkEditForm
 
 
 
 
+@register_model_view(ServiceTemplate, 'bulk_delete', path='delete', detail=False)
 class ServiceTemplateBulkDeleteView(generic.BulkDeleteView):
 class ServiceTemplateBulkDeleteView(generic.BulkDeleteView):
     queryset = ServiceTemplate.objects.all()
     queryset = ServiceTemplate.objects.all()
     filterset = filtersets.ServiceTemplateFilterSet
     filterset = filtersets.ServiceTemplateFilterSet
@@ -1336,6 +1419,7 @@ class ServiceTemplateBulkDeleteView(generic.BulkDeleteView):
 # Services
 # Services
 #
 #
 
 
+@register_model_view(Service, 'list', path='', detail=False)
 class ServiceListView(generic.ObjectListView):
 class ServiceListView(generic.ObjectListView):
     queryset = Service.objects.prefetch_related('device', 'virtual_machine')
     queryset = Service.objects.prefetch_related('device', 'virtual_machine')
     filterset = filtersets.ServiceFilterSet
     filterset = filtersets.ServiceFilterSet
@@ -1348,6 +1432,7 @@ class ServiceView(generic.ObjectView):
     queryset = Service.objects.all()
     queryset = Service.objects.all()
 
 
 
 
+@register_model_view(Service, 'add', detail=False)
 class ServiceCreateView(generic.ObjectEditView):
 class ServiceCreateView(generic.ObjectEditView):
     queryset = Service.objects.all()
     queryset = Service.objects.all()
     form = forms.ServiceCreateForm
     form = forms.ServiceCreateForm
@@ -1364,11 +1449,13 @@ class ServiceDeleteView(generic.ObjectDeleteView):
     queryset = Service.objects.all()
     queryset = Service.objects.all()
 
 
 
 
+@register_model_view(Service, 'import', detail=False)
 class ServiceBulkImportView(generic.BulkImportView):
 class ServiceBulkImportView(generic.BulkImportView):
     queryset = Service.objects.all()
     queryset = Service.objects.all()
     model_form = forms.ServiceImportForm
     model_form = forms.ServiceImportForm
 
 
 
 
+@register_model_view(Service, 'bulk_edit', path='edit', detail=False)
 class ServiceBulkEditView(generic.BulkEditView):
 class ServiceBulkEditView(generic.BulkEditView):
     queryset = Service.objects.prefetch_related('device', 'virtual_machine')
     queryset = Service.objects.prefetch_related('device', 'virtual_machine')
     filterset = filtersets.ServiceFilterSet
     filterset = filtersets.ServiceFilterSet
@@ -1376,6 +1463,7 @@ class ServiceBulkEditView(generic.BulkEditView):
     form = forms.ServiceBulkEditForm
     form = forms.ServiceBulkEditForm
 
 
 
 
+@register_model_view(Service, 'bulk_delete', path='delete', detail=False)
 class ServiceBulkDeleteView(generic.BulkDeleteView):
 class ServiceBulkDeleteView(generic.BulkDeleteView):
     queryset = Service.objects.prefetch_related('device', 'virtual_machine')
     queryset = Service.objects.prefetch_related('device', 'virtual_machine')
     filterset = filtersets.ServiceFilterSet
     filterset = filtersets.ServiceFilterSet

+ 7 - 37
netbox/tenancy/urls.py

@@ -1,57 +1,27 @@
 from django.urls import include, path
 from django.urls import include, path
 
 
 from utilities.urls import get_model_urls
 from utilities.urls import get_model_urls
-from . import views
+from . import views  # noqa F401
 
 
 app_name = 'tenancy'
 app_name = 'tenancy'
 urlpatterns = [
 urlpatterns = [
 
 
-    # Tenant groups
-    path('tenant-groups/', views.TenantGroupListView.as_view(), name='tenantgroup_list'),
-    path('tenant-groups/add/', views.TenantGroupEditView.as_view(), name='tenantgroup_add'),
-    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/', include(get_model_urls('tenancy', 'tenantgroup', detail=False))),
     path('tenant-groups/<int:pk>/', include(get_model_urls('tenancy', 'tenantgroup'))),
     path('tenant-groups/<int:pk>/', include(get_model_urls('tenancy', 'tenantgroup'))),
 
 
-    # Tenants
-    path('tenants/', views.TenantListView.as_view(), name='tenant_list'),
-    path('tenants/add/', views.TenantEditView.as_view(), name='tenant_add'),
-    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/', include(get_model_urls('tenancy', 'tenant', detail=False))),
     path('tenants/<int:pk>/', include(get_model_urls('tenancy', 'tenant'))),
     path('tenants/<int:pk>/', include(get_model_urls('tenancy', 'tenant'))),
 
 
-    # Contact groups
-    path('contact-groups/', views.ContactGroupListView.as_view(), name='contactgroup_list'),
-    path('contact-groups/add/', views.ContactGroupEditView.as_view(), name='contactgroup_add'),
-    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/', include(get_model_urls('tenancy', 'contactgroup', detail=False))),
     path('contact-groups/<int:pk>/', include(get_model_urls('tenancy', 'contactgroup'))),
     path('contact-groups/<int:pk>/', include(get_model_urls('tenancy', 'contactgroup'))),
 
 
-    # Contact roles
-    path('contact-roles/', views.ContactRoleListView.as_view(), name='contactrole_list'),
-    path('contact-roles/add/', views.ContactRoleEditView.as_view(), name='contactrole_add'),
-    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/', include(get_model_urls('tenancy', 'contactrole', detail=False))),
     path('contact-roles/<int:pk>/', include(get_model_urls('tenancy', 'contactrole'))),
     path('contact-roles/<int:pk>/', include(get_model_urls('tenancy', 'contactrole'))),
 
 
-    # Contacts
-    path('contacts/', views.ContactListView.as_view(), name='contact_list'),
-    path('contacts/add/', views.ContactEditView.as_view(), name='contact_add'),
-    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/', include(get_model_urls('tenancy', 'contact', detail=False))),
     path('contacts/<int:pk>/', include(get_model_urls('tenancy', 'contact'))),
     path('contacts/<int:pk>/', include(get_model_urls('tenancy', 'contact'))),
 
 
-    # Contact assignments
-    path('contact-assignments/', views.ContactAssignmentListView.as_view(), name='contactassignment_list'),
-    path('contact-assignments/add/', views.ContactAssignmentEditView.as_view(), name='contactassignment_add'),
-    path('contact-assignments/import/', views.ContactAssignmentBulkImportView.as_view(), name='contactassignment_import'),
-    path('contact-assignments/edit/', views.ContactAssignmentBulkEditView.as_view(), name='contactassignment_bulk_edit'),
-    path('contact-assignments/delete/', views.ContactAssignmentBulkDeleteView.as_view(), name='contactassignment_bulk_delete'),
+    path('contact-assignments/', include(get_model_urls('tenancy', 'contactassignment', detail=False))),
     path('contact-assignments/<int:pk>/', include(get_model_urls('tenancy', 'contactassignment'))),
     path('contact-assignments/<int:pk>/', include(get_model_urls('tenancy', 'contactassignment'))),
 
 
 ]
 ]

+ 37 - 7
netbox/tenancy/views.py

@@ -37,11 +37,12 @@ class ObjectContactsView(generic.ObjectChildrenView):
 
 
         return table
         return table
 
 
+
 #
 #
 # Tenant groups
 # Tenant groups
 #
 #
 
 
-
+@register_model_view(TenantGroup, 'list', path='', detail=False)
 class TenantGroupListView(generic.ObjectListView):
 class TenantGroupListView(generic.ObjectListView):
     queryset = TenantGroup.objects.add_related_count(
     queryset = TenantGroup.objects.add_related_count(
         TenantGroup.objects.all(),
         TenantGroup.objects.all(),
@@ -67,6 +68,7 @@ class TenantGroupView(GetRelatedModelsMixin, generic.ObjectView):
         }
         }
 
 
 
 
+@register_model_view(TenantGroup, 'add', detail=False)
 @register_model_view(TenantGroup, 'edit')
 @register_model_view(TenantGroup, 'edit')
 class TenantGroupEditView(generic.ObjectEditView):
 class TenantGroupEditView(generic.ObjectEditView):
     queryset = TenantGroup.objects.all()
     queryset = TenantGroup.objects.all()
@@ -78,11 +80,13 @@ class TenantGroupDeleteView(generic.ObjectDeleteView):
     queryset = TenantGroup.objects.all()
     queryset = TenantGroup.objects.all()
 
 
 
 
+@register_model_view(TenantGroup, 'import', detail=False)
 class TenantGroupBulkImportView(generic.BulkImportView):
 class TenantGroupBulkImportView(generic.BulkImportView):
     queryset = TenantGroup.objects.all()
     queryset = TenantGroup.objects.all()
     model_form = forms.TenantGroupImportForm
     model_form = forms.TenantGroupImportForm
 
 
 
 
+@register_model_view(TenantGroup, 'bulk_edit', path='edit', detail=False)
 class TenantGroupBulkEditView(generic.BulkEditView):
 class TenantGroupBulkEditView(generic.BulkEditView):
     queryset = TenantGroup.objects.add_related_count(
     queryset = TenantGroup.objects.add_related_count(
         TenantGroup.objects.all(),
         TenantGroup.objects.all(),
@@ -96,6 +100,7 @@ class TenantGroupBulkEditView(generic.BulkEditView):
     form = forms.TenantGroupBulkEditForm
     form = forms.TenantGroupBulkEditForm
 
 
 
 
+@register_model_view(TenantGroup, 'bulk_delete', path='delete', detail=False)
 class TenantGroupBulkDeleteView(generic.BulkDeleteView):
 class TenantGroupBulkDeleteView(generic.BulkDeleteView):
     queryset = TenantGroup.objects.add_related_count(
     queryset = TenantGroup.objects.add_related_count(
         TenantGroup.objects.all(),
         TenantGroup.objects.all(),
@@ -112,6 +117,7 @@ class TenantGroupBulkDeleteView(generic.BulkDeleteView):
 #  Tenants
 #  Tenants
 #
 #
 
 
+@register_model_view(Tenant, 'list', path='', detail=False)
 class TenantListView(generic.ObjectListView):
 class TenantListView(generic.ObjectListView):
     queryset = Tenant.objects.all()
     queryset = Tenant.objects.all()
     filterset = filtersets.TenantFilterSet
     filterset = filtersets.TenantFilterSet
@@ -129,6 +135,7 @@ class TenantView(GetRelatedModelsMixin, generic.ObjectView):
         }
         }
 
 
 
 
+@register_model_view(Tenant, 'add', detail=False)
 @register_model_view(Tenant, 'edit')
 @register_model_view(Tenant, 'edit')
 class TenantEditView(generic.ObjectEditView):
 class TenantEditView(generic.ObjectEditView):
     queryset = Tenant.objects.all()
     queryset = Tenant.objects.all()
@@ -140,11 +147,13 @@ class TenantDeleteView(generic.ObjectDeleteView):
     queryset = Tenant.objects.all()
     queryset = Tenant.objects.all()
 
 
 
 
+@register_model_view(Tenant, 'import', detail=False)
 class TenantBulkImportView(generic.BulkImportView):
 class TenantBulkImportView(generic.BulkImportView):
     queryset = Tenant.objects.all()
     queryset = Tenant.objects.all()
     model_form = forms.TenantImportForm
     model_form = forms.TenantImportForm
 
 
 
 
+@register_model_view(Tenant, 'bulk_edit', path='edit', detail=False)
 class TenantBulkEditView(generic.BulkEditView):
 class TenantBulkEditView(generic.BulkEditView):
     queryset = Tenant.objects.all()
     queryset = Tenant.objects.all()
     filterset = filtersets.TenantFilterSet
     filterset = filtersets.TenantFilterSet
@@ -152,6 +161,7 @@ class TenantBulkEditView(generic.BulkEditView):
     form = forms.TenantBulkEditForm
     form = forms.TenantBulkEditForm
 
 
 
 
+@register_model_view(Tenant, 'bulk_delete', path='delete', detail=False)
 class TenantBulkDeleteView(generic.BulkDeleteView):
 class TenantBulkDeleteView(generic.BulkDeleteView):
     queryset = Tenant.objects.all()
     queryset = Tenant.objects.all()
     filterset = filtersets.TenantFilterSet
     filterset = filtersets.TenantFilterSet
@@ -167,6 +177,7 @@ class TenantContactsView(ObjectContactsView):
 # Contact groups
 # Contact groups
 #
 #
 
 
+@register_model_view(ContactGroup, 'list', path='', detail=False)
 class ContactGroupListView(generic.ObjectListView):
 class ContactGroupListView(generic.ObjectListView):
     queryset = ContactGroup.objects.add_related_count(
     queryset = ContactGroup.objects.add_related_count(
         ContactGroup.objects.all(),
         ContactGroup.objects.all(),
@@ -192,6 +203,7 @@ class ContactGroupView(GetRelatedModelsMixin, generic.ObjectView):
         }
         }
 
 
 
 
+@register_model_view(ContactGroup, 'add', detail=False)
 @register_model_view(ContactGroup, 'edit')
 @register_model_view(ContactGroup, 'edit')
 class ContactGroupEditView(generic.ObjectEditView):
 class ContactGroupEditView(generic.ObjectEditView):
     queryset = ContactGroup.objects.all()
     queryset = ContactGroup.objects.all()
@@ -203,11 +215,13 @@ class ContactGroupDeleteView(generic.ObjectDeleteView):
     queryset = ContactGroup.objects.all()
     queryset = ContactGroup.objects.all()
 
 
 
 
+@register_model_view(ContactGroup, 'import', detail=False)
 class ContactGroupBulkImportView(generic.BulkImportView):
 class ContactGroupBulkImportView(generic.BulkImportView):
     queryset = ContactGroup.objects.all()
     queryset = ContactGroup.objects.all()
     model_form = forms.ContactGroupImportForm
     model_form = forms.ContactGroupImportForm
 
 
 
 
+@register_model_view(ContactGroup, 'bulk_edit', path='edit', detail=False)
 class ContactGroupBulkEditView(generic.BulkEditView):
 class ContactGroupBulkEditView(generic.BulkEditView):
     queryset = ContactGroup.objects.add_related_count(
     queryset = ContactGroup.objects.add_related_count(
         ContactGroup.objects.all(),
         ContactGroup.objects.all(),
@@ -221,6 +235,7 @@ class ContactGroupBulkEditView(generic.BulkEditView):
     form = forms.ContactGroupBulkEditForm
     form = forms.ContactGroupBulkEditForm
 
 
 
 
+@register_model_view(ContactGroup, 'bulk_delete', path='delete', detail=False)
 class ContactGroupBulkDeleteView(generic.BulkDeleteView):
 class ContactGroupBulkDeleteView(generic.BulkDeleteView):
     queryset = ContactGroup.objects.add_related_count(
     queryset = ContactGroup.objects.add_related_count(
         ContactGroup.objects.all(),
         ContactGroup.objects.all(),
@@ -237,6 +252,7 @@ class ContactGroupBulkDeleteView(generic.BulkDeleteView):
 # Contact roles
 # Contact roles
 #
 #
 
 
+@register_model_view(ContactRole, 'list', path='', detail=False)
 class ContactRoleListView(generic.ObjectListView):
 class ContactRoleListView(generic.ObjectListView):
     queryset = ContactRole.objects.all()
     queryset = ContactRole.objects.all()
     filterset = filtersets.ContactRoleFilterSet
     filterset = filtersets.ContactRoleFilterSet
@@ -254,6 +270,7 @@ class ContactRoleView(GetRelatedModelsMixin, generic.ObjectView):
         }
         }
 
 
 
 
+@register_model_view(ContactRole, 'add', detail=False)
 @register_model_view(ContactRole, 'edit')
 @register_model_view(ContactRole, 'edit')
 class ContactRoleEditView(generic.ObjectEditView):
 class ContactRoleEditView(generic.ObjectEditView):
     queryset = ContactRole.objects.all()
     queryset = ContactRole.objects.all()
@@ -265,11 +282,13 @@ class ContactRoleDeleteView(generic.ObjectDeleteView):
     queryset = ContactRole.objects.all()
     queryset = ContactRole.objects.all()
 
 
 
 
+@register_model_view(ContactRole, 'import', detail=False)
 class ContactRoleBulkImportView(generic.BulkImportView):
 class ContactRoleBulkImportView(generic.BulkImportView):
     queryset = ContactRole.objects.all()
     queryset = ContactRole.objects.all()
     model_form = forms.ContactRoleImportForm
     model_form = forms.ContactRoleImportForm
 
 
 
 
+@register_model_view(ContactRole, 'bulk_edit', path='edit', detail=False)
 class ContactRoleBulkEditView(generic.BulkEditView):
 class ContactRoleBulkEditView(generic.BulkEditView):
     queryset = ContactRole.objects.all()
     queryset = ContactRole.objects.all()
     filterset = filtersets.ContactRoleFilterSet
     filterset = filtersets.ContactRoleFilterSet
@@ -277,6 +296,7 @@ class ContactRoleBulkEditView(generic.BulkEditView):
     form = forms.ContactRoleBulkEditForm
     form = forms.ContactRoleBulkEditForm
 
 
 
 
+@register_model_view(ContactRole, 'bulk_delete', path='delete', detail=False)
 class ContactRoleBulkDeleteView(generic.BulkDeleteView):
 class ContactRoleBulkDeleteView(generic.BulkDeleteView):
     queryset = ContactRole.objects.all()
     queryset = ContactRole.objects.all()
     filterset = filtersets.ContactRoleFilterSet
     filterset = filtersets.ContactRoleFilterSet
@@ -287,6 +307,7 @@ class ContactRoleBulkDeleteView(generic.BulkDeleteView):
 # Contacts
 # Contacts
 #
 #
 
 
+@register_model_view(Contact, 'list', path='', detail=False)
 class ContactListView(generic.ObjectListView):
 class ContactListView(generic.ObjectListView):
     queryset = Contact.objects.annotate(
     queryset = Contact.objects.annotate(
         assignment_count=count_related(ContactAssignment, 'contact')
         assignment_count=count_related(ContactAssignment, 'contact')
@@ -301,6 +322,7 @@ class ContactView(generic.ObjectView):
     queryset = Contact.objects.all()
     queryset = Contact.objects.all()
 
 
 
 
+@register_model_view(Contact, 'add', detail=False)
 @register_model_view(Contact, 'edit')
 @register_model_view(Contact, 'edit')
 class ContactEditView(generic.ObjectEditView):
 class ContactEditView(generic.ObjectEditView):
     queryset = Contact.objects.all()
     queryset = Contact.objects.all()
@@ -312,11 +334,13 @@ class ContactDeleteView(generic.ObjectDeleteView):
     queryset = Contact.objects.all()
     queryset = Contact.objects.all()
 
 
 
 
+@register_model_view(Contact, 'import', detail=False)
 class ContactBulkImportView(generic.BulkImportView):
 class ContactBulkImportView(generic.BulkImportView):
     queryset = Contact.objects.all()
     queryset = Contact.objects.all()
     model_form = forms.ContactImportForm
     model_form = forms.ContactImportForm
 
 
 
 
+@register_model_view(Contact, 'bulk_edit', path='edit', detail=False)
 class ContactBulkEditView(generic.BulkEditView):
 class ContactBulkEditView(generic.BulkEditView):
     queryset = Contact.objects.annotate(
     queryset = Contact.objects.annotate(
         assignment_count=count_related(ContactAssignment, 'contact')
         assignment_count=count_related(ContactAssignment, 'contact')
@@ -326,6 +350,7 @@ class ContactBulkEditView(generic.BulkEditView):
     form = forms.ContactBulkEditForm
     form = forms.ContactBulkEditForm
 
 
 
 
+@register_model_view(Contact, 'bulk_delete', path='delete', detail=False)
 class ContactBulkDeleteView(generic.BulkDeleteView):
 class ContactBulkDeleteView(generic.BulkDeleteView):
     queryset = Contact.objects.annotate(
     queryset = Contact.objects.annotate(
         assignment_count=count_related(ContactAssignment, 'contact')
         assignment_count=count_related(ContactAssignment, 'contact')
@@ -333,11 +358,12 @@ class ContactBulkDeleteView(generic.BulkDeleteView):
     filterset = filtersets.ContactFilterSet
     filterset = filtersets.ContactFilterSet
     table = tables.ContactTable
     table = tables.ContactTable
 
 
+
 #
 #
 # Contact assignments
 # Contact assignments
 #
 #
 
 
-
+@register_model_view(ContactAssignment, 'list', path='', detail=False)
 class ContactAssignmentListView(generic.ObjectListView):
 class ContactAssignmentListView(generic.ObjectListView):
     queryset = ContactAssignment.objects.all()
     queryset = ContactAssignment.objects.all()
     filterset = filtersets.ContactAssignmentFilterSet
     filterset = filtersets.ContactAssignmentFilterSet
@@ -351,6 +377,7 @@ class ContactAssignmentListView(generic.ObjectListView):
     }
     }
 
 
 
 
+@register_model_view(ContactAssignment, 'add', detail=False)
 @register_model_view(ContactAssignment, 'edit')
 @register_model_view(ContactAssignment, 'edit')
 class ContactAssignmentEditView(generic.ObjectEditView):
 class ContactAssignmentEditView(generic.ObjectEditView):
     queryset = ContactAssignment.objects.all()
     queryset = ContactAssignment.objects.all()
@@ -370,6 +397,13 @@ class ContactAssignmentEditView(generic.ObjectEditView):
         }
         }
 
 
 
 
+@register_model_view(ContactAssignment, 'import', detail=False)
+class ContactAssignmentBulkImportView(generic.BulkImportView):
+    queryset = ContactAssignment.objects.all()
+    model_form = forms.ContactAssignmentImportForm
+
+
+@register_model_view(ContactAssignment, 'bulk_edit', path='edit', detail=False)
 class ContactAssignmentBulkEditView(generic.BulkEditView):
 class ContactAssignmentBulkEditView(generic.BulkEditView):
     queryset = ContactAssignment.objects.all()
     queryset = ContactAssignment.objects.all()
     filterset = filtersets.ContactAssignmentFilterSet
     filterset = filtersets.ContactAssignmentFilterSet
@@ -377,11 +411,7 @@ class ContactAssignmentBulkEditView(generic.BulkEditView):
     form = forms.ContactAssignmentBulkEditForm
     form = forms.ContactAssignmentBulkEditForm
 
 
 
 
-class ContactAssignmentBulkImportView(generic.BulkImportView):
-    queryset = ContactAssignment.objects.all()
-    model_form = forms.ContactAssignmentImportForm
-
-
+@register_model_view(ContactAssignment, 'bulk_delete', path='delete', detail=False)
 class ContactAssignmentBulkDeleteView(generic.BulkDeleteView):
 class ContactAssignmentBulkDeleteView(generic.BulkDeleteView):
     queryset = ContactAssignment.objects.all()
     queryset = ContactAssignment.objects.all()
     filterset = filtersets.ContactAssignmentFilterSet
     filterset = filtersets.ContactAssignmentFilterSet

+ 5 - 24
netbox/users/urls.py

@@ -1,40 +1,21 @@
 from django.urls import include, path
 from django.urls import include, path
 
 
 from utilities.urls import get_model_urls
 from utilities.urls import get_model_urls
-from . import views
+from . import views  # noqa F401
 
 
 app_name = 'users'
 app_name = 'users'
 urlpatterns = [
 urlpatterns = [
 
 
-    # Tokens
-    path('tokens/', views.TokenListView.as_view(), name='token_list'),
-    path('tokens/add/', views.TokenEditView.as_view(), name='token_add'),
-    path('tokens/import/', views.TokenBulkImportView.as_view(), name='token_import'),
-    path('tokens/edit/', views.TokenBulkEditView.as_view(), name='token_bulk_edit'),
-    path('tokens/delete/', views.TokenBulkDeleteView.as_view(), name='token_bulk_delete'),
+    path('tokens/', include(get_model_urls('users', 'token', detail=False))),
     path('tokens/<int:pk>/', include(get_model_urls('users', 'token'))),
     path('tokens/<int:pk>/', include(get_model_urls('users', 'token'))),
 
 
-    # Users
-    path('users/', views.UserListView.as_view(), name='user_list'),
-    path('users/add/', views.UserEditView.as_view(), name='user_add'),
-    path('users/edit/', views.UserBulkEditView.as_view(), name='user_bulk_edit'),
-    path('users/import/', views.UserBulkImportView.as_view(), name='user_import'),
-    path('users/delete/', views.UserBulkDeleteView.as_view(), name='user_bulk_delete'),
+    path('users/', include(get_model_urls('users', 'user', detail=False))),
     path('users/<int:pk>/', include(get_model_urls('users', 'user'))),
     path('users/<int:pk>/', include(get_model_urls('users', 'user'))),
 
 
-    # Groups
-    path('groups/', views.GroupListView.as_view(), name='group_list'),
-    path('groups/add/', views.GroupEditView.as_view(), name='group_add'),
-    path('groups/edit/', views.GroupBulkEditView.as_view(), name='group_bulk_edit'),
-    path('groups/import/', views.GroupBulkImportView.as_view(), name='group_import'),
-    path('groups/delete/', views.GroupBulkDeleteView.as_view(), name='group_bulk_delete'),
+    path('groups/', include(get_model_urls('users', 'group', detail=False))),
     path('groups/<int:pk>/', include(get_model_urls('users', 'group'))),
     path('groups/<int:pk>/', include(get_model_urls('users', 'group'))),
 
 
-    # Permissions
-    path('permissions/', views.ObjectPermissionListView.as_view(), name='objectpermission_list'),
-    path('permissions/add/', views.ObjectPermissionEditView.as_view(), name='objectpermission_add'),
-    path('permissions/edit/', views.ObjectPermissionBulkEditView.as_view(), name='objectpermission_bulk_edit'),
-    path('permissions/delete/', views.ObjectPermissionBulkDeleteView.as_view(), name='objectpermission_bulk_delete'),
+    path('permissions/', include(get_model_urls('users', 'objectpermission', detail=False))),
     path('permissions/<int:pk>/', include(get_model_urls('users', 'objectpermission'))),
     path('permissions/<int:pk>/', include(get_model_urls('users', 'objectpermission'))),
 
 
 ]
 ]

+ 24 - 5
netbox/users/views.py

@@ -12,6 +12,7 @@ from .models import Group, User, ObjectPermission, Token
 # Tokens
 # Tokens
 #
 #
 
 
+@register_model_view(Token, 'list', path='', detail=False)
 class TokenListView(generic.ObjectListView):
 class TokenListView(generic.ObjectListView):
     queryset = Token.objects.all()
     queryset = Token.objects.all()
     filterset = filtersets.TokenFilterSet
     filterset = filtersets.TokenFilterSet
@@ -24,6 +25,7 @@ class TokenView(generic.ObjectView):
     queryset = Token.objects.all()
     queryset = Token.objects.all()
 
 
 
 
+@register_model_view(Token, 'add', detail=False)
 @register_model_view(Token, 'edit')
 @register_model_view(Token, 'edit')
 class TokenEditView(generic.ObjectEditView):
 class TokenEditView(generic.ObjectEditView):
     queryset = Token.objects.all()
     queryset = Token.objects.all()
@@ -36,17 +38,20 @@ class TokenDeleteView(generic.ObjectDeleteView):
     queryset = Token.objects.all()
     queryset = Token.objects.all()
 
 
 
 
+@register_model_view(Token, 'import', detail=False)
 class TokenBulkImportView(generic.BulkImportView):
 class TokenBulkImportView(generic.BulkImportView):
     queryset = Token.objects.all()
     queryset = Token.objects.all()
     model_form = forms.TokenImportForm
     model_form = forms.TokenImportForm
 
 
 
 
+@register_model_view(Token, 'bulk_edit', path='edit', detail=False)
 class TokenBulkEditView(generic.BulkEditView):
 class TokenBulkEditView(generic.BulkEditView):
     queryset = Token.objects.all()
     queryset = Token.objects.all()
     table = tables.TokenTable
     table = tables.TokenTable
     form = forms.TokenBulkEditForm
     form = forms.TokenBulkEditForm
 
 
 
 
+@register_model_view(Token, 'bulk_delete', path='delete', detail=False)
 class TokenBulkDeleteView(generic.BulkDeleteView):
 class TokenBulkDeleteView(generic.BulkDeleteView):
     queryset = Token.objects.all()
     queryset = Token.objects.all()
     table = tables.TokenTable
     table = tables.TokenTable
@@ -56,6 +61,7 @@ class TokenBulkDeleteView(generic.BulkDeleteView):
 # Users
 # Users
 #
 #
 
 
+@register_model_view(User, 'list', path='', detail=False)
 class UserListView(generic.ObjectListView):
 class UserListView(generic.ObjectListView):
     queryset = User.objects.all()
     queryset = User.objects.all()
     filterset = filtersets.UserFilterSet
     filterset = filtersets.UserFilterSet
@@ -77,6 +83,7 @@ class UserView(generic.ObjectView):
         }
         }
 
 
 
 
+@register_model_view(User, 'add', detail=False)
 @register_model_view(User, 'edit')
 @register_model_view(User, 'edit')
 class UserEditView(generic.ObjectEditView):
 class UserEditView(generic.ObjectEditView):
     queryset = User.objects.all()
     queryset = User.objects.all()
@@ -88,6 +95,13 @@ class UserDeleteView(generic.ObjectDeleteView):
     queryset = User.objects.all()
     queryset = User.objects.all()
 
 
 
 
+@register_model_view(User, 'import', detail=False)
+class UserBulkImportView(generic.BulkImportView):
+    queryset = User.objects.all()
+    model_form = forms.UserImportForm
+
+
+@register_model_view(User, 'bulk_edit', path='edit', detail=False)
 class UserBulkEditView(generic.BulkEditView):
 class UserBulkEditView(generic.BulkEditView):
     queryset = User.objects.all()
     queryset = User.objects.all()
     filterset = filtersets.UserFilterSet
     filterset = filtersets.UserFilterSet
@@ -95,11 +109,7 @@ class UserBulkEditView(generic.BulkEditView):
     form = forms.UserBulkEditForm
     form = forms.UserBulkEditForm
 
 
 
 
-class UserBulkImportView(generic.BulkImportView):
-    queryset = User.objects.all()
-    model_form = forms.UserImportForm
-
-
+@register_model_view(User, 'bulk_delete', path='delete', detail=False)
 class UserBulkDeleteView(generic.BulkDeleteView):
 class UserBulkDeleteView(generic.BulkDeleteView):
     queryset = User.objects.all()
     queryset = User.objects.all()
     filterset = filtersets.UserFilterSet
     filterset = filtersets.UserFilterSet
@@ -110,6 +120,7 @@ class UserBulkDeleteView(generic.BulkDeleteView):
 # Groups
 # Groups
 #
 #
 
 
+@register_model_view(Group, 'list', path='', detail=False)
 class GroupListView(generic.ObjectListView):
 class GroupListView(generic.ObjectListView):
     queryset = Group.objects.annotate(users_count=Count('user')).order_by('name')
     queryset = Group.objects.annotate(users_count=Count('user')).order_by('name')
     filterset = filtersets.GroupFilterSet
     filterset = filtersets.GroupFilterSet
@@ -123,6 +134,7 @@ class GroupView(generic.ObjectView):
     template_name = 'users/group.html'
     template_name = 'users/group.html'
 
 
 
 
+@register_model_view(Group, 'add', detail=False)
 @register_model_view(Group, 'edit')
 @register_model_view(Group, 'edit')
 class GroupEditView(generic.ObjectEditView):
 class GroupEditView(generic.ObjectEditView):
     queryset = Group.objects.all()
     queryset = Group.objects.all()
@@ -134,11 +146,13 @@ class GroupDeleteView(generic.ObjectDeleteView):
     queryset = Group.objects.all()
     queryset = Group.objects.all()
 
 
 
 
+@register_model_view(Group, 'import', detail=False)
 class GroupBulkImportView(generic.BulkImportView):
 class GroupBulkImportView(generic.BulkImportView):
     queryset = Group.objects.all()
     queryset = Group.objects.all()
     model_form = forms.GroupImportForm
     model_form = forms.GroupImportForm
 
 
 
 
+@register_model_view(Group, 'bulk_edit', path='edit', detail=False)
 class GroupBulkEditView(generic.BulkEditView):
 class GroupBulkEditView(generic.BulkEditView):
     queryset = Group.objects.all()
     queryset = Group.objects.all()
     filterset = filtersets.GroupFilterSet
     filterset = filtersets.GroupFilterSet
@@ -146,6 +160,7 @@ class GroupBulkEditView(generic.BulkEditView):
     form = forms.GroupBulkEditForm
     form = forms.GroupBulkEditForm
 
 
 
 
+@register_model_view(Group, 'bulk_delete', path='delete', detail=False)
 class GroupBulkDeleteView(generic.BulkDeleteView):
 class GroupBulkDeleteView(generic.BulkDeleteView):
     queryset = Group.objects.annotate(users_count=Count('user')).order_by('name')
     queryset = Group.objects.annotate(users_count=Count('user')).order_by('name')
     filterset = filtersets.GroupFilterSet
     filterset = filtersets.GroupFilterSet
@@ -156,6 +171,7 @@ class GroupBulkDeleteView(generic.BulkDeleteView):
 # ObjectPermissions
 # ObjectPermissions
 #
 #
 
 
+@register_model_view(ObjectPermission, 'list', path='', detail=False)
 class ObjectPermissionListView(generic.ObjectListView):
 class ObjectPermissionListView(generic.ObjectListView):
     queryset = ObjectPermission.objects.all()
     queryset = ObjectPermission.objects.all()
     filterset = filtersets.ObjectPermissionFilterSet
     filterset = filtersets.ObjectPermissionFilterSet
@@ -169,6 +185,7 @@ class ObjectPermissionView(generic.ObjectView):
     template_name = 'users/objectpermission.html'
     template_name = 'users/objectpermission.html'
 
 
 
 
+@register_model_view(ObjectPermission, 'add', detail=False)
 @register_model_view(ObjectPermission, 'edit')
 @register_model_view(ObjectPermission, 'edit')
 class ObjectPermissionEditView(generic.ObjectEditView):
 class ObjectPermissionEditView(generic.ObjectEditView):
     queryset = ObjectPermission.objects.all()
     queryset = ObjectPermission.objects.all()
@@ -180,6 +197,7 @@ class ObjectPermissionDeleteView(generic.ObjectDeleteView):
     queryset = ObjectPermission.objects.all()
     queryset = ObjectPermission.objects.all()
 
 
 
 
+@register_model_view(ObjectPermission, 'bulk_edit', path='edit', detail=False)
 class ObjectPermissionBulkEditView(generic.BulkEditView):
 class ObjectPermissionBulkEditView(generic.BulkEditView):
     queryset = ObjectPermission.objects.all()
     queryset = ObjectPermission.objects.all()
     filterset = filtersets.ObjectPermissionFilterSet
     filterset = filtersets.ObjectPermissionFilterSet
@@ -187,6 +205,7 @@ class ObjectPermissionBulkEditView(generic.BulkEditView):
     form = forms.ObjectPermissionBulkEditForm
     form = forms.ObjectPermissionBulkEditForm
 
 
 
 
+@register_model_view(ObjectPermission, 'bulk_delete', path='delete', detail=False)
 class ObjectPermissionBulkDeleteView(generic.BulkDeleteView):
 class ObjectPermissionBulkDeleteView(generic.BulkDeleteView):
     queryset = ObjectPermission.objects.all()
     queryset = ObjectPermission.objects.all()
     filterset = filtersets.ObjectPermissionFilterSet
     filterset = filtersets.ObjectPermissionFilterSet

+ 8 - 3
netbox/utilities/urls.py

@@ -9,22 +9,27 @@ __all__ = (
 )
 )
 
 
 
 
-def get_model_urls(app_label, model_name):
+def get_model_urls(app_label, model_name, detail=True):
     """
     """
     Return a list of URL paths for detail views registered to the given model.
     Return a list of URL paths for detail views registered to the given model.
 
 
     Args:
     Args:
         app_label: App/plugin name
         app_label: App/plugin name
         model_name: Model name
         model_name: Model name
+        detail: If True (default), return only URL views for an individual object.
+            Otherwise, return only list views.
     """
     """
     paths = []
     paths = []
 
 
     # Retrieve registered views for this model
     # Retrieve registered views for this model
     try:
     try:
-        views = registry['views'][app_label][model_name]
+        views = [
+            view for view in registry['views'][app_label][model_name]
+            if view['detail'] == detail
+        ]
     except KeyError:
     except KeyError:
         # No views have been registered for this model
         # No views have been registered for this model
-        views = []
+        return []
 
 
     for config in views:
     for config in views:
         # Import the view class or function
         # Import the view class or function

+ 4 - 2
netbox/utilities/views.py

@@ -272,7 +272,7 @@ def get_viewname(model, action=None, rest_api=False):
     return viewname
     return viewname
 
 
 
 
-def register_model_view(model, name='', path=None, kwargs=None):
+def register_model_view(model, name='', path=None, detail=True, kwargs=None):
     """
     """
     This decorator can be used to "attach" a view to any model in NetBox. This is typically used to inject
     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:
     additional tabs within a model's detail view. For example, to add a custom tab to NetBox's dcim.Site model:
@@ -289,6 +289,7 @@ def register_model_view(model, name='', path=None, kwargs=None):
         name: The string used to form the view's name for URL resolution (e.g. via `reverse()`). This will be appended
         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. If blank, the model name will be used.
             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.
         path: The URL path by which the view can be reached (optional). If not provided, `name` will be used.
+        detail: True if the path applied to an individual object; False if it attaches to the base (list) path.
         kwargs: A dictionary of keyword arguments for the view to include when registering its URL path (optional).
         kwargs: A dictionary of keyword arguments for the view to include when registering its URL path (optional).
     """
     """
     def _wrapper(cls):
     def _wrapper(cls):
@@ -301,7 +302,8 @@ def register_model_view(model, name='', path=None, kwargs=None):
         registry['views'][app_label][model_name].append({
         registry['views'][app_label][model_name].append({
             'name': name,
             'name': name,
             'view': cls,
             'view': cls,
-            'path': path or name,
+            'path': path if path is not None else name,
+            'detail': detail,
             'kwargs': kwargs or {},
             'kwargs': kwargs or {},
         })
         })
 
 

+ 16 - 40
netbox/virtualization/urls.py

@@ -6,57 +6,33 @@ from . import views
 app_name = 'virtualization'
 app_name = 'virtualization'
 urlpatterns = [
 urlpatterns = [
 
 
-    # Cluster types
-    path('cluster-types/', views.ClusterTypeListView.as_view(), name='clustertype_list'),
-    path('cluster-types/add/', views.ClusterTypeEditView.as_view(), name='clustertype_add'),
-    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/', include(get_model_urls('virtualization', 'clustertype', detail=False))),
     path('cluster-types/<int:pk>/', include(get_model_urls('virtualization', 'clustertype'))),
     path('cluster-types/<int:pk>/', include(get_model_urls('virtualization', 'clustertype'))),
 
 
-    # Cluster groups
-    path('cluster-groups/', views.ClusterGroupListView.as_view(), name='clustergroup_list'),
-    path('cluster-groups/add/', views.ClusterGroupEditView.as_view(), name='clustergroup_add'),
-    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/', include(get_model_urls('virtualization', 'clustergroup', detail=False))),
     path('cluster-groups/<int:pk>/', include(get_model_urls('virtualization', 'clustergroup'))),
     path('cluster-groups/<int:pk>/', include(get_model_urls('virtualization', 'clustergroup'))),
 
 
-    # Clusters
-    path('clusters/', views.ClusterListView.as_view(), name='cluster_list'),
-    path('clusters/add/', views.ClusterEditView.as_view(), name='cluster_add'),
-    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/', include(get_model_urls('virtualization', 'cluster', detail=False))),
     path('clusters/<int:pk>/', include(get_model_urls('virtualization', 'cluster'))),
     path('clusters/<int:pk>/', include(get_model_urls('virtualization', 'cluster'))),
 
 
-    # Virtual machines
-    path('virtual-machines/', views.VirtualMachineListView.as_view(), name='virtualmachine_list'),
-    path('virtual-machines/add/', views.VirtualMachineEditView.as_view(), name='virtualmachine_add'),
-    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/', include(get_model_urls('virtualization', 'virtualmachine', detail=False))),
     path('virtual-machines/<int:pk>/', include(get_model_urls('virtualization', 'virtualmachine'))),
     path('virtual-machines/<int:pk>/', include(get_model_urls('virtualization', 'virtualmachine'))),
 
 
-    # VM interfaces
-    path('interfaces/', views.VMInterfaceListView.as_view(), name='vminterface_list'),
-    path('interfaces/add/', views.VMInterfaceCreateView.as_view(), name='vminterface_add'),
-    path('interfaces/import/', views.VMInterfaceBulkImportView.as_view(), name='vminterface_import'),
-    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/', include(get_model_urls('virtualization', 'vminterface', detail=False))),
     path('interfaces/<int:pk>/', include(get_model_urls('virtualization', 'vminterface'))),
     path('interfaces/<int:pk>/', include(get_model_urls('virtualization', 'vminterface'))),
-    path('virtual-machines/interfaces/add/', views.VirtualMachineBulkAddInterfaceView.as_view(), name='virtualmachine_bulk_add_vminterface'),
+    path(
+        'virtual-machines/interfaces/add/',
+        views.VirtualMachineBulkAddInterfaceView.as_view(),
+        name='virtualmachine_bulk_add_vminterface'
+    ),
 
 
-    # Virtual disks
-    path('virtual-disks/', views.VirtualDiskListView.as_view(), name='virtualdisk_list'),
-    path('virtual-disks/add/', views.VirtualDiskCreateView.as_view(), name='virtualdisk_add'),
-    path('virtual-disks/import/', views.VirtualDiskBulkImportView.as_view(), name='virtualdisk_import'),
-    path('virtual-disks/edit/', views.VirtualDiskBulkEditView.as_view(), name='virtualdisk_bulk_edit'),
-    path('virtual-disks/rename/', views.VirtualDiskBulkRenameView.as_view(), name='virtualdisk_bulk_rename'),
-    path('virtual-disks/delete/', views.VirtualDiskBulkDeleteView.as_view(), name='virtualdisk_bulk_delete'),
+    path('virtual-disks/', include(get_model_urls('virtualization', 'virtualdisk', detail=False))),
     path('virtual-disks/<int:pk>/', include(get_model_urls('virtualization', 'virtualdisk'))),
     path('virtual-disks/<int:pk>/', include(get_model_urls('virtualization', 'virtualdisk'))),
-    path('virtual-machines/disks/add/', views.VirtualMachineBulkAddVirtualDiskView.as_view(), name='virtualmachine_bulk_add_virtualdisk'),
+    path(
+        'virtual-machines/disks/add/',
+        views.VirtualMachineBulkAddVirtualDiskView.as_view(),
+        name='virtualmachine_bulk_add_virtualdisk'
+    ),
 
 
     # TODO: Remove in v4.2
     # TODO: Remove in v4.2
     # Redirect old (pre-v4.1) URLs for VirtualDisk views
     # Redirect old (pre-v4.1) URLs for VirtualDisk views

+ 32 - 0
netbox/virtualization/views.py

@@ -31,6 +31,7 @@ from .models import *
 # Cluster types
 # Cluster types
 #
 #
 
 
+@register_model_view(ClusterType, 'list', path='', detail=False)
 class ClusterTypeListView(generic.ObjectListView):
 class ClusterTypeListView(generic.ObjectListView):
     queryset = ClusterType.objects.annotate(
     queryset = ClusterType.objects.annotate(
         cluster_count=count_related(Cluster, 'type')
         cluster_count=count_related(Cluster, 'type')
@@ -50,6 +51,7 @@ class ClusterTypeView(GetRelatedModelsMixin, generic.ObjectView):
         }
         }
 
 
 
 
+@register_model_view(ClusterType, 'add', detail=False)
 @register_model_view(ClusterType, 'edit')
 @register_model_view(ClusterType, 'edit')
 class ClusterTypeEditView(generic.ObjectEditView):
 class ClusterTypeEditView(generic.ObjectEditView):
     queryset = ClusterType.objects.all()
     queryset = ClusterType.objects.all()
@@ -61,11 +63,13 @@ class ClusterTypeDeleteView(generic.ObjectDeleteView):
     queryset = ClusterType.objects.all()
     queryset = ClusterType.objects.all()
 
 
 
 
+@register_model_view(ClusterType, 'import', detail=False)
 class ClusterTypeBulkImportView(generic.BulkImportView):
 class ClusterTypeBulkImportView(generic.BulkImportView):
     queryset = ClusterType.objects.all()
     queryset = ClusterType.objects.all()
     model_form = forms.ClusterTypeImportForm
     model_form = forms.ClusterTypeImportForm
 
 
 
 
+@register_model_view(ClusterType, 'bulk_edit', path='edit', detail=False)
 class ClusterTypeBulkEditView(generic.BulkEditView):
 class ClusterTypeBulkEditView(generic.BulkEditView):
     queryset = ClusterType.objects.annotate(
     queryset = ClusterType.objects.annotate(
         cluster_count=count_related(Cluster, 'type')
         cluster_count=count_related(Cluster, 'type')
@@ -75,6 +79,7 @@ class ClusterTypeBulkEditView(generic.BulkEditView):
     form = forms.ClusterTypeBulkEditForm
     form = forms.ClusterTypeBulkEditForm
 
 
 
 
+@register_model_view(ClusterType, 'bulk_delete', path='delete', detail=False)
 class ClusterTypeBulkDeleteView(generic.BulkDeleteView):
 class ClusterTypeBulkDeleteView(generic.BulkDeleteView):
     queryset = ClusterType.objects.annotate(
     queryset = ClusterType.objects.annotate(
         cluster_count=count_related(Cluster, 'type')
         cluster_count=count_related(Cluster, 'type')
@@ -87,6 +92,7 @@ class ClusterTypeBulkDeleteView(generic.BulkDeleteView):
 # Cluster groups
 # Cluster groups
 #
 #
 
 
+@register_model_view(ClusterGroup, 'list', path='', detail=False)
 class ClusterGroupListView(generic.ObjectListView):
 class ClusterGroupListView(generic.ObjectListView):
     queryset = ClusterGroup.objects.annotate(
     queryset = ClusterGroup.objects.annotate(
         cluster_count=count_related(Cluster, 'group')
         cluster_count=count_related(Cluster, 'group')
@@ -106,6 +112,7 @@ class ClusterGroupView(GetRelatedModelsMixin, generic.ObjectView):
         }
         }
 
 
 
 
+@register_model_view(ClusterGroup, 'add', detail=False)
 @register_model_view(ClusterGroup, 'edit')
 @register_model_view(ClusterGroup, 'edit')
 class ClusterGroupEditView(generic.ObjectEditView):
 class ClusterGroupEditView(generic.ObjectEditView):
     queryset = ClusterGroup.objects.all()
     queryset = ClusterGroup.objects.all()
@@ -117,6 +124,7 @@ class ClusterGroupDeleteView(generic.ObjectDeleteView):
     queryset = ClusterGroup.objects.all()
     queryset = ClusterGroup.objects.all()
 
 
 
 
+@register_model_view(ClusterGroup, 'import', detail=False)
 class ClusterGroupBulkImportView(generic.BulkImportView):
 class ClusterGroupBulkImportView(generic.BulkImportView):
     queryset = ClusterGroup.objects.annotate(
     queryset = ClusterGroup.objects.annotate(
         cluster_count=count_related(Cluster, 'group')
         cluster_count=count_related(Cluster, 'group')
@@ -124,6 +132,7 @@ class ClusterGroupBulkImportView(generic.BulkImportView):
     model_form = forms.ClusterGroupImportForm
     model_form = forms.ClusterGroupImportForm
 
 
 
 
+@register_model_view(ClusterGroup, 'bulk_edit', path='edit', detail=False)
 class ClusterGroupBulkEditView(generic.BulkEditView):
 class ClusterGroupBulkEditView(generic.BulkEditView):
     queryset = ClusterGroup.objects.annotate(
     queryset = ClusterGroup.objects.annotate(
         cluster_count=count_related(Cluster, 'group')
         cluster_count=count_related(Cluster, 'group')
@@ -133,6 +142,7 @@ class ClusterGroupBulkEditView(generic.BulkEditView):
     form = forms.ClusterGroupBulkEditForm
     form = forms.ClusterGroupBulkEditForm
 
 
 
 
+@register_model_view(ClusterGroup, 'bulk_delete', path='delete', detail=False)
 class ClusterGroupBulkDeleteView(generic.BulkDeleteView):
 class ClusterGroupBulkDeleteView(generic.BulkDeleteView):
     queryset = ClusterGroup.objects.annotate(
     queryset = ClusterGroup.objects.annotate(
         cluster_count=count_related(Cluster, 'group')
         cluster_count=count_related(Cluster, 'group')
@@ -150,6 +160,7 @@ class ClusterGroupContactsView(ObjectContactsView):
 # Clusters
 # Clusters
 #
 #
 
 
+@register_model_view(Cluster, 'list', path='', detail=False)
 class ClusterListView(generic.ObjectListView):
 class ClusterListView(generic.ObjectListView):
     permission_required = 'virtualization.view_cluster'
     permission_required = 'virtualization.view_cluster'
     queryset = Cluster.objects.annotate(
     queryset = Cluster.objects.annotate(
@@ -213,6 +224,7 @@ class ClusterDevicesView(generic.ObjectChildrenView):
         return Device.objects.restrict(request.user, 'view').filter(cluster=parent)
         return Device.objects.restrict(request.user, 'view').filter(cluster=parent)
 
 
 
 
+@register_model_view(Cluster, 'add', detail=False)
 @register_model_view(Cluster, 'edit')
 @register_model_view(Cluster, 'edit')
 class ClusterEditView(generic.ObjectEditView):
 class ClusterEditView(generic.ObjectEditView):
     queryset = Cluster.objects.all()
     queryset = Cluster.objects.all()
@@ -224,11 +236,13 @@ class ClusterDeleteView(generic.ObjectDeleteView):
     queryset = Cluster.objects.all()
     queryset = Cluster.objects.all()
 
 
 
 
+@register_model_view(Cluster, 'import', detail=False)
 class ClusterBulkImportView(generic.BulkImportView):
 class ClusterBulkImportView(generic.BulkImportView):
     queryset = Cluster.objects.all()
     queryset = Cluster.objects.all()
     model_form = forms.ClusterImportForm
     model_form = forms.ClusterImportForm
 
 
 
 
+@register_model_view(Cluster, 'bulk_edit', path='edit', detail=False)
 class ClusterBulkEditView(generic.BulkEditView):
 class ClusterBulkEditView(generic.BulkEditView):
     queryset = Cluster.objects.all()
     queryset = Cluster.objects.all()
     filterset = filtersets.ClusterFilterSet
     filterset = filtersets.ClusterFilterSet
@@ -236,6 +250,7 @@ class ClusterBulkEditView(generic.BulkEditView):
     form = forms.ClusterBulkEditForm
     form = forms.ClusterBulkEditForm
 
 
 
 
+@register_model_view(Cluster, 'bulk_delete', path='delete', detail=False)
 class ClusterBulkDeleteView(generic.BulkDeleteView):
 class ClusterBulkDeleteView(generic.BulkDeleteView):
     queryset = Cluster.objects.all()
     queryset = Cluster.objects.all()
     filterset = filtersets.ClusterFilterSet
     filterset = filtersets.ClusterFilterSet
@@ -337,6 +352,7 @@ class ClusterContactsView(ObjectContactsView):
 # Virtual machines
 # Virtual machines
 #
 #
 
 
+@register_model_view(VirtualMachine, 'list', path='', detail=False)
 class VirtualMachineListView(generic.ObjectListView):
 class VirtualMachineListView(generic.ObjectListView):
     queryset = VirtualMachine.objects.prefetch_related('primary_ip4', 'primary_ip6')
     queryset = VirtualMachine.objects.prefetch_related('primary_ip4', 'primary_ip6')
     filterset = filtersets.VirtualMachineFilterSet
     filterset = filtersets.VirtualMachineFilterSet
@@ -457,6 +473,7 @@ class VirtualMachineRenderConfigView(generic.ObjectView):
         }
         }
 
 
 
 
+@register_model_view(VirtualMachine, 'add', detail=False)
 @register_model_view(VirtualMachine, 'edit')
 @register_model_view(VirtualMachine, 'edit')
 class VirtualMachineEditView(generic.ObjectEditView):
 class VirtualMachineEditView(generic.ObjectEditView):
     queryset = VirtualMachine.objects.all()
     queryset = VirtualMachine.objects.all()
@@ -468,11 +485,13 @@ class VirtualMachineDeleteView(generic.ObjectDeleteView):
     queryset = VirtualMachine.objects.all()
     queryset = VirtualMachine.objects.all()
 
 
 
 
+@register_model_view(VirtualMachine, 'import', detail=False)
 class VirtualMachineBulkImportView(generic.BulkImportView):
 class VirtualMachineBulkImportView(generic.BulkImportView):
     queryset = VirtualMachine.objects.all()
     queryset = VirtualMachine.objects.all()
     model_form = forms.VirtualMachineImportForm
     model_form = forms.VirtualMachineImportForm
 
 
 
 
+@register_model_view(VirtualMachine, 'bulk_edit', path='edit', detail=False)
 class VirtualMachineBulkEditView(generic.BulkEditView):
 class VirtualMachineBulkEditView(generic.BulkEditView):
     queryset = VirtualMachine.objects.prefetch_related('primary_ip4', 'primary_ip6')
     queryset = VirtualMachine.objects.prefetch_related('primary_ip4', 'primary_ip6')
     filterset = filtersets.VirtualMachineFilterSet
     filterset = filtersets.VirtualMachineFilterSet
@@ -480,6 +499,7 @@ class VirtualMachineBulkEditView(generic.BulkEditView):
     form = forms.VirtualMachineBulkEditForm
     form = forms.VirtualMachineBulkEditForm
 
 
 
 
+@register_model_view(VirtualMachine, 'bulk_delete', path='delete', detail=False)
 class VirtualMachineBulkDeleteView(generic.BulkDeleteView):
 class VirtualMachineBulkDeleteView(generic.BulkDeleteView):
     queryset = VirtualMachine.objects.prefetch_related('primary_ip4', 'primary_ip6')
     queryset = VirtualMachine.objects.prefetch_related('primary_ip4', 'primary_ip6')
     filterset = filtersets.VirtualMachineFilterSet
     filterset = filtersets.VirtualMachineFilterSet
@@ -495,6 +515,7 @@ class VirtualMachineContactsView(ObjectContactsView):
 # VM interfaces
 # VM interfaces
 #
 #
 
 
+@register_model_view(VMInterface, 'list', path='', detail=False)
 class VMInterfaceListView(generic.ObjectListView):
 class VMInterfaceListView(generic.ObjectListView):
     queryset = VMInterface.objects.all()
     queryset = VMInterface.objects.all()
     filterset = filtersets.VMInterfaceFilterSet
     filterset = filtersets.VMInterfaceFilterSet
@@ -545,6 +566,7 @@ class VMInterfaceView(generic.ObjectView):
         }
         }
 
 
 
 
+@register_model_view(VMInterface, 'add', detail=False)
 class VMInterfaceCreateView(generic.ComponentCreateView):
 class VMInterfaceCreateView(generic.ComponentCreateView):
     queryset = VMInterface.objects.all()
     queryset = VMInterface.objects.all()
     form = forms.VMInterfaceCreateForm
     form = forms.VMInterfaceCreateForm
@@ -562,11 +584,13 @@ class VMInterfaceDeleteView(generic.ObjectDeleteView):
     queryset = VMInterface.objects.all()
     queryset = VMInterface.objects.all()
 
 
 
 
+@register_model_view(VMInterface, 'import', detail=False)
 class VMInterfaceBulkImportView(generic.BulkImportView):
 class VMInterfaceBulkImportView(generic.BulkImportView):
     queryset = VMInterface.objects.all()
     queryset = VMInterface.objects.all()
     model_form = forms.VMInterfaceImportForm
     model_form = forms.VMInterfaceImportForm
 
 
 
 
+@register_model_view(VMInterface, 'bulk_edit', path='edit', detail=False)
 class VMInterfaceBulkEditView(generic.BulkEditView):
 class VMInterfaceBulkEditView(generic.BulkEditView):
     queryset = VMInterface.objects.all()
     queryset = VMInterface.objects.all()
     filterset = filtersets.VMInterfaceFilterSet
     filterset = filtersets.VMInterfaceFilterSet
@@ -574,11 +598,13 @@ class VMInterfaceBulkEditView(generic.BulkEditView):
     form = forms.VMInterfaceBulkEditForm
     form = forms.VMInterfaceBulkEditForm
 
 
 
 
+@register_model_view(VMInterface, 'bulk_rename', path='rename', detail=False)
 class VMInterfaceBulkRenameView(generic.BulkRenameView):
 class VMInterfaceBulkRenameView(generic.BulkRenameView):
     queryset = VMInterface.objects.all()
     queryset = VMInterface.objects.all()
     form = forms.VMInterfaceBulkRenameForm
     form = forms.VMInterfaceBulkRenameForm
 
 
 
 
+@register_model_view(VMInterface, 'bulk_delete', path='delete', detail=False)
 class VMInterfaceBulkDeleteView(generic.BulkDeleteView):
 class VMInterfaceBulkDeleteView(generic.BulkDeleteView):
     # Ensure child interfaces are deleted prior to their parents
     # Ensure child interfaces are deleted prior to their parents
     queryset = VMInterface.objects.order_by('virtual_machine', 'parent', CollateAsChar('_name'))
     queryset = VMInterface.objects.order_by('virtual_machine', 'parent', CollateAsChar('_name'))
@@ -590,6 +616,7 @@ class VMInterfaceBulkDeleteView(generic.BulkDeleteView):
 # Virtual disks
 # Virtual disks
 #
 #
 
 
+@register_model_view(VirtualDisk, 'list', path='', detail=False)
 class VirtualDiskListView(generic.ObjectListView):
 class VirtualDiskListView(generic.ObjectListView):
     queryset = VirtualDisk.objects.all()
     queryset = VirtualDisk.objects.all()
     filterset = filtersets.VirtualDiskFilterSet
     filterset = filtersets.VirtualDiskFilterSet
@@ -602,6 +629,7 @@ class VirtualDiskView(generic.ObjectView):
     queryset = VirtualDisk.objects.all()
     queryset = VirtualDisk.objects.all()
 
 
 
 
+@register_model_view(VirtualDisk, 'add', detail=False)
 class VirtualDiskCreateView(generic.ComponentCreateView):
 class VirtualDiskCreateView(generic.ComponentCreateView):
     queryset = VirtualDisk.objects.all()
     queryset = VirtualDisk.objects.all()
     form = forms.VirtualDiskCreateForm
     form = forms.VirtualDiskCreateForm
@@ -619,11 +647,13 @@ class VirtualDiskDeleteView(generic.ObjectDeleteView):
     queryset = VirtualDisk.objects.all()
     queryset = VirtualDisk.objects.all()
 
 
 
 
+@register_model_view(VirtualDisk, 'import', detail=False)
 class VirtualDiskBulkImportView(generic.BulkImportView):
 class VirtualDiskBulkImportView(generic.BulkImportView):
     queryset = VirtualDisk.objects.all()
     queryset = VirtualDisk.objects.all()
     model_form = forms.VirtualDiskImportForm
     model_form = forms.VirtualDiskImportForm
 
 
 
 
+@register_model_view(VirtualDisk, 'bulk_edit', path='edit', detail=False)
 class VirtualDiskBulkEditView(generic.BulkEditView):
 class VirtualDiskBulkEditView(generic.BulkEditView):
     queryset = VirtualDisk.objects.all()
     queryset = VirtualDisk.objects.all()
     filterset = filtersets.VirtualDiskFilterSet
     filterset = filtersets.VirtualDiskFilterSet
@@ -631,11 +661,13 @@ class VirtualDiskBulkEditView(generic.BulkEditView):
     form = forms.VirtualDiskBulkEditForm
     form = forms.VirtualDiskBulkEditForm
 
 
 
 
+@register_model_view(VirtualDisk, 'bulk_rename', path='rename', detail=False)
 class VirtualDiskBulkRenameView(generic.BulkRenameView):
 class VirtualDiskBulkRenameView(generic.BulkRenameView):
     queryset = VirtualDisk.objects.all()
     queryset = VirtualDisk.objects.all()
     form = forms.VirtualDiskBulkRenameForm
     form = forms.VirtualDiskBulkRenameForm
 
 
 
 
+@register_model_view(VirtualDisk, 'bulk_delete', path='delete', detail=False)
 class VirtualDiskBulkDeleteView(generic.BulkDeleteView):
 class VirtualDiskBulkDeleteView(generic.BulkDeleteView):
     queryset = VirtualDisk.objects.all()
     queryset = VirtualDisk.objects.all()
     filterset = filtersets.VirtualDiskFilterSet
     filterset = filtersets.VirtualDiskFilterSet

+ 11 - 61
netbox/vpn/urls.py

@@ -1,89 +1,39 @@
 from django.urls import include, path
 from django.urls import include, path
 
 
 from utilities.urls import get_model_urls
 from utilities.urls import get_model_urls
-from . import views
+from . import views  # noqa F401
 
 
 app_name = 'vpn'
 app_name = 'vpn'
 urlpatterns = [
 urlpatterns = [
 
 
-    # Tunnel groups
-    path('tunnel-groups/', views.TunnelGroupListView.as_view(), name='tunnelgroup_list'),
-    path('tunnel-groups/add/', views.TunnelGroupEditView.as_view(), name='tunnelgroup_add'),
-    path('tunnel-groups/import/', views.TunnelGroupBulkImportView.as_view(), name='tunnelgroup_import'),
-    path('tunnel-groups/edit/', views.TunnelGroupBulkEditView.as_view(), name='tunnelgroup_bulk_edit'),
-    path('tunnel-groups/delete/', views.TunnelGroupBulkDeleteView.as_view(), name='tunnelgroup_bulk_delete'),
+    path('tunnel-groups/', include(get_model_urls('vpn', 'tunnelgroup', detail=False))),
     path('tunnel-groups/<int:pk>/', include(get_model_urls('vpn', 'tunnelgroup'))),
     path('tunnel-groups/<int:pk>/', include(get_model_urls('vpn', 'tunnelgroup'))),
 
 
-    # Tunnels
-    path('tunnels/', views.TunnelListView.as_view(), name='tunnel_list'),
-    path('tunnels/add/', views.TunnelEditView.as_view(), name='tunnel_add'),
-    path('tunnels/import/', views.TunnelBulkImportView.as_view(), name='tunnel_import'),
-    path('tunnels/edit/', views.TunnelBulkEditView.as_view(), name='tunnel_bulk_edit'),
-    path('tunnels/delete/', views.TunnelBulkDeleteView.as_view(), name='tunnel_bulk_delete'),
+    path('tunnels/', include(get_model_urls('vpn', 'tunnel', detail=False))),
     path('tunnels/<int:pk>/', include(get_model_urls('vpn', 'tunnel'))),
     path('tunnels/<int:pk>/', include(get_model_urls('vpn', 'tunnel'))),
 
 
-    # Tunnel terminations
-    path('tunnel-terminations/', views.TunnelTerminationListView.as_view(), name='tunneltermination_list'),
-    path('tunnel-terminations/add/', views.TunnelTerminationEditView.as_view(), name='tunneltermination_add'),
-    path('tunnel-terminations/import/', views.TunnelTerminationBulkImportView.as_view(), name='tunneltermination_import'),
-    path('tunnel-terminations/edit/', views.TunnelTerminationBulkEditView.as_view(), name='tunneltermination_bulk_edit'),
-    path('tunnel-terminations/delete/', views.TunnelTerminationBulkDeleteView.as_view(), name='tunneltermination_bulk_delete'),
+    path('tunnel-terminations/', include(get_model_urls('vpn', 'tunneltermination', detail=False))),
     path('tunnel-terminations/<int:pk>/', include(get_model_urls('vpn', 'tunneltermination'))),
     path('tunnel-terminations/<int:pk>/', include(get_model_urls('vpn', 'tunneltermination'))),
 
 
-    # IKE proposals
-    path('ike-proposals/', views.IKEProposalListView.as_view(), name='ikeproposal_list'),
-    path('ike-proposals/add/', views.IKEProposalEditView.as_view(), name='ikeproposal_add'),
-    path('ike-proposals/import/', views.IKEProposalBulkImportView.as_view(), name='ikeproposal_import'),
-    path('ike-proposals/edit/', views.IKEProposalBulkEditView.as_view(), name='ikeproposal_bulk_edit'),
-    path('ike-proposals/delete/', views.IKEProposalBulkDeleteView.as_view(), name='ikeproposal_bulk_delete'),
+    path('ike-proposals/', include(get_model_urls('vpn', 'ikeproposal', detail=False))),
     path('ike-proposals/<int:pk>/', include(get_model_urls('vpn', 'ikeproposal'))),
     path('ike-proposals/<int:pk>/', include(get_model_urls('vpn', 'ikeproposal'))),
 
 
-    # IKE policies
-    path('ike-policies/', views.IKEPolicyListView.as_view(), name='ikepolicy_list'),
-    path('ike-policies/add/', views.IKEPolicyEditView.as_view(), name='ikepolicy_add'),
-    path('ike-policies/import/', views.IKEPolicyBulkImportView.as_view(), name='ikepolicy_import'),
-    path('ike-policies/edit/', views.IKEPolicyBulkEditView.as_view(), name='ikepolicy_bulk_edit'),
-    path('ike-policies/delete/', views.IKEPolicyBulkDeleteView.as_view(), name='ikepolicy_bulk_delete'),
+    path('ike-policies/', include(get_model_urls('vpn', 'ikepolicy', detail=False))),
     path('ike-policies/<int:pk>/', include(get_model_urls('vpn', 'ikepolicy'))),
     path('ike-policies/<int:pk>/', include(get_model_urls('vpn', 'ikepolicy'))),
 
 
-    # IPSec proposals
-    path('ipsec-proposals/', views.IPSecProposalListView.as_view(), name='ipsecproposal_list'),
-    path('ipsec-proposals/add/', views.IPSecProposalEditView.as_view(), name='ipsecproposal_add'),
-    path('ipsec-proposals/import/', views.IPSecProposalBulkImportView.as_view(), name='ipsecproposal_import'),
-    path('ipsec-proposals/edit/', views.IPSecProposalBulkEditView.as_view(), name='ipsecproposal_bulk_edit'),
-    path('ipsec-proposals/delete/', views.IPSecProposalBulkDeleteView.as_view(), name='ipsecproposal_bulk_delete'),
+    path('ipsec-proposals/', include(get_model_urls('vpn', 'ipsecproposal', detail=False))),
     path('ipsec-proposals/<int:pk>/', include(get_model_urls('vpn', 'ipsecproposal'))),
     path('ipsec-proposals/<int:pk>/', include(get_model_urls('vpn', 'ipsecproposal'))),
 
 
-    # IPSec policies
-    path('ipsec-policies/', views.IPSecPolicyListView.as_view(), name='ipsecpolicy_list'),
-    path('ipsec-policies/add/', views.IPSecPolicyEditView.as_view(), name='ipsecpolicy_add'),
-    path('ipsec-policies/import/', views.IPSecPolicyBulkImportView.as_view(), name='ipsecpolicy_import'),
-    path('ipsec-policies/edit/', views.IPSecPolicyBulkEditView.as_view(), name='ipsecpolicy_bulk_edit'),
-    path('ipsec-policies/delete/', views.IPSecPolicyBulkDeleteView.as_view(), name='ipsecpolicy_bulk_delete'),
+    path('ipsec-policies/', include(get_model_urls('vpn', 'ipsecpolicy', detail=False))),
     path('ipsec-policies/<int:pk>/', include(get_model_urls('vpn', 'ipsecpolicy'))),
     path('ipsec-policies/<int:pk>/', include(get_model_urls('vpn', 'ipsecpolicy'))),
 
 
-    # IPSec profiles
-    path('ipsec-profiles/', views.IPSecProfileListView.as_view(), name='ipsecprofile_list'),
-    path('ipsec-profiles/add/', views.IPSecProfileEditView.as_view(), name='ipsecprofile_add'),
-    path('ipsec-profiles/import/', views.IPSecProfileBulkImportView.as_view(), name='ipsecprofile_import'),
-    path('ipsec-profiles/edit/', views.IPSecProfileBulkEditView.as_view(), name='ipsecprofile_bulk_edit'),
-    path('ipsec-profiles/delete/', views.IPSecProfileBulkDeleteView.as_view(), name='ipsecprofile_bulk_delete'),
+    path('ipsec-profiles/', include(get_model_urls('vpn', 'ipsecprofile', detail=False))),
     path('ipsec-profiles/<int:pk>/', include(get_model_urls('vpn', 'ipsecprofile'))),
     path('ipsec-profiles/<int:pk>/', include(get_model_urls('vpn', 'ipsecprofile'))),
 
 
-    # L2VPN
-    path('l2vpns/', views.L2VPNListView.as_view(), name='l2vpn_list'),
-    path('l2vpns/add/', views.L2VPNEditView.as_view(), name='l2vpn_add'),
-    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/', include(get_model_urls('vpn', 'l2vpn', detail=False))),
     path('l2vpns/<int:pk>/', include(get_model_urls('vpn', 'l2vpn'))),
     path('l2vpns/<int:pk>/', include(get_model_urls('vpn', 'l2vpn'))),
 
 
-    # L2VPN terminations
-    path('l2vpn-terminations/', views.L2VPNTerminationListView.as_view(), name='l2vpntermination_list'),
-    path('l2vpn-terminations/add/', views.L2VPNTerminationEditView.as_view(), name='l2vpntermination_add'),
-    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/', include(get_model_urls('vpn', 'l2vpntermination', detail=False))),
     path('l2vpn-terminations/<int:pk>/', include(get_model_urls('vpn', 'l2vpntermination'))),
     path('l2vpn-terminations/<int:pk>/', include(get_model_urls('vpn', 'l2vpntermination'))),
 
 
 ]
 ]

+ 52 - 0
netbox/vpn/views.py

@@ -11,6 +11,7 @@ from .models import *
 # Tunnel groups
 # Tunnel groups
 #
 #
 
 
+@register_model_view(TunnelGroup, 'list', path='', detail=False)
 class TunnelGroupListView(generic.ObjectListView):
 class TunnelGroupListView(generic.ObjectListView):
     queryset = TunnelGroup.objects.annotate(
     queryset = TunnelGroup.objects.annotate(
         tunnel_count=count_related(Tunnel, 'group')
         tunnel_count=count_related(Tunnel, 'group')
@@ -30,6 +31,7 @@ class TunnelGroupView(GetRelatedModelsMixin, generic.ObjectView):
         }
         }
 
 
 
 
+@register_model_view(TunnelGroup, 'add', detail=False)
 @register_model_view(TunnelGroup, 'edit')
 @register_model_view(TunnelGroup, 'edit')
 class TunnelGroupEditView(generic.ObjectEditView):
 class TunnelGroupEditView(generic.ObjectEditView):
     queryset = TunnelGroup.objects.all()
     queryset = TunnelGroup.objects.all()
@@ -41,11 +43,13 @@ class TunnelGroupDeleteView(generic.ObjectDeleteView):
     queryset = TunnelGroup.objects.all()
     queryset = TunnelGroup.objects.all()
 
 
 
 
+@register_model_view(TunnelGroup, 'import', detail=False)
 class TunnelGroupBulkImportView(generic.BulkImportView):
 class TunnelGroupBulkImportView(generic.BulkImportView):
     queryset = TunnelGroup.objects.all()
     queryset = TunnelGroup.objects.all()
     model_form = forms.TunnelGroupImportForm
     model_form = forms.TunnelGroupImportForm
 
 
 
 
+@register_model_view(TunnelGroup, 'bulk_edit', path='edit', detail=False)
 class TunnelGroupBulkEditView(generic.BulkEditView):
 class TunnelGroupBulkEditView(generic.BulkEditView):
     queryset = TunnelGroup.objects.annotate(
     queryset = TunnelGroup.objects.annotate(
         tunnel_count=count_related(Tunnel, 'group')
         tunnel_count=count_related(Tunnel, 'group')
@@ -55,6 +59,7 @@ class TunnelGroupBulkEditView(generic.BulkEditView):
     form = forms.TunnelGroupBulkEditForm
     form = forms.TunnelGroupBulkEditForm
 
 
 
 
+@register_model_view(TunnelGroup, 'bulk_delete', path='delete', detail=False)
 class TunnelGroupBulkDeleteView(generic.BulkDeleteView):
 class TunnelGroupBulkDeleteView(generic.BulkDeleteView):
     queryset = TunnelGroup.objects.annotate(
     queryset = TunnelGroup.objects.annotate(
         tunnel_count=count_related(Tunnel, 'group')
         tunnel_count=count_related(Tunnel, 'group')
@@ -67,6 +72,7 @@ class TunnelGroupBulkDeleteView(generic.BulkDeleteView):
 # Tunnels
 # Tunnels
 #
 #
 
 
+@register_model_view(Tunnel, 'list', path='', detail=False)
 class TunnelListView(generic.ObjectListView):
 class TunnelListView(generic.ObjectListView):
     queryset = Tunnel.objects.annotate(
     queryset = Tunnel.objects.annotate(
         count_terminations=count_related(TunnelTermination, 'tunnel')
         count_terminations=count_related(TunnelTermination, 'tunnel')
@@ -81,6 +87,7 @@ class TunnelView(generic.ObjectView):
     queryset = Tunnel.objects.all()
     queryset = Tunnel.objects.all()
 
 
 
 
+@register_model_view(Tunnel, 'add', detail=False)
 @register_model_view(Tunnel, 'edit')
 @register_model_view(Tunnel, 'edit')
 class TunnelEditView(generic.ObjectEditView):
 class TunnelEditView(generic.ObjectEditView):
     queryset = Tunnel.objects.all()
     queryset = Tunnel.objects.all()
@@ -100,11 +107,13 @@ class TunnelDeleteView(generic.ObjectDeleteView):
     queryset = Tunnel.objects.all()
     queryset = Tunnel.objects.all()
 
 
 
 
+@register_model_view(Tunnel, 'import', detail=False)
 class TunnelBulkImportView(generic.BulkImportView):
 class TunnelBulkImportView(generic.BulkImportView):
     queryset = Tunnel.objects.all()
     queryset = Tunnel.objects.all()
     model_form = forms.TunnelImportForm
     model_form = forms.TunnelImportForm
 
 
 
 
+@register_model_view(Tunnel, 'bulk_edit', path='edit', detail=False)
 class TunnelBulkEditView(generic.BulkEditView):
 class TunnelBulkEditView(generic.BulkEditView):
     queryset = Tunnel.objects.annotate(
     queryset = Tunnel.objects.annotate(
         count_terminations=count_related(TunnelTermination, 'tunnel')
         count_terminations=count_related(TunnelTermination, 'tunnel')
@@ -114,6 +123,7 @@ class TunnelBulkEditView(generic.BulkEditView):
     form = forms.TunnelBulkEditForm
     form = forms.TunnelBulkEditForm
 
 
 
 
+@register_model_view(Tunnel, 'bulk_delete', path='delete', detail=False)
 class TunnelBulkDeleteView(generic.BulkDeleteView):
 class TunnelBulkDeleteView(generic.BulkDeleteView):
     queryset = Tunnel.objects.annotate(
     queryset = Tunnel.objects.annotate(
         count_terminations=count_related(TunnelTermination, 'tunnel')
         count_terminations=count_related(TunnelTermination, 'tunnel')
@@ -126,6 +136,7 @@ class TunnelBulkDeleteView(generic.BulkDeleteView):
 # Tunnel terminations
 # Tunnel terminations
 #
 #
 
 
+@register_model_view(TunnelTermination, 'list', path='', detail=False)
 class TunnelTerminationListView(generic.ObjectListView):
 class TunnelTerminationListView(generic.ObjectListView):
     queryset = TunnelTermination.objects.all()
     queryset = TunnelTermination.objects.all()
     filterset = filtersets.TunnelTerminationFilterSet
     filterset = filtersets.TunnelTerminationFilterSet
@@ -138,6 +149,7 @@ class TunnelTerminationView(generic.ObjectView):
     queryset = TunnelTermination.objects.all()
     queryset = TunnelTermination.objects.all()
 
 
 
 
+@register_model_view(TunnelTermination, 'add', detail=False)
 @register_model_view(TunnelTermination, 'edit')
 @register_model_view(TunnelTermination, 'edit')
 class TunnelTerminationEditView(generic.ObjectEditView):
 class TunnelTerminationEditView(generic.ObjectEditView):
     queryset = TunnelTermination.objects.all()
     queryset = TunnelTermination.objects.all()
@@ -149,11 +161,13 @@ class TunnelTerminationDeleteView(generic.ObjectDeleteView):
     queryset = TunnelTermination.objects.all()
     queryset = TunnelTermination.objects.all()
 
 
 
 
+@register_model_view(TunnelTermination, 'import', detail=False)
 class TunnelTerminationBulkImportView(generic.BulkImportView):
 class TunnelTerminationBulkImportView(generic.BulkImportView):
     queryset = TunnelTermination.objects.all()
     queryset = TunnelTermination.objects.all()
     model_form = forms.TunnelTerminationImportForm
     model_form = forms.TunnelTerminationImportForm
 
 
 
 
+@register_model_view(TunnelTermination, 'bulk_edit', path='edit', detail=False)
 class TunnelTerminationBulkEditView(generic.BulkEditView):
 class TunnelTerminationBulkEditView(generic.BulkEditView):
     queryset = TunnelTermination.objects.all()
     queryset = TunnelTermination.objects.all()
     filterset = filtersets.TunnelTerminationFilterSet
     filterset = filtersets.TunnelTerminationFilterSet
@@ -161,6 +175,7 @@ class TunnelTerminationBulkEditView(generic.BulkEditView):
     form = forms.TunnelTerminationBulkEditForm
     form = forms.TunnelTerminationBulkEditForm
 
 
 
 
+@register_model_view(TunnelTermination, 'bulk_delete', path='delete', detail=False)
 class TunnelTerminationBulkDeleteView(generic.BulkDeleteView):
 class TunnelTerminationBulkDeleteView(generic.BulkDeleteView):
     queryset = TunnelTermination.objects.all()
     queryset = TunnelTermination.objects.all()
     filterset = filtersets.TunnelTerminationFilterSet
     filterset = filtersets.TunnelTerminationFilterSet
@@ -171,6 +186,7 @@ class TunnelTerminationBulkDeleteView(generic.BulkDeleteView):
 # IKE proposals
 # IKE proposals
 #
 #
 
 
+@register_model_view(IKEProposal, 'list', path='', detail=False)
 class IKEProposalListView(generic.ObjectListView):
 class IKEProposalListView(generic.ObjectListView):
     queryset = IKEProposal.objects.all()
     queryset = IKEProposal.objects.all()
     filterset = filtersets.IKEProposalFilterSet
     filterset = filtersets.IKEProposalFilterSet
@@ -183,6 +199,7 @@ class IKEProposalView(generic.ObjectView):
     queryset = IKEProposal.objects.all()
     queryset = IKEProposal.objects.all()
 
 
 
 
+@register_model_view(IKEProposal, 'add', detail=False)
 @register_model_view(IKEProposal, 'edit')
 @register_model_view(IKEProposal, 'edit')
 class IKEProposalEditView(generic.ObjectEditView):
 class IKEProposalEditView(generic.ObjectEditView):
     queryset = IKEProposal.objects.all()
     queryset = IKEProposal.objects.all()
@@ -194,11 +211,13 @@ class IKEProposalDeleteView(generic.ObjectDeleteView):
     queryset = IKEProposal.objects.all()
     queryset = IKEProposal.objects.all()
 
 
 
 
+@register_model_view(IKEProposal, 'import', detail=False)
 class IKEProposalBulkImportView(generic.BulkImportView):
 class IKEProposalBulkImportView(generic.BulkImportView):
     queryset = IKEProposal.objects.all()
     queryset = IKEProposal.objects.all()
     model_form = forms.IKEProposalImportForm
     model_form = forms.IKEProposalImportForm
 
 
 
 
+@register_model_view(IKEProposal, 'bulk_edit', path='edit', detail=False)
 class IKEProposalBulkEditView(generic.BulkEditView):
 class IKEProposalBulkEditView(generic.BulkEditView):
     queryset = IKEProposal.objects.all()
     queryset = IKEProposal.objects.all()
     filterset = filtersets.IKEProposalFilterSet
     filterset = filtersets.IKEProposalFilterSet
@@ -206,6 +225,7 @@ class IKEProposalBulkEditView(generic.BulkEditView):
     form = forms.IKEProposalBulkEditForm
     form = forms.IKEProposalBulkEditForm
 
 
 
 
+@register_model_view(IKEProposal, 'bulk_delete', path='delete', detail=False)
 class IKEProposalBulkDeleteView(generic.BulkDeleteView):
 class IKEProposalBulkDeleteView(generic.BulkDeleteView):
     queryset = IKEProposal.objects.all()
     queryset = IKEProposal.objects.all()
     filterset = filtersets.IKEProposalFilterSet
     filterset = filtersets.IKEProposalFilterSet
@@ -216,6 +236,7 @@ class IKEProposalBulkDeleteView(generic.BulkDeleteView):
 # IKE policies
 # IKE policies
 #
 #
 
 
+@register_model_view(IKEPolicy, 'list', path='', detail=False)
 class IKEPolicyListView(generic.ObjectListView):
 class IKEPolicyListView(generic.ObjectListView):
     queryset = IKEPolicy.objects.all()
     queryset = IKEPolicy.objects.all()
     filterset = filtersets.IKEPolicyFilterSet
     filterset = filtersets.IKEPolicyFilterSet
@@ -228,6 +249,7 @@ class IKEPolicyView(generic.ObjectView):
     queryset = IKEPolicy.objects.all()
     queryset = IKEPolicy.objects.all()
 
 
 
 
+@register_model_view(IKEPolicy, 'add', detail=False)
 @register_model_view(IKEPolicy, 'edit')
 @register_model_view(IKEPolicy, 'edit')
 class IKEPolicyEditView(generic.ObjectEditView):
 class IKEPolicyEditView(generic.ObjectEditView):
     queryset = IKEPolicy.objects.all()
     queryset = IKEPolicy.objects.all()
@@ -239,11 +261,13 @@ class IKEPolicyDeleteView(generic.ObjectDeleteView):
     queryset = IKEPolicy.objects.all()
     queryset = IKEPolicy.objects.all()
 
 
 
 
+@register_model_view(IKEPolicy, 'import', detail=False)
 class IKEPolicyBulkImportView(generic.BulkImportView):
 class IKEPolicyBulkImportView(generic.BulkImportView):
     queryset = IKEPolicy.objects.all()
     queryset = IKEPolicy.objects.all()
     model_form = forms.IKEPolicyImportForm
     model_form = forms.IKEPolicyImportForm
 
 
 
 
+@register_model_view(IKEPolicy, 'bulk_edit', path='edit', detail=False)
 class IKEPolicyBulkEditView(generic.BulkEditView):
 class IKEPolicyBulkEditView(generic.BulkEditView):
     queryset = IKEPolicy.objects.all()
     queryset = IKEPolicy.objects.all()
     filterset = filtersets.IKEPolicyFilterSet
     filterset = filtersets.IKEPolicyFilterSet
@@ -251,6 +275,7 @@ class IKEPolicyBulkEditView(generic.BulkEditView):
     form = forms.IKEPolicyBulkEditForm
     form = forms.IKEPolicyBulkEditForm
 
 
 
 
+@register_model_view(IKEPolicy, 'bulk_delete', path='delete', detail=False)
 class IKEPolicyBulkDeleteView(generic.BulkDeleteView):
 class IKEPolicyBulkDeleteView(generic.BulkDeleteView):
     queryset = IKEPolicy.objects.all()
     queryset = IKEPolicy.objects.all()
     filterset = filtersets.IKEPolicyFilterSet
     filterset = filtersets.IKEPolicyFilterSet
@@ -261,6 +286,7 @@ class IKEPolicyBulkDeleteView(generic.BulkDeleteView):
 # IPSec proposals
 # IPSec proposals
 #
 #
 
 
+@register_model_view(IPSecProposal, 'list', path='', detail=False)
 class IPSecProposalListView(generic.ObjectListView):
 class IPSecProposalListView(generic.ObjectListView):
     queryset = IPSecProposal.objects.all()
     queryset = IPSecProposal.objects.all()
     filterset = filtersets.IPSecProposalFilterSet
     filterset = filtersets.IPSecProposalFilterSet
@@ -273,6 +299,7 @@ class IPSecProposalView(generic.ObjectView):
     queryset = IPSecProposal.objects.all()
     queryset = IPSecProposal.objects.all()
 
 
 
 
+@register_model_view(IPSecProposal, 'add', detail=False)
 @register_model_view(IPSecProposal, 'edit')
 @register_model_view(IPSecProposal, 'edit')
 class IPSecProposalEditView(generic.ObjectEditView):
 class IPSecProposalEditView(generic.ObjectEditView):
     queryset = IPSecProposal.objects.all()
     queryset = IPSecProposal.objects.all()
@@ -284,11 +311,13 @@ class IPSecProposalDeleteView(generic.ObjectDeleteView):
     queryset = IPSecProposal.objects.all()
     queryset = IPSecProposal.objects.all()
 
 
 
 
+@register_model_view(IPSecProposal, 'import', detail=False)
 class IPSecProposalBulkImportView(generic.BulkImportView):
 class IPSecProposalBulkImportView(generic.BulkImportView):
     queryset = IPSecProposal.objects.all()
     queryset = IPSecProposal.objects.all()
     model_form = forms.IPSecProposalImportForm
     model_form = forms.IPSecProposalImportForm
 
 
 
 
+@register_model_view(IPSecProposal, 'bulk_edit', path='edit', detail=False)
 class IPSecProposalBulkEditView(generic.BulkEditView):
 class IPSecProposalBulkEditView(generic.BulkEditView):
     queryset = IPSecProposal.objects.all()
     queryset = IPSecProposal.objects.all()
     filterset = filtersets.IPSecProposalFilterSet
     filterset = filtersets.IPSecProposalFilterSet
@@ -296,6 +325,7 @@ class IPSecProposalBulkEditView(generic.BulkEditView):
     form = forms.IPSecProposalBulkEditForm
     form = forms.IPSecProposalBulkEditForm
 
 
 
 
+@register_model_view(IPSecProposal, 'bulk_delete', path='delete', detail=False)
 class IPSecProposalBulkDeleteView(generic.BulkDeleteView):
 class IPSecProposalBulkDeleteView(generic.BulkDeleteView):
     queryset = IPSecProposal.objects.all()
     queryset = IPSecProposal.objects.all()
     filterset = filtersets.IPSecProposalFilterSet
     filterset = filtersets.IPSecProposalFilterSet
@@ -306,6 +336,7 @@ class IPSecProposalBulkDeleteView(generic.BulkDeleteView):
 # IPSec policies
 # IPSec policies
 #
 #
 
 
+@register_model_view(IPSecPolicy, 'list', path='', detail=False)
 class IPSecPolicyListView(generic.ObjectListView):
 class IPSecPolicyListView(generic.ObjectListView):
     queryset = IPSecPolicy.objects.all()
     queryset = IPSecPolicy.objects.all()
     filterset = filtersets.IPSecPolicyFilterSet
     filterset = filtersets.IPSecPolicyFilterSet
@@ -318,6 +349,7 @@ class IPSecPolicyView(generic.ObjectView):
     queryset = IPSecPolicy.objects.all()
     queryset = IPSecPolicy.objects.all()
 
 
 
 
+@register_model_view(IPSecPolicy, 'add', detail=False)
 @register_model_view(IPSecPolicy, 'edit')
 @register_model_view(IPSecPolicy, 'edit')
 class IPSecPolicyEditView(generic.ObjectEditView):
 class IPSecPolicyEditView(generic.ObjectEditView):
     queryset = IPSecPolicy.objects.all()
     queryset = IPSecPolicy.objects.all()
@@ -329,11 +361,13 @@ class IPSecPolicyDeleteView(generic.ObjectDeleteView):
     queryset = IPSecPolicy.objects.all()
     queryset = IPSecPolicy.objects.all()
 
 
 
 
+@register_model_view(IPSecPolicy, 'import', detail=False)
 class IPSecPolicyBulkImportView(generic.BulkImportView):
 class IPSecPolicyBulkImportView(generic.BulkImportView):
     queryset = IPSecPolicy.objects.all()
     queryset = IPSecPolicy.objects.all()
     model_form = forms.IPSecPolicyImportForm
     model_form = forms.IPSecPolicyImportForm
 
 
 
 
+@register_model_view(IPSecPolicy, 'bulk_edit', path='edit', detail=False)
 class IPSecPolicyBulkEditView(generic.BulkEditView):
 class IPSecPolicyBulkEditView(generic.BulkEditView):
     queryset = IPSecPolicy.objects.all()
     queryset = IPSecPolicy.objects.all()
     filterset = filtersets.IPSecPolicyFilterSet
     filterset = filtersets.IPSecPolicyFilterSet
@@ -341,6 +375,7 @@ class IPSecPolicyBulkEditView(generic.BulkEditView):
     form = forms.IPSecPolicyBulkEditForm
     form = forms.IPSecPolicyBulkEditForm
 
 
 
 
+@register_model_view(IPSecPolicy, 'bulk_delete', path='delete', detail=False)
 class IPSecPolicyBulkDeleteView(generic.BulkDeleteView):
 class IPSecPolicyBulkDeleteView(generic.BulkDeleteView):
     queryset = IPSecPolicy.objects.all()
     queryset = IPSecPolicy.objects.all()
     filterset = filtersets.IPSecPolicyFilterSet
     filterset = filtersets.IPSecPolicyFilterSet
@@ -351,6 +386,7 @@ class IPSecPolicyBulkDeleteView(generic.BulkDeleteView):
 # IPSec profiles
 # IPSec profiles
 #
 #
 
 
+@register_model_view(IPSecProfile, 'list', path='', detail=False)
 class IPSecProfileListView(generic.ObjectListView):
 class IPSecProfileListView(generic.ObjectListView):
     queryset = IPSecProfile.objects.all()
     queryset = IPSecProfile.objects.all()
     filterset = filtersets.IPSecProfileFilterSet
     filterset = filtersets.IPSecProfileFilterSet
@@ -363,6 +399,7 @@ class IPSecProfileView(generic.ObjectView):
     queryset = IPSecProfile.objects.all()
     queryset = IPSecProfile.objects.all()
 
 
 
 
+@register_model_view(IPSecProfile, 'add', detail=False)
 @register_model_view(IPSecProfile, 'edit')
 @register_model_view(IPSecProfile, 'edit')
 class IPSecProfileEditView(generic.ObjectEditView):
 class IPSecProfileEditView(generic.ObjectEditView):
     queryset = IPSecProfile.objects.all()
     queryset = IPSecProfile.objects.all()
@@ -374,11 +411,13 @@ class IPSecProfileDeleteView(generic.ObjectDeleteView):
     queryset = IPSecProfile.objects.all()
     queryset = IPSecProfile.objects.all()
 
 
 
 
+@register_model_view(IPSecProfile, 'import', detail=False)
 class IPSecProfileBulkImportView(generic.BulkImportView):
 class IPSecProfileBulkImportView(generic.BulkImportView):
     queryset = IPSecProfile.objects.all()
     queryset = IPSecProfile.objects.all()
     model_form = forms.IPSecProfileImportForm
     model_form = forms.IPSecProfileImportForm
 
 
 
 
+@register_model_view(IPSecProfile, 'bulk_edit', path='edit', detail=False)
 class IPSecProfileBulkEditView(generic.BulkEditView):
 class IPSecProfileBulkEditView(generic.BulkEditView):
     queryset = IPSecProfile.objects.all()
     queryset = IPSecProfile.objects.all()
     filterset = filtersets.IPSecProfileFilterSet
     filterset = filtersets.IPSecProfileFilterSet
@@ -386,14 +425,18 @@ class IPSecProfileBulkEditView(generic.BulkEditView):
     form = forms.IPSecProfileBulkEditForm
     form = forms.IPSecProfileBulkEditForm
 
 
 
 
+@register_model_view(IPSecProfile, 'bulk_delete', path='delete', detail=False)
 class IPSecProfileBulkDeleteView(generic.BulkDeleteView):
 class IPSecProfileBulkDeleteView(generic.BulkDeleteView):
     queryset = IPSecProfile.objects.all()
     queryset = IPSecProfile.objects.all()
     filterset = filtersets.IPSecProfileFilterSet
     filterset = filtersets.IPSecProfileFilterSet
     table = tables.IPSecProfileTable
     table = tables.IPSecProfileTable
 
 
 
 
+#
 # L2VPN
 # L2VPN
+#
 
 
+@register_model_view(L2VPN, 'list', path='', detail=False)
 class L2VPNListView(generic.ObjectListView):
 class L2VPNListView(generic.ObjectListView):
     queryset = L2VPN.objects.all()
     queryset = L2VPN.objects.all()
     table = tables.L2VPNTable
     table = tables.L2VPNTable
@@ -421,6 +464,7 @@ class L2VPNView(generic.ObjectView):
         }
         }
 
 
 
 
+@register_model_view(L2VPN, 'add', detail=False)
 @register_model_view(L2VPN, 'edit')
 @register_model_view(L2VPN, 'edit')
 class L2VPNEditView(generic.ObjectEditView):
 class L2VPNEditView(generic.ObjectEditView):
     queryset = L2VPN.objects.all()
     queryset = L2VPN.objects.all()
@@ -432,11 +476,13 @@ class L2VPNDeleteView(generic.ObjectDeleteView):
     queryset = L2VPN.objects.all()
     queryset = L2VPN.objects.all()
 
 
 
 
+@register_model_view(L2VPN, 'import', detail=False)
 class L2VPNBulkImportView(generic.BulkImportView):
 class L2VPNBulkImportView(generic.BulkImportView):
     queryset = L2VPN.objects.all()
     queryset = L2VPN.objects.all()
     model_form = forms.L2VPNImportForm
     model_form = forms.L2VPNImportForm
 
 
 
 
+@register_model_view(L2VPN, 'bulk_edit', path='edit', detail=False)
 class L2VPNBulkEditView(generic.BulkEditView):
 class L2VPNBulkEditView(generic.BulkEditView):
     queryset = L2VPN.objects.all()
     queryset = L2VPN.objects.all()
     filterset = filtersets.L2VPNFilterSet
     filterset = filtersets.L2VPNFilterSet
@@ -444,6 +490,7 @@ class L2VPNBulkEditView(generic.BulkEditView):
     form = forms.L2VPNBulkEditForm
     form = forms.L2VPNBulkEditForm
 
 
 
 
+@register_model_view(L2VPN, 'bulk_delete', path='delete', detail=False)
 class L2VPNBulkDeleteView(generic.BulkDeleteView):
 class L2VPNBulkDeleteView(generic.BulkDeleteView):
     queryset = L2VPN.objects.all()
     queryset = L2VPN.objects.all()
     filterset = filtersets.L2VPNFilterSet
     filterset = filtersets.L2VPNFilterSet
@@ -459,6 +506,7 @@ class L2VPNContactsView(ObjectContactsView):
 # L2VPN terminations
 # L2VPN terminations
 #
 #
 
 
+@register_model_view(L2VPNTermination, 'list', path='', detail=False)
 class L2VPNTerminationListView(generic.ObjectListView):
 class L2VPNTerminationListView(generic.ObjectListView):
     queryset = L2VPNTermination.objects.all()
     queryset = L2VPNTermination.objects.all()
     table = tables.L2VPNTerminationTable
     table = tables.L2VPNTerminationTable
@@ -471,6 +519,7 @@ class L2VPNTerminationView(generic.ObjectView):
     queryset = L2VPNTermination.objects.all()
     queryset = L2VPNTermination.objects.all()
 
 
 
 
+@register_model_view(L2VPNTermination, 'add', detail=False)
 @register_model_view(L2VPNTermination, 'edit')
 @register_model_view(L2VPNTermination, 'edit')
 class L2VPNTerminationEditView(generic.ObjectEditView):
 class L2VPNTerminationEditView(generic.ObjectEditView):
     queryset = L2VPNTermination.objects.all()
     queryset = L2VPNTermination.objects.all()
@@ -482,11 +531,13 @@ class L2VPNTerminationDeleteView(generic.ObjectDeleteView):
     queryset = L2VPNTermination.objects.all()
     queryset = L2VPNTermination.objects.all()
 
 
 
 
+@register_model_view(L2VPNTermination, 'import', detail=False)
 class L2VPNTerminationBulkImportView(generic.BulkImportView):
 class L2VPNTerminationBulkImportView(generic.BulkImportView):
     queryset = L2VPNTermination.objects.all()
     queryset = L2VPNTermination.objects.all()
     model_form = forms.L2VPNTerminationImportForm
     model_form = forms.L2VPNTerminationImportForm
 
 
 
 
+@register_model_view(L2VPNTermination, 'bulk_edit', path='edit', detail=False)
 class L2VPNTerminationBulkEditView(generic.BulkEditView):
 class L2VPNTerminationBulkEditView(generic.BulkEditView):
     queryset = L2VPNTermination.objects.all()
     queryset = L2VPNTermination.objects.all()
     filterset = filtersets.L2VPNTerminationFilterSet
     filterset = filtersets.L2VPNTerminationFilterSet
@@ -494,6 +545,7 @@ class L2VPNTerminationBulkEditView(generic.BulkEditView):
     form = forms.L2VPNTerminationBulkEditForm
     form = forms.L2VPNTerminationBulkEditForm
 
 
 
 
+@register_model_view(L2VPNTermination, 'bulk_delete', path='delete', detail=False)
 class L2VPNTerminationBulkDeleteView(generic.BulkDeleteView):
 class L2VPNTerminationBulkDeleteView(generic.BulkDeleteView):
     queryset = L2VPNTermination.objects.all()
     queryset = L2VPNTermination.objects.all()
     filterset = filtersets.L2VPNTerminationFilterSet
     filterset = filtersets.L2VPNTerminationFilterSet

+ 4 - 19
netbox/wireless/urls.py

@@ -1,33 +1,18 @@
 from django.urls import include, path
 from django.urls import include, path
 
 
 from utilities.urls import get_model_urls
 from utilities.urls import get_model_urls
-from . import views
+from . import views  # noqa F401
 
 
 app_name = 'wireless'
 app_name = 'wireless'
 urlpatterns = (
 urlpatterns = (
 
 
-    # Wireless LAN groups
-    path('wireless-lan-groups/', views.WirelessLANGroupListView.as_view(), name='wirelesslangroup_list'),
-    path('wireless-lan-groups/add/', views.WirelessLANGroupEditView.as_view(), name='wirelesslangroup_add'),
-    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/', include(get_model_urls('wireless', 'wirelesslangroup', detail=False))),
     path('wireless-lan-groups/<int:pk>/', include(get_model_urls('wireless', 'wirelesslangroup'))),
     path('wireless-lan-groups/<int:pk>/', include(get_model_urls('wireless', 'wirelesslangroup'))),
 
 
-    # Wireless LANs
-    path('wireless-lans/', views.WirelessLANListView.as_view(), name='wirelesslan_list'),
-    path('wireless-lans/add/', views.WirelessLANEditView.as_view(), name='wirelesslan_add'),
-    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/', include(get_model_urls('wireless', 'wirelesslan', detail=False))),
     path('wireless-lans/<int:pk>/', include(get_model_urls('wireless', 'wirelesslan'))),
     path('wireless-lans/<int:pk>/', include(get_model_urls('wireless', 'wirelesslan'))),
 
 
-    # Wireless links
-    path('wireless-links/', views.WirelessLinkListView.as_view(), name='wirelesslink_list'),
-    path('wireless-links/add/', views.WirelessLinkEditView.as_view(), name='wirelesslink_add'),
-    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/', include(get_model_urls('wireless', 'wirelesslink', detail=False))),
     path('wireless-links/<int:pk>/', include(get_model_urls('wireless', 'wirelesslink'))),
     path('wireless-links/<int:pk>/', include(get_model_urls('wireless', 'wirelesslink'))),
 
 
 )
 )

+ 15 - 0
netbox/wireless/views.py

@@ -10,6 +10,7 @@ from .models import *
 # Wireless LAN groups
 # Wireless LAN groups
 #
 #
 
 
+@register_model_view(WirelessLANGroup, 'list', path='', detail=False)
 class WirelessLANGroupListView(generic.ObjectListView):
 class WirelessLANGroupListView(generic.ObjectListView):
     queryset = WirelessLANGroup.objects.add_related_count(
     queryset = WirelessLANGroup.objects.add_related_count(
         WirelessLANGroup.objects.all(),
         WirelessLANGroup.objects.all(),
@@ -35,6 +36,7 @@ class WirelessLANGroupView(GetRelatedModelsMixin, generic.ObjectView):
         }
         }
 
 
 
 
+@register_model_view(WirelessLANGroup, 'add', detail=False)
 @register_model_view(WirelessLANGroup, 'edit')
 @register_model_view(WirelessLANGroup, 'edit')
 class WirelessLANGroupEditView(generic.ObjectEditView):
 class WirelessLANGroupEditView(generic.ObjectEditView):
     queryset = WirelessLANGroup.objects.all()
     queryset = WirelessLANGroup.objects.all()
@@ -46,11 +48,13 @@ class WirelessLANGroupDeleteView(generic.ObjectDeleteView):
     queryset = WirelessLANGroup.objects.all()
     queryset = WirelessLANGroup.objects.all()
 
 
 
 
+@register_model_view(WirelessLANGroup, 'import', detail=False)
 class WirelessLANGroupBulkImportView(generic.BulkImportView):
 class WirelessLANGroupBulkImportView(generic.BulkImportView):
     queryset = WirelessLANGroup.objects.all()
     queryset = WirelessLANGroup.objects.all()
     model_form = forms.WirelessLANGroupImportForm
     model_form = forms.WirelessLANGroupImportForm
 
 
 
 
+@register_model_view(WirelessLANGroup, 'bulk_edit', path='edit', detail=False)
 class WirelessLANGroupBulkEditView(generic.BulkEditView):
 class WirelessLANGroupBulkEditView(generic.BulkEditView):
     queryset = WirelessLANGroup.objects.add_related_count(
     queryset = WirelessLANGroup.objects.add_related_count(
         WirelessLANGroup.objects.all(),
         WirelessLANGroup.objects.all(),
@@ -64,6 +68,7 @@ class WirelessLANGroupBulkEditView(generic.BulkEditView):
     form = forms.WirelessLANGroupBulkEditForm
     form = forms.WirelessLANGroupBulkEditForm
 
 
 
 
+@register_model_view(WirelessLANGroup, 'bulk_delete', path='delete', detail=False)
 class WirelessLANGroupBulkDeleteView(generic.BulkDeleteView):
 class WirelessLANGroupBulkDeleteView(generic.BulkDeleteView):
     queryset = WirelessLANGroup.objects.add_related_count(
     queryset = WirelessLANGroup.objects.add_related_count(
         WirelessLANGroup.objects.all(),
         WirelessLANGroup.objects.all(),
@@ -80,6 +85,7 @@ class WirelessLANGroupBulkDeleteView(generic.BulkDeleteView):
 # Wireless LANs
 # Wireless LANs
 #
 #
 
 
+@register_model_view(WirelessLAN, 'list', path='', detail=False)
 class WirelessLANListView(generic.ObjectListView):
 class WirelessLANListView(generic.ObjectListView):
     queryset = WirelessLAN.objects.annotate(
     queryset = WirelessLAN.objects.annotate(
         interface_count=count_related(Interface, 'wireless_lans')
         interface_count=count_related(Interface, 'wireless_lans')
@@ -105,6 +111,7 @@ class WirelessLANView(generic.ObjectView):
         }
         }
 
 
 
 
+@register_model_view(WirelessLAN, 'add', detail=False)
 @register_model_view(WirelessLAN, 'edit')
 @register_model_view(WirelessLAN, 'edit')
 class WirelessLANEditView(generic.ObjectEditView):
 class WirelessLANEditView(generic.ObjectEditView):
     queryset = WirelessLAN.objects.all()
     queryset = WirelessLAN.objects.all()
@@ -116,11 +123,13 @@ class WirelessLANDeleteView(generic.ObjectDeleteView):
     queryset = WirelessLAN.objects.all()
     queryset = WirelessLAN.objects.all()
 
 
 
 
+@register_model_view(WirelessLAN, 'import', detail=False)
 class WirelessLANBulkImportView(generic.BulkImportView):
 class WirelessLANBulkImportView(generic.BulkImportView):
     queryset = WirelessLAN.objects.all()
     queryset = WirelessLAN.objects.all()
     model_form = forms.WirelessLANImportForm
     model_form = forms.WirelessLANImportForm
 
 
 
 
+@register_model_view(WirelessLAN, 'bulk_edit', path='edit', detail=False)
 class WirelessLANBulkEditView(generic.BulkEditView):
 class WirelessLANBulkEditView(generic.BulkEditView):
     queryset = WirelessLAN.objects.all()
     queryset = WirelessLAN.objects.all()
     filterset = filtersets.WirelessLANFilterSet
     filterset = filtersets.WirelessLANFilterSet
@@ -128,6 +137,7 @@ class WirelessLANBulkEditView(generic.BulkEditView):
     form = forms.WirelessLANBulkEditForm
     form = forms.WirelessLANBulkEditForm
 
 
 
 
+@register_model_view(WirelessLAN, 'bulk_delete', path='delete', detail=False)
 class WirelessLANBulkDeleteView(generic.BulkDeleteView):
 class WirelessLANBulkDeleteView(generic.BulkDeleteView):
     queryset = WirelessLAN.objects.all()
     queryset = WirelessLAN.objects.all()
     filterset = filtersets.WirelessLANFilterSet
     filterset = filtersets.WirelessLANFilterSet
@@ -138,6 +148,7 @@ class WirelessLANBulkDeleteView(generic.BulkDeleteView):
 # Wireless Links
 # Wireless Links
 #
 #
 
 
+@register_model_view(WirelessLink, 'list', path='', detail=False)
 class WirelessLinkListView(generic.ObjectListView):
 class WirelessLinkListView(generic.ObjectListView):
     queryset = WirelessLink.objects.all()
     queryset = WirelessLink.objects.all()
     filterset = filtersets.WirelessLinkFilterSet
     filterset = filtersets.WirelessLinkFilterSet
@@ -150,6 +161,7 @@ class WirelessLinkView(generic.ObjectView):
     queryset = WirelessLink.objects.all()
     queryset = WirelessLink.objects.all()
 
 
 
 
+@register_model_view(WirelessLink, 'add', detail=False)
 @register_model_view(WirelessLink, 'edit')
 @register_model_view(WirelessLink, 'edit')
 class WirelessLinkEditView(generic.ObjectEditView):
 class WirelessLinkEditView(generic.ObjectEditView):
     queryset = WirelessLink.objects.all()
     queryset = WirelessLink.objects.all()
@@ -161,11 +173,13 @@ class WirelessLinkDeleteView(generic.ObjectDeleteView):
     queryset = WirelessLink.objects.all()
     queryset = WirelessLink.objects.all()
 
 
 
 
+@register_model_view(WirelessLink, 'import', detail=False)
 class WirelessLinkBulkImportView(generic.BulkImportView):
 class WirelessLinkBulkImportView(generic.BulkImportView):
     queryset = WirelessLink.objects.all()
     queryset = WirelessLink.objects.all()
     model_form = forms.WirelessLinkImportForm
     model_form = forms.WirelessLinkImportForm
 
 
 
 
+@register_model_view(WirelessLink, 'bulk_edit', path='edit', detail=False)
 class WirelessLinkBulkEditView(generic.BulkEditView):
 class WirelessLinkBulkEditView(generic.BulkEditView):
     queryset = WirelessLink.objects.all()
     queryset = WirelessLink.objects.all()
     filterset = filtersets.WirelessLinkFilterSet
     filterset = filtersets.WirelessLinkFilterSet
@@ -173,6 +187,7 @@ class WirelessLinkBulkEditView(generic.BulkEditView):
     form = forms.WirelessLinkBulkEditForm
     form = forms.WirelessLinkBulkEditForm
 
 
 
 
+@register_model_view(WirelessLink, 'bulk_delete', path='delete', detail=False)
 class WirelessLinkBulkDeleteView(generic.BulkDeleteView):
 class WirelessLinkBulkDeleteView(generic.BulkDeleteView):
     queryset = WirelessLink.objects.all()
     queryset = WirelessLink.objects.all()
     filterset = filtersets.WirelessLinkFilterSet
     filterset = filtersets.WirelessLinkFilterSet

Some files were not shown because too many files changed in this diff