From b885bf6da5f029a522960e7874c547d161f76adc Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Sun, 11 Jan 2026 22:04:34 +0100 Subject: [PATCH] Activate member deletion functionality: implement `MemberDeleteView` with HTMX integration, add `is_active` filter, update member filter template, and refine related styles. --- backend/members/urls.py | 2 +- backend/members/views.py | 34 +++++++++++++++++++++++++++- backend/mixins.py | 4 ++-- members/filters.py | 3 ++- templates/members/member_filter.html | 2 +- theme/static_src/src/styles.css | 1 + 6 files changed, 40 insertions(+), 6 deletions(-) diff --git a/backend/members/urls.py b/backend/members/urls.py index ddfff65..be8770d 100644 --- a/backend/members/urls.py +++ b/backend/members/urls.py @@ -7,6 +7,6 @@ urlpatterns = [ path("", MemberListView.as_view(), name="list"), # path("add/", MemberAddView.as_view(), name="add"), # path("/edit/", MemberEditView.as_view(), name="edit"), - # path("/delete/", MemberDeleteView.as_view(), name="delete"), + path("/delete/", MemberDeleteView.as_view(), name="delete"), # path("load/", MemberLoadView.as_view(), name="load"), ] diff --git a/backend/members/views.py b/backend/members/views.py index a206ece..eec36e8 100644 --- a/backend/members/views.py +++ b/backend/members/views.py @@ -1,9 +1,11 @@ from typing import Any from django.contrib import messages +from django.contrib.messages.views import SuccessMessageMixin from django.http import HttpResponse, HttpResponseRedirect from django.urls import reverse_lazy from django.utils.translation import gettext_lazy as _ +from django.views.generic import DeleteView from django_filters.views import FilterView from rules.contrib.views import PermissionRequiredMixin @@ -21,6 +23,17 @@ class MemberListView(HTMXViewMixin, PermissionRequiredMixin, FilterView): def handle_no_permission(self) -> HttpResponseRedirect: messages.error(self.request, self.get_permission_denied_message()) return HttpResponseRedirect(reverse_lazy("backend:index")) + + def get_filterset_kwargs(self, filterset_class) -> dict[str, Any]: + kwargs = super().get_filterset_kwargs(filterset_class) + + filter_values = {} if kwargs["data"] is None else kwargs["data"].dict() + + if not filter_values: + filter_values.update({"user__is_active": "true"}) + + kwargs["data"] = filter_values + return kwargs class MemberAddView: ... @@ -29,7 +42,26 @@ class MemberAddView: ... class MemberEditView: ... -class MemberDeleteView: ... +class MemberDeleteView(HTMXViewMixin, PermissionRequiredMixin, SuccessMessageMixin, DeleteView): + model = Member + success_message = _("Member %(name)s has been deleted successfully.") + permission_required = "members.delete_member" + permission_denied_message = _("You do not have permission to view this page.") + success_url = reverse_lazy("backend:members:list") + + def handle_no_permission(self) -> HttpResponseRedirect: + messages.error(self.request, self.get_permission_denied_message()) + return HttpResponseRedirect(reverse_lazy("backend:index")) + + def get_success_message(self, cleaned_data): + return self.success_message % dict(cleaned_data, name=self.object.user.get_full_name()) + + def delete(self, request, *args, **kwargs) -> HttpResponseRedirect: + self.object = self.get_object() + self.object.user.is_active = False + self.object.user.save() + + return HttpResponseRedirect(self.get_success_url()) class MemberLoadView: ... diff --git a/backend/mixins.py b/backend/mixins.py index a798796..60cb82b 100644 --- a/backend/mixins.py +++ b/backend/mixins.py @@ -17,14 +17,14 @@ class HTMXPartialMixin: class HTMXViewMixin: """ - A full featured HTMX integration mixin for Django CBVs. + A full-featured HTMX integration mixin for Django CBVs. Supports: - partial rendering via django-partials - HX-Redirect - HX-Push-URL - HX-Trigger events - HX-Refresh - - Graceful fallback to normal DJango rendering + - Graceful fallback to normal Django rendering """ # Name of the partial block: template.html#partial_name diff --git a/members/filters.py b/members/filters.py index 1dc44a6..b2fe60a 100644 --- a/members/filters.py +++ b/members/filters.py @@ -8,9 +8,10 @@ class MemberFilter(django_filters.FilterSet): user__first_name = django_filters.CharFilter(field_name="user__first_name", label=_("First name"), lookup_expr="icontains") user__last_name = django_filters.CharFilter(field_name="user__last_name", label=_("Last name"), lookup_expr="icontains") license = django_filters.CharFilter(label=_("License"), lookup_expr="icontains") + user__is_active = django_filters.TypedChoiceFilter( field_name='user__is_active', label=_("Active?"), initial="true", choices=( ('', 'All'), ('true', 'Yes'), ('false', 'No'), ), coerce=lambda x: x.lower() == 'true' ) class Meta: model = Member - fields = ["user__first_name", "user__last_name", "license"] + fields = ["user__first_name", "user__last_name", "license", "user__is_active"] \ No newline at end of file diff --git a/templates/members/member_filter.html b/templates/members/member_filter.html index 7aba9d5..9089f83 100644 --- a/templates/members/member_filter.html +++ b/templates/members/member_filter.html @@ -129,7 +129,7 @@ {% translate "Details" %} - + {% translate "Delete" %} diff --git a/theme/static_src/src/styles.css b/theme/static_src/src/styles.css index 5c81457..f0696be 100644 --- a/theme/static_src/src/styles.css +++ b/theme/static_src/src/styles.css @@ -57,6 +57,7 @@ } @source inline("input-{xs,sm,md,lg,xl}"); +@source inline("select-{xs,sm,md,lg,xl}"); @layer components { h1.page-title {