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:
@@ -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
@@ -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}"
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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 %}
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user