Add bulk delete functionality for questions with confirmation feedback

This commit is contained in:
Ross
2025-10-20 12:32:59 +01:00
parent 1085b9e6a2
commit 980bb199cf
4 changed files with 101 additions and 3 deletions
@@ -0,0 +1,4 @@
<div class="alert alert-info">Deleted {{ deleted }} items.</div>
{% if errors %}
<div class="alert alert-warning">Errors: {{ errors }}</div>
{% endif %}
+1
View File
@@ -34,6 +34,7 @@ urlpatterns = [
views.generic_exam_json_edit,
name="generic_exam_json_edit",
),
path("bulk_delete_questions/", views.bulk_delete_questions, name="bulk_delete_questions"),
path(
"examination-autocomplete",
views.ExaminationAutocomplete.as_view(
+59
View File
@@ -149,6 +149,65 @@ from django.db.models import Prefetch
from loguru import logger
@login_required
@user_passes_test(lambda u: u.is_superuser)
def bulk_delete_questions(request):
"""Generic bulk delete for question-like models.
Expects POST with 'app' (app_name) and 'selected' (list or comma-separated ids).
Returns a small HTML fragment summarising deletions (suitable for HTMX).
"""
if request.method != "POST":
return HttpResponse(status=405)
app = request.POST.get("app")
selected_list = request.POST.getlist("selected")
selected_raw = request.POST.get("selected")
ids = set()
if selected_list:
for s in selected_list:
try:
ids.add(int(s))
except Exception:
continue
elif selected_raw:
for part in selected_raw.split(','):
try:
ids.add(int(part))
except Exception:
continue
# Map app name to model class
APP_MODEL_MAP = {
"sbas": SbasQuestion,
"physics": PhysicsQuestion,
"anatomy": AnatomyQuestion,
"rapids": RapidQuestion,
"rapid": RapidQuestion,
"shorts": ShortsQuestion,
"longs": LongQuestion,
"long": LongQuestion,
}
Model = APP_MODEL_MAP.get(app)
if Model is None:
return JsonResponse({"ok": False, "error": f"Unknown app: {app}"}, status=400)
deleted = 0
errors = []
try:
qs = Model.objects.filter(pk__in=list(ids))
deleted = qs.count()
qs.delete()
except Exception as e:
logger.exception("bulk_delete failed for app=%s ids=%s", app, ids)
return JsonResponse({"ok": False, "error": str(e)}, status=500)
# Render a small fragment
return render(request, "generic/partials/bulk_delete_result.html", {"deleted": deleted, "errors": errors})
class RedirectMixin:
def get_success_url(self) -> str:
if "redirect" in self.request.GET: