Activate member deletion functionality: implement MemberDeleteView with HTMX integration, add is_active filter, update member filter template, and refine related styles.

This commit is contained in:
2026-01-11 22:04:34 +01:00
parent d2d50afdd7
commit b885bf6da5
6 changed files with 40 additions and 6 deletions

View File

@@ -7,6 +7,6 @@ urlpatterns = [
path("", MemberListView.as_view(), name="list"), path("", MemberListView.as_view(), name="list"),
# path("add/", MemberAddView.as_view(), name="add"), # path("add/", MemberAddView.as_view(), name="add"),
# path("<int:pk>/edit/", MemberEditView.as_view(), name="edit"), # path("<int:pk>/edit/", MemberEditView.as_view(), name="edit"),
# path("<int:pk>/delete/", MemberDeleteView.as_view(), name="delete"), path("<int:pk>/delete/", MemberDeleteView.as_view(), name="delete"),
# path("load/", MemberLoadView.as_view(), name="load"), # path("load/", MemberLoadView.as_view(), name="load"),
] ]

View File

@@ -1,9 +1,11 @@
from typing import Any from typing import Any
from django.contrib import messages from django.contrib import messages
from django.contrib.messages.views import SuccessMessageMixin
from django.http import HttpResponse, HttpResponseRedirect from django.http import HttpResponse, HttpResponseRedirect
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.views.generic import DeleteView
from django_filters.views import FilterView from django_filters.views import FilterView
from rules.contrib.views import PermissionRequiredMixin from rules.contrib.views import PermissionRequiredMixin
@@ -22,6 +24,17 @@ class MemberListView(HTMXViewMixin, PermissionRequiredMixin, FilterView):
messages.error(self.request, self.get_permission_denied_message()) messages.error(self.request, self.get_permission_denied_message())
return HttpResponseRedirect(reverse_lazy("backend:index")) 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: ... class MemberAddView: ...
@@ -29,7 +42,26 @@ class MemberAddView: ...
class MemberEditView: ... class MemberEditView: ...
class MemberDeleteView: ... class MemberDeleteView(HTMXViewMixin, PermissionRequiredMixin, SuccessMessageMixin, DeleteView):
model = Member
success_message = _("Member <strong>%(name)s</strong> 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: ... class MemberLoadView: ...

View File

@@ -17,14 +17,14 @@ class HTMXPartialMixin:
class HTMXViewMixin: class HTMXViewMixin:
""" """
A full featured HTMX integration mixin for Django CBVs. A full-featured HTMX integration mixin for Django CBVs.
Supports: Supports:
- partial rendering via django-partials - partial rendering via django-partials
- HX-Redirect - HX-Redirect
- HX-Push-URL - HX-Push-URL
- HX-Trigger events - HX-Trigger events
- HX-Refresh - HX-Refresh
- Graceful fallback to normal DJango rendering - Graceful fallback to normal Django rendering
""" """
# Name of the partial block: template.html#partial_name # Name of the partial block: template.html#partial_name

View File

@@ -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__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") 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") 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: class Meta:
model = Member model = Member
fields = ["user__first_name", "user__last_name", "license"] fields = ["user__first_name", "user__last_name", "license", "user__is_active"]

View File

@@ -129,7 +129,7 @@
<i class="fa-solid fa-eye"></i>{% translate "Details" %} <i class="fa-solid fa-eye"></i>{% translate "Details" %}
</a> </a>
<a class="btn btn-outline btn-error btn-sm" href=""> <a class="btn btn-outline btn-error btn-sm" href="{% url "backend:members:delete" member.pk %}">
<i class="fa-solid fa-trash"></i>{% translate "Delete" %} <i class="fa-solid fa-trash"></i>{% translate "Delete" %}
</a> </a>
</div> </div>

View File

@@ -57,6 +57,7 @@
} }
@source inline("input-{xs,sm,md,lg,xl}"); @source inline("input-{xs,sm,md,lg,xl}");
@source inline("select-{xs,sm,md,lg,xl}");
@layer components { @layer components {
h1.page-title { h1.page-title {