Enhance filtering functionality and UI consistency across user-related views by implementing a generic search filter and updating filter display in templates.

This commit is contained in:
Ross
2025-11-03 10:47:40 +00:00
parent 2d0098b2b6
commit 83a60203f7
5 changed files with 63 additions and 39 deletions
+45
View File
@@ -1,4 +1,6 @@
import django_filters import django_filters
from django.db.models import Q
from collections import OrderedDict
from django.contrib.auth.models import User from django.contrib.auth.models import User
@@ -34,6 +36,8 @@ class CidUserFilter(django_filters.FilterSet):
return parent return parent
class UserUserFilter(django_filters.FilterSet): 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__grade = django_filters.ModelChoiceFilter(queryset=UserGrades.objects.all(),label="Grade")
userprofile__supervisor = django_filters.ModelChoiceFilter(queryset=Supervisor.objects.all(), label="Supervisor") userprofile__supervisor = django_filters.ModelChoiceFilter(queryset=Supervisor.objects.all(), label="Supervisor")
userprofile__peninsula_trainee = django_filters.BooleanFilter(label="Peninsula Trainee") userprofile__peninsula_trainee = django_filters.BooleanFilter(label="Peninsula Trainee")
@@ -69,6 +73,47 @@ class UserUserFilter(django_filters.FilterSet):
super(UserUserFilter, self).__init__(*args, **kwargs) super(UserUserFilter, self).__init__(*args, **kwargs)
self.form.initial['is_active'] = True 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 ExaminationFilter(django_filters.FilterSet):
class Meta: class Meta:
+15 -15
View File
@@ -151,7 +151,7 @@ class FirstImageColumn(tables.Column):
return format_html('<span title="{}">Invalid image url<span>', image_object) return format_html('<span title="{}">Invalid image url<span>', image_object)
class CidUserTable(tables.Table): class CidUserTable(SelectionTable):
cid = tables.LinkColumn( cid = tables.LinkColumn(
"cid_scores", args=[A("cid"), A("passcode")], orderable=True "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") # anatomy_exams = tables.ManyToManyColumn(verbose_name="Anatomy Exams")
group = tables.Column(linkify=True) 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) 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 "generic:update_cid", text="Edit", args=[A("pk")], orderable=True
) )
class Meta: class Meta(SelectionTable.Meta):
model = CidUser model = CidUser
template_name = "django_tables2/bootstrap4.html" template_name = "django_tables2/bootstrap4.html"
fields = ( fields = (
@@ -199,7 +199,7 @@ class CidUserTable(tables.Table):
return format_html(record.email) return format_html(record.email)
class CidUserExamTable(tables.Table): class CidUserExamTable(SelectionTable):
cid = tables.LinkColumn( cid = tables.LinkColumn(
"cid_scores", args=[A("cid"), A("passcode")], orderable=True "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") longs_exams = tables.ManyToManyColumn(verbose_name="Long Exams")
anatomy_exams = tables.ManyToManyColumn(verbose_name="Anatomy 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) 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 "generic:update_cid", text="Edit", args=[A("pk")], orderable=True
) )
class Meta: class Meta(SelectionTable.Meta):
model = CidUser model = CidUser
template_name = "django_tables2/bootstrap4.html" template_name = "django_tables2/bootstrap4.html"
fields = ( fields = (
@@ -256,7 +256,7 @@ class CidUserExamTable(tables.Table):
# return format_html("""<a href="#" onclick="return window.create_popup_window('/longs/series/{}', 'Series')" >Popup</a>""", record.pk) # return format_html("""<a href="#" onclick="return window.create_popup_window('/longs/series/{}', 'Series')" >Popup</a>""", record.pk)
class UserUserTable(tables.Table): class UserUserTable(SelectionTable):
user = tables.Column( user = tables.Column(
linkify={"viewname": "account_profile", "args": [A("username")]}, linkify={"viewname": "account_profile", "args": [A("username")]},
@@ -275,7 +275,7 @@ class UserUserTable(tables.Table):
accessor=A("userprofile__supervisor"), accessor=A("userprofile__supervisor"),
) )
selection = tables.CheckBoxColumn(accessor="pk", orderable=False) #selection = tables.CheckBoxColumn(accessor="pk", orderable=False)
edit = tables.Column( edit = tables.Column(
empty_values=(), empty_values=(),
@@ -294,7 +294,7 @@ class UserUserTable(tables.Table):
date_joined = tables.DateTimeColumn(format="Y/m/d") date_joined = tables.DateTimeColumn(format="Y/m/d")
class Meta: class Meta(SelectionTable.Meta):
model = User model = User
template_name = "django_tables2/bootstrap4.html" template_name = "django_tables2/bootstrap4.html"
fields = ( fields = (
@@ -348,7 +348,7 @@ class UserUserTable(tables.Table):
record.email, record.email,
) )
class ExaminationTable(tables.Table): class ExaminationTable(SelectionTable):
examination = tables.Column( examination = tables.Column(
linkify=("generic:examination_detail", {"pk": tables.A("pk")}), linkify=("generic:examination_detail", {"pk": tables.A("pk")}),
verbose_name="Condition", verbose_name="Condition",
@@ -360,19 +360,19 @@ class ExaminationTable(tables.Table):
delete = tables.LinkColumn( delete = tables.LinkColumn(
"generic:examination_delete", text="Delete", args=[A("pk")], orderable=False "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.ManyToManyColumn(verbose_name="Synonyms")
#synonym = tables.Column(empty_values=()) #synonym = tables.Column(empty_values=())
#parent = tables.ManyToManyColumn(verbose_name="Parents") #parent = tables.ManyToManyColumn(verbose_name="Parents")
class Meta: class Meta(SelectionTable.Meta):
model = Examination model = Examination
template_name = "django_tables2/bootstrap4.html" template_name = "django_tables2/bootstrap4.html"
#fields = ("primary", "subspecialty") #fields = ("primary", "subspecialty")
sequence = ("examination",) sequence = ("examination",)
class SupervisorTable(tables.Table): class SupervisorTable(SelectionTable):
name = tables.Column( name = tables.Column(
linkify=("generic:supervisor_detail", {"pk": tables.A("pk")}), linkify=("generic:supervisor_detail", {"pk": tables.A("pk")}),
verbose_name="Name", verbose_name="Name",
@@ -383,13 +383,13 @@ class SupervisorTable(tables.Table):
accessor="pk", orderable=False accessor="pk", orderable=False
) )
class Meta: class Meta(SelectionTable.Meta):
model = Supervisor model = Supervisor
template_name = "django_tables2/bootstrap4.html" template_name = "django_tables2/bootstrap4.html"
sequence = ("name", "email") sequence = ("name", "email")
exclude = ("id",) exclude = ("id",)
attrs = {"id": "supervisor-table"} SelectionTable.Meta.attrs.update({"id": "supervisor-table"})
def render_edit(self, value, record): def render_edit(self, value, record):
url = f"{reverse("generic:supervisor_edit", args=[record.pk])}?redirect={self.request.path}" url = f"{reverse("generic:supervisor_edit", args=[record.pk])}?redirect={self.request.path}"
+1 -12
View File
@@ -7,23 +7,12 @@
{% block content %} {% block content %}
<div id="view-filter-options">
<details class="filter">
<summary>
<h3>Filter CID Users </h3>
</summary>
<form action="" method="get">
{{ filter.form |crispy}}
<input class="btn btn-primary btn-sm mt-1 mb-1" type="submit" />
</form>
</details>
</div>
<div> <div>
<span id="manage-span"> <span id="manage-span">
{% render_table table %} {% render_table table %}
</span> </span>
</div> </div>
{% include "generic/partials/filter_bar.html" with filter=filter app_name=app_name collapse_id="bottom-filter-body" %}
<details> <details>
<summary> <summary>
+1 -12
View File
@@ -4,18 +4,6 @@
<h2>Supervisors</h2> <h2>Supervisors</h2>
<details>
<summary>
<h4>Filter</h4>
</summary>
<div id="view-filter-options">
<form action="" method="get">
{{ filter.form }}
<input class="btn btn-primary btn-sm mt-1 mb-1" type="submit" />
</form>
</div>
</details>
{% render_table table %} {% render_table table %}
{% comment %} <ul id="supervisor-list"> {% comment %} <ul id="supervisor-list">
@@ -25,6 +13,7 @@
</li> </li>
{% endfor %} {% endfor %}
</ul> {% endcomment %} </ul> {% endcomment %}
{% include "generic/partials/filter_bar.html" with filter=filter app_name=app_name collapse_id="bottom-filter-body" %}
{% endblock %} {% endblock %}
+1
View File
@@ -71,6 +71,7 @@
{% render_table table %} {% render_table table %}
</span> </span>
</div> </div>
{% include "generic/partials/filter_bar.html" with filter=filter app_name=app_name collapse_id="bottom-filter-body" %}
<a href="{% url 'accounts_bulk_create' %}">Bulk create users</a> <a href="{% url 'accounts_bulk_create' %}">Bulk create users</a>
<a href="{% url 'create_user' %}">Create single user</a> <a href="{% url 'create_user' %}">Create single user</a>