From 83a60203f78ca6071218b6f9b41d593379c44d11 Mon Sep 17 00:00:00 2001 From: Ross Date: Mon, 3 Nov 2025 10:47:40 +0000 Subject: [PATCH] Enhance filtering functionality and UI consistency across user-related views by implementing a generic search filter and updating filter display in templates. --- generic/filters.py | 45 +++++++++++++++++++ generic/tables.py | 30 ++++++------- generic/templates/generic/cid_view.html | 13 +----- .../templates/generic/supervisor_list.html | 13 +----- generic/templates/generic/user_view.html | 1 + 5 files changed, 63 insertions(+), 39 deletions(-) diff --git a/generic/filters.py b/generic/filters.py index fe147cd9..d2e883e0 100644 --- a/generic/filters.py +++ b/generic/filters.py @@ -1,4 +1,6 @@ import django_filters +from django.db.models import Q +from collections import OrderedDict from django.contrib.auth.models import User @@ -34,6 +36,8 @@ class CidUserFilter(django_filters.FilterSet): return parent class UserUserFilter(django_filters.FilterSet): + # Generic search across name and email/username + q = django_filters.CharFilter(method='filter_search', label='Search') userprofile__grade = django_filters.ModelChoiceFilter(queryset=UserGrades.objects.all(),label="Grade") userprofile__supervisor = django_filters.ModelChoiceFilter(queryset=Supervisor.objects.all(), label="Supervisor") userprofile__peninsula_trainee = django_filters.BooleanFilter(label="Peninsula Trainee") @@ -69,6 +73,47 @@ class UserUserFilter(django_filters.FilterSet): super(UserUserFilter, self).__init__(*args, **kwargs) self.form.initial['is_active'] = True + def filter_search(self, queryset, name, value): + """ + Generic search that matches first name, last name, email or username. + """ + v = (value or "").strip() + if not v: + return queryset + + return queryset.filter( + Q(first_name__icontains=v) + | Q(last_name__icontains=v) + | Q(email__icontains=v) + | Q(username__icontains=v) + ) + + def __post_init_reorder__(self): + """Internal helper to move the generic search 'q' to the front of the form fields.""" + try: + fields = self.form.fields + if 'q' in fields: + new_order = ['q'] + [k for k in fields.keys() if k != 'q'] + self.form.fields = OrderedDict((k, fields[k]) for k in new_order) + except Exception: + pass + + # Ensure reorder runs after initialization + def __new__(cls, *args, **kwargs): + instance = super(UserUserFilter, cls).__new__(cls) + # attach a post-init hook to reorder form fields + orig_init = instance.__init__ + + def wrapped_init(*a, **k): + orig_init(*a, **k) + try: + instance.__post_init_reorder__() + except Exception: + pass + + instance.__init__ = wrapped_init + return instance + class ExaminationFilter(django_filters.FilterSet): class Meta: diff --git a/generic/tables.py b/generic/tables.py index 83c2cadd..67b1132f 100644 --- a/generic/tables.py +++ b/generic/tables.py @@ -151,7 +151,7 @@ class FirstImageColumn(tables.Column): return format_html('Invalid image url', image_object) -class CidUserTable(tables.Table): +class CidUserTable(SelectionTable): cid = tables.LinkColumn( "cid_scores", args=[A("cid"), A("passcode")], orderable=True ) @@ -163,7 +163,7 @@ class CidUserTable(tables.Table): # anatomy_exams = tables.ManyToManyColumn(verbose_name="Anatomy Exams") group = tables.Column(linkify=True) - selection = tables.CheckBoxColumn(accessor="pk", orderable=False) + #selection = tables.CheckBoxColumn(accessor="pk", orderable=False) emails = tables.Column(empty_values=(), orderable=False) @@ -171,7 +171,7 @@ class CidUserTable(tables.Table): "generic:update_cid", text="Edit", args=[A("pk")], orderable=True ) - class Meta: + class Meta(SelectionTable.Meta): model = CidUser template_name = "django_tables2/bootstrap4.html" fields = ( @@ -199,7 +199,7 @@ class CidUserTable(tables.Table): return format_html(record.email) -class CidUserExamTable(tables.Table): +class CidUserExamTable(SelectionTable): cid = tables.LinkColumn( "cid_scores", args=[A("cid"), A("passcode")], orderable=True ) @@ -210,7 +210,7 @@ class CidUserExamTable(tables.Table): longs_exams = tables.ManyToManyColumn(verbose_name="Long Exams") anatomy_exams = tables.ManyToManyColumn(verbose_name="Anatomy Exams") - selection = tables.CheckBoxColumn(accessor="pk", orderable=False) + #selection = tables.CheckBoxColumn(accessor="pk", orderable=False) emails = tables.Column(empty_values=(), orderable=False) @@ -218,7 +218,7 @@ class CidUserExamTable(tables.Table): "generic:update_cid", text="Edit", args=[A("pk")], orderable=True ) - class Meta: + class Meta(SelectionTable.Meta): model = CidUser template_name = "django_tables2/bootstrap4.html" fields = ( @@ -256,7 +256,7 @@ class CidUserExamTable(tables.Table): # return format_html("""Popup""", record.pk) -class UserUserTable(tables.Table): +class UserUserTable(SelectionTable): user = tables.Column( linkify={"viewname": "account_profile", "args": [A("username")]}, @@ -275,7 +275,7 @@ class UserUserTable(tables.Table): accessor=A("userprofile__supervisor"), ) - selection = tables.CheckBoxColumn(accessor="pk", orderable=False) + #selection = tables.CheckBoxColumn(accessor="pk", orderable=False) edit = tables.Column( empty_values=(), @@ -294,7 +294,7 @@ class UserUserTable(tables.Table): date_joined = tables.DateTimeColumn(format="Y/m/d") - class Meta: + class Meta(SelectionTable.Meta): model = User template_name = "django_tables2/bootstrap4.html" fields = ( @@ -348,7 +348,7 @@ class UserUserTable(tables.Table): record.email, ) -class ExaminationTable(tables.Table): +class ExaminationTable(SelectionTable): examination = tables.Column( linkify=("generic:examination_detail", {"pk": tables.A("pk")}), verbose_name="Condition", @@ -360,19 +360,19 @@ class ExaminationTable(tables.Table): delete = tables.LinkColumn( "generic:examination_delete", text="Delete", args=[A("pk")], orderable=False ) - selection = tables.CheckBoxColumn(accessor="pk", orderable=False) + #selection = tables.CheckBoxColumn(accessor="pk", orderable=False) # synonym = tables.ManyToManyColumn(verbose_name="Synonyms") #synonym = tables.Column(empty_values=()) #parent = tables.ManyToManyColumn(verbose_name="Parents") - class Meta: + class Meta(SelectionTable.Meta): model = Examination template_name = "django_tables2/bootstrap4.html" #fields = ("primary", "subspecialty") sequence = ("examination",) -class SupervisorTable(tables.Table): +class SupervisorTable(SelectionTable): name = tables.Column( linkify=("generic:supervisor_detail", {"pk": tables.A("pk")}), verbose_name="Name", @@ -383,13 +383,13 @@ class SupervisorTable(tables.Table): accessor="pk", orderable=False ) - class Meta: + class Meta(SelectionTable.Meta): model = Supervisor template_name = "django_tables2/bootstrap4.html" sequence = ("name", "email") exclude = ("id",) - attrs = {"id": "supervisor-table"} + SelectionTable.Meta.attrs.update({"id": "supervisor-table"}) def render_edit(self, value, record): url = f"{reverse("generic:supervisor_edit", args=[record.pk])}?redirect={self.request.path}" diff --git a/generic/templates/generic/cid_view.html b/generic/templates/generic/cid_view.html index de0ca2de..6fc22bb3 100644 --- a/generic/templates/generic/cid_view.html +++ b/generic/templates/generic/cid_view.html @@ -7,23 +7,12 @@ {% block content %} -
-
- -

