Repository: netbox-community/netbox

Issue:
Leverage HTMX for confirmation dialogs
### NetBox version

v3.1.4

### Feature type

Change to existing functionality

### Proposed functionality

Currently, when deleting an object, two requests are made:

1. The user clicks delete and is taken to the deletion confirmation form.
2. The user submits the confirmation form, the object is deleted, and the user is redirected to the return URL.

It should be trivial to instead use a modal dialog to display the confirmation form. The proposed workflow would be:

1. The user clicks delete and an HTMX request is made to retrieve the confirmation form (and any relevant information, such as related objects). The form is rendered in a modal and displayed, but the user remains on the original page.
2. The user submits the confirmation form, the object is deleted, and the user is redirected to the return URL.

Although we're still technically making two requests, the first loads only the confirmation form and avoids a complete page request.

We should be able to implement this entirely within the relevant generic view classes.

### Use case

Loading the confirmation dialog via HTMX avoids a full page refresh and greatly reduces the time needed to delete an object (and especially to abort the deletion of an object).

### Database changes

_No response_

### External dependencies

_No response_


Discussion:

Resolving patch:
diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py
--- a/netbox/ipam/views.py
+++ b/netbox/ipam/views.py
@@ -529,7 +529,6 @@ class PrefixEditView(generic.ObjectEditView):
 
 class PrefixDeleteView(generic.ObjectDeleteView):
     queryset = Prefix.objects.all()
-    template_name = 'ipam/prefix_delete.html'
 
 
 class PrefixBulkImportView(generic.BulkImportView):
diff --git a/netbox/netbox/views/generic.py b/netbox/netbox/views/generic.py
--- a/netbox/netbox/views/generic.py
+++ b/netbox/netbox/views/generic.py
@@ -10,6 +10,7 @@
 from django.forms import Form, ModelMultipleChoiceField, MultipleHiddenInput, Textarea
 from django.http import HttpResponse
 from django.shortcuts import get_object_or_404, redirect, render
+from django.urls import reverse
 from django.utils.html import escape
 from django.utils.http import is_safe_url
 from django.utils.safestring import mark_safe
@@ -430,10 +431,21 @@ def get(self, request, **kwargs):
         obj = self.get_object(kwargs)
         form = ConfirmationForm(initial=request.GET)
 
+        # If this is an HTMX request, return only the rendered deletion form as modal content
+        if is_htmx(request):
+            viewname = f'{self.queryset.model._meta.app_label}:{self.queryset.model._meta.model_name}_delete'
+            form_url = reverse(viewname, kwargs={'pk': obj.pk})
+            return render(request, 'htmx/delete_form.html', {
+                'object': obj,
+                'object_type': self.queryset.model._meta.verbose_name,
+                'form': form,
+                'form_url': form_url,
+            })
+
         return render(request, self.template_name, {
-            'obj': obj,
+            'object': obj,
+            'object_type': self.queryset.model._meta.verbose_name,
             'form': form,
-            'obj_type': self.queryset.model._meta.verbose_name,
             'return_url': self.get_return_url(request, obj),
         })
 
@@ -466,9 +478,9 @@ def post(self, request, **kwargs):
             logger.debug("Form validation failed")
 
         return render(request, self.template_name, {
-            'obj': obj,
+            'object': obj,
+            'object_type': self.queryset.model._meta.verbose_name,
             'form': form,
-            'obj_type': self.queryset.model._meta.verbose_name,
             'return_url': self.get_return_url(request, obj),
         })
 


Test patch: