From 222ddab9cbda8b3cbb68cc4eabc74bd64ce8885d Mon Sep 17 00:00:00 2001 From: Zaki Benaissa Date: Fri, 5 Jan 2024 15:58:02 +0100 Subject: [PATCH 1/3] Add django-filters as a dependency and an app --- config/settings.py | 1 + requirements/base.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/config/settings.py b/config/settings.py index 809e68d..d01178e 100644 --- a/config/settings.py +++ b/config/settings.py @@ -49,6 +49,7 @@ DJANGO_APPS = [ THIRD_PARTY_APPS = [ "crispy_forms", "rest_framework", + "django_filters", ] # Custom apps diff --git a/requirements/base.txt b/requirements/base.txt index 76a65b6..9970d33 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -15,6 +15,7 @@ django-crispy-forms==1.14.0 # https://github.com/django-crispy-forms/django-cri crispy-bootstrap5==0.7 # https://github.com/django-crispy-forms/crispy-bootstrap5 django-compressor==4.1 # https://github.com/django-compressor/django-compressor django-redis==5.2.0 # https://github.com/jazzband/django-redis +django-filter==23.5 # https://github.com/carltongibson/django-filter # Django REST Framework djangorestframework==3.14.0 # https://github.com/encode/django-rest-framework django-cors-headers==3.13.0 # https://github.com/adamchainz/django-cors-headers From a59ccae6d504981a283c50a5f33025147d738812 Mon Sep 17 00:00:00 2001 From: Zaki Benaissa Date: Fri, 5 Jan 2024 18:24:58 +0100 Subject: [PATCH 2/3] Make lecturers fiterable --- accounts/filters.py | 31 +++++++++++++++++++++++++++ accounts/views.py | 5 ++++- templates/accounts/lecturer_list.html | 8 +++---- 3 files changed, 38 insertions(+), 6 deletions(-) create mode 100644 accounts/filters.py diff --git a/accounts/filters.py b/accounts/filters.py new file mode 100644 index 0000000..79dbfe0 --- /dev/null +++ b/accounts/filters.py @@ -0,0 +1,31 @@ +from django.db.models import Q +import django_filters +from .models import User + + +class LecturerFilter(django_filters.FilterSet): + username = django_filters.CharFilter(lookup_expr="exact", label="") + name = django_filters.CharFilter(method="filter_by_name", label="") + email = django_filters.CharFilter(lookup_expr="icontains", label="") + + class Meta: + model = User + fields = ["username", "email"] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # Change html classes and placeholders + self.filters["username"].field.widget.attrs.update({"class": "au-input"}) + self.filters["username"].field.widget.attrs.update({"placeholder": "ID No."}) + + self.filters["name"].field.widget.attrs.update({"class": "au-input"}) + self.filters["name"].field.widget.attrs.update({"placeholder": "Name"}) + + self.filters["email"].field.widget.attrs.update({"class": "au-input"}) + self.filters["email"].field.widget.attrs.update({"placeholder": "Email"}) + + def filter_by_name(self, queryset, name, value): + return queryset.filter( + Q(first_name__icontains=value) | Q(last_name__icontains=value) + ) diff --git a/accounts/views.py b/accounts/views.py index 3c4aa18..9574ef1 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -7,12 +7,14 @@ from django.views.generic import CreateView, ListView from django.db.models import Q from django.utils.decorators import method_decorator from django.contrib.auth.forms import PasswordChangeForm +from django_filters.views import FilterView from core.models import Session, Semester from course.models import Course from result.models import TakenCourse from .decorators import admin_required from .forms import StaffAddForm, StudentAddForm, ProfileUpdateForm, ParentAddForm from .models import User, Student, Parent +from .filters import LecturerFilter def validate_username(request): @@ -259,7 +261,8 @@ def edit_staff(request, pk): @method_decorator([login_required, admin_required], name="dispatch") -class LecturerListView(ListView): +class LecturerListView(FilterView): + filterset_class = LecturerFilter queryset = User.objects.filter(is_lecturer=True) template_name = "accounts/lecturer_list.html" paginate_by = 10 # if pagination is desired diff --git a/templates/accounts/lecturer_list.html b/templates/accounts/lecturer_list.html index 25be0c2..eb97ddc 100644 --- a/templates/accounts/lecturer_list.html +++ b/templates/accounts/lecturer_list.html @@ -29,11 +29,9 @@
- - - + {{ filter.form }}
@@ -55,7 +53,7 @@ - {% for lecturer in object_list %} + {% for lecturer in filter.qs %} {{ forloop.counter }}. {{ lecturer.username }} From 90942980fc12a743e3181fbbd6a0a016ae5b2365 Mon Sep 17 00:00:00 2001 From: Zaki Benaissa Date: Fri, 5 Jan 2024 18:49:15 +0100 Subject: [PATCH 3/3] Make students filterable --- accounts/filters.py | 58 +++++++++++++++++++++++----- accounts/urls.py | 4 +- accounts/views.py | 16 +++----- templates/accounts/student_list.html | 8 ++-- 4 files changed, 59 insertions(+), 27 deletions(-) diff --git a/accounts/filters.py b/accounts/filters.py index 79dbfe0..6c54d34 100644 --- a/accounts/filters.py +++ b/accounts/filters.py @@ -1,6 +1,6 @@ from django.db.models import Q import django_filters -from .models import User +from .models import User, Student class LecturerFilter(django_filters.FilterSet): @@ -16,16 +16,56 @@ class LecturerFilter(django_filters.FilterSet): super().__init__(*args, **kwargs) # Change html classes and placeholders - self.filters["username"].field.widget.attrs.update({"class": "au-input"}) - self.filters["username"].field.widget.attrs.update({"placeholder": "ID No."}) - - self.filters["name"].field.widget.attrs.update({"class": "au-input"}) - self.filters["name"].field.widget.attrs.update({"placeholder": "Name"}) - - self.filters["email"].field.widget.attrs.update({"class": "au-input"}) - self.filters["email"].field.widget.attrs.update({"placeholder": "Email"}) + self.filters["username"].field.widget.attrs.update( + {"class": "au-input", "placeholder": "ID No."} + ) + self.filters["name"].field.widget.attrs.update( + {"class": "au-input", "placeholder": "Name"} + ) + self.filters["email"].field.widget.attrs.update( + {"class": "au-input", "placeholder": "Email"} + ) def filter_by_name(self, queryset, name, value): return queryset.filter( Q(first_name__icontains=value) | Q(last_name__icontains=value) ) + + +class StudentFilter(django_filters.FilterSet): + student__username = django_filters.CharFilter(lookup_expr="exact", label="") + student__name = django_filters.CharFilter(method="filter_by_name", label="") + student__email = django_filters.CharFilter(lookup_expr="icontains", label="") + department__title = django_filters.CharFilter(lookup_expr="icontains", label="") + + class Meta: + model = Student + fields = [ + "student__username", + "student__name", + "student__email", + "department__title", + ] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # Change html classes and placeholders + self.filters["student__username"].field.widget.attrs.update( + {"class": "au-input", "placeholder": "ID No."} + ) + self.filters["student__name"].field.widget.attrs.update( + {"class": "au-input", "placeholder": "Name"} + ) + self.filters["student__email"].field.widget.attrs.update( + {"class": "au-input", "placeholder": "Email"} + ) + self.filters["department__title"].field.widget.attrs.update( + {"class": "au-input", "placeholder": "Department"} + ) + + def filter_by_name(self, queryset, name, value): + return queryset.filter( + Q(student__first_name__icontains=value) + | Q(student__last_name__icontains=value) + ) diff --git a/accounts/urls.py b/accounts/urls.py index a387ae0..edfc697 100644 --- a/accounts/urls.py +++ b/accounts/urls.py @@ -14,7 +14,7 @@ from .views import ( admin_panel, profile_update, change_password, - LecturerListView, + LecturerFilterView, StudentListView, staff_add_view, edit_staff, @@ -37,7 +37,7 @@ urlpatterns = [ path("profile//detail/", profile_single, name="profile_single"), path("setting/", profile_update, name="edit_profile"), path("change_password/", change_password, name="change_password"), - path("lecturers/", LecturerListView.as_view(), name="lecturer_list"), + path("lecturers/", LecturerFilterView.as_view(), name="lecturer_list"), path("lecturer/add/", staff_add_view, name="add_lecturer"), path("staff//edit/", edit_staff, name="staff_edit"), path("lecturers//delete/", delete_staff, name="lecturer_delete"), diff --git a/accounts/views.py b/accounts/views.py index 9574ef1..246c1ca 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -14,7 +14,7 @@ from result.models import TakenCourse from .decorators import admin_required from .forms import StaffAddForm, StudentAddForm, ProfileUpdateForm, ParentAddForm from .models import User, Student, Parent -from .filters import LecturerFilter +from .filters import LecturerFilter, StudentFilter def validate_username(request): @@ -261,7 +261,7 @@ def edit_staff(request, pk): @method_decorator([login_required, admin_required], name="dispatch") -class LecturerListView(FilterView): +class LecturerFilterView(FilterView): filterset_class = LecturerFilter queryset = User.objects.filter(is_lecturer=True) template_name = "accounts/lecturer_list.html" @@ -351,16 +351,10 @@ def edit_student(request, pk): @method_decorator([login_required, admin_required], name="dispatch") -class StudentListView(ListView): +class StudentListView(FilterView): + filterset_class = StudentFilter template_name = "accounts/student_list.html" - paginate_by = 10 # if pagination is desired - - def get_queryset(self): - queryset = Student.objects.all() - query = self.request.GET.get("student_id") - if query is not None: - queryset = queryset.filter(Q(department=query)) - return queryset + paginate_by = 10 def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) diff --git a/templates/accounts/student_list.html b/templates/accounts/student_list.html index 3417f2c..6f18517 100644 --- a/templates/accounts/student_list.html +++ b/templates/accounts/student_list.html @@ -29,11 +29,9 @@
- - - + {{ filter.form }}
@@ -63,7 +61,7 @@ - {% for student in object_list %} + {% for student in filter.qs %} {{ forloop.counter }}. {{ student.student.username }}