Filter CID Users

-
-
- {{ filter.form |crispy}} - -
-
-
-
{% render_table table %}
+ {% include "generic/partials/filter_bar.html" with filter=filter app_name=app_name collapse_id="bottom-filter-body" %}
diff --git a/generic/templates/generic/supervisor_list.html b/generic/templates/generic/supervisor_list.html index 026ca4e4..6fde21d7 100755 --- a/generic/templates/generic/supervisor_list.html +++ b/generic/templates/generic/supervisor_list.html @@ -4,18 +4,6 @@

Supervisors

-
- -

Filter

-
-
-
- {{ filter.form }} - -
-
-
- {% render_table table %} {% comment %}
    @@ -25,6 +13,7 @@ {% endfor %}
{% endcomment %} + {% include "generic/partials/filter_bar.html" with filter=filter app_name=app_name collapse_id="bottom-filter-body" %} {% endblock %} diff --git a/generic/templates/generic/user_view.html b/generic/templates/generic/user_view.html index 144b6924..490aaf77 100644 --- a/generic/templates/generic/user_view.html +++ b/generic/templates/generic/user_view.html @@ -71,6 +71,7 @@ {% render_table table %} + {% include "generic/partials/filter_bar.html" with filter=filter app_name=app_name collapse_id="bottom-filter-body" %} Bulk create users Create single user