From 849a14014c82e68aa8045773a17dcc0483f5bec1 Mon Sep 17 00:00:00 2001 From: bkfox Date: Tue, 22 Feb 2022 15:29:57 +0100 Subject: [PATCH 1/2] use django filters + search filter; still need adapts sub-page list views --- aircox/filters.py | 26 ++++++++++ aircox/models/page.py | 5 ++ aircox/templates/aircox/base.html | 13 +++++ aircox/templates/aircox/basepage_list.html | 43 +--------------- aircox/templates/aircox/page_list.html | 59 ++++++++++++++++++++++ aircox/views/mixins.py | 28 ++++++++++ aircox/views/page.py | 44 +++++----------- 7 files changed, 145 insertions(+), 73 deletions(-) create mode 100644 aircox/filters.py diff --git a/aircox/filters.py b/aircox/filters.py new file mode 100644 index 0000000..39fa212 --- /dev/null +++ b/aircox/filters.py @@ -0,0 +1,26 @@ +from django.utils.translation import gettext_lazy as _ +import django_filters as filters + +from .models import Page, Episode + + +class PageFilters(filters.FilterSet): + q = filters.CharFilter(method='search_filter', label=_('Search')) + + class Meta: + model = Page + fields = { + 'category__id': ['in'], + 'pub_date': ['exact', 'gte', 'lte'], + } + + def search_filter(self, queryset, name, value): + return queryset.search(value) + + +class EpisodeFilters(PageFilters): + class Meta: + model = Episode + fields = { + } + diff --git a/aircox/models/page.py b/aircox/models/page.py index 19b4a82..b1c5123 100644 --- a/aircox/models/page.py +++ b/aircox/models/page.py @@ -53,6 +53,11 @@ class PageQuerySet(InheritanceQuerySet): return self.filter(parent=parent) if id is None else \ self.filter(parent__id=id) + def search(self, q, search_content=True): + if search_content: + return self.filter(models.Q(title__icontains=q) | models.Q(content__icontains=q)) + return self.filter(title__icontains=q) + class BasePage(models.Model): """ Base class for publishable content """ diff --git a/aircox/templates/aircox/base.html b/aircox/templates/aircox/base.html index 0b489f1..955e801 100644 --- a/aircox/templates/aircox/base.html +++ b/aircox/templates/aircox/base.html @@ -57,6 +57,19 @@ Usefull context: {% endblock %} diff --git a/aircox/templates/aircox/basepage_list.html b/aircox/templates/aircox/basepage_list.html index 43656d8..61b09fd 100644 --- a/aircox/templates/aircox/basepage_list.html +++ b/aircox/templates/aircox/basepage_list.html @@ -24,48 +24,7 @@ {% block main %}{{ block.super }} -{% block before_list %} -{% if filters %} -
-
- {% block filters %} - {% for label, name, choices in filters %} -
-
- -
-
-
-
- {% for label, value, checked in choices %} - - {% endfor %} -
-
-
-
- {% endfor %} - {% endblock %} -
-
-
-
- -
- -
-
-
-{% endif %} - -{% endblock %} +{% block before_list %}{% endblock %}
{% block pages_list %} diff --git a/aircox/templates/aircox/page_list.html b/aircox/templates/aircox/page_list.html index 32ae43b..1d1945e 100644 --- a/aircox/templates/aircox/page_list.html +++ b/aircox/templates/aircox/page_list.html @@ -2,3 +2,62 @@ {% comment %}Display a list of Pages{% endcomment %} {% load i18n aircox %} +{% block before_list %} +{{ block.super }} + +{% if view.has_filters %} +
+
+ {% block filters %} +
+
+ +
+
+
+
+ + + + +
+
+
+
+
+
+ +
+
+
+
+ {% for label, value in categories %} + + {% endfor %} +
+
+
+
+ {% endblock %} +
+
+
+
+ +
+ +
+
+
+{% endif %} +{% endblock %} + diff --git a/aircox/views/mixins.py b/aircox/views/mixins.py index 29d11be..c74235e 100644 --- a/aircox/views/mixins.py +++ b/aircox/views/mixins.py @@ -80,3 +80,31 @@ class AttachedToMixin: .published().first() return super().get_page() + +class FiltersMixin: + """ Mixin integrating Django filters' filter set """ + filterset = None + filterset_class = None + + def get_filterset(self, data, query): + return self.filterset_class(data, query) + + def get_queryset(self): + query = super().get_queryset() + if self.filterset_class: + self.filterset = self.get_filterset(self.request.GET.copy(), query) + return self.filterset.qs + return query + + def get_context_data(self, **kwargs): + filterset = kwargs.setdefault('filterset', self.filterset) + if filterset.is_valid(): + kwargs['filterset_data'] = filterset.form.cleaned_data + else: + kwargs['filterset_data'] = {} + + params = self.request.GET.copy() + kwargs['get_params'] = params.pop('page', True) and params + return super().get_context_data(**kwargs) + + diff --git a/aircox/views/page.py b/aircox/views/page.py index edd9215..4699287 100644 --- a/aircox/views/page.py +++ b/aircox/views/page.py @@ -5,11 +5,12 @@ from django.views.generic import DetailView, ListView from honeypot.decorators import check_honeypot +from ..filters import PageFilters from ..forms import CommentForm from ..models import Category, Comment from ..utils import Redirect from .base import BaseView -from .mixins import AttachedToMixin, ParentMixin +from .mixins import AttachedToMixin, FiltersMixin, ParentMixin __all__ = ['BasePageListView', 'BasePageDetailView', 'PageDetailView', 'PageListView'] @@ -71,50 +72,32 @@ class BasePageDetailView(BaseView, DetailView): return self.object -class PageListView(BasePageListView): +class PageListView(FiltersMixin, BasePageListView): """ Page list view. """ + filterset_class = PageFilters template_name = None has_filters = True categories = None filters = None - def get(self, *args, **kwargs): - self.categories = set(self.request.GET.getlist('categories')) - self.filters = set(self.request.GET.getlist('filters')) - return super().get(*args, **kwargs) - def get_template_names(self): return super().get_template_names() + ['aircox/page_list.html'] + def get_filterset(self, data, query): + # FIXME: not the most efficient, cause join then split (in django filters) + data['category__id__in'] = ','.join(data.getlist('category__id__in')) + return super().get_filterset(data, query) + def get_queryset(self): qs = super().get_queryset().select_related('category') \ .order_by('-pub_date') - - # category can be filtered based on request.GET['categories'] - # (by id) - if self.categories: - qs = qs.filter(category__slug__in=self.categories) return qs - def get_filters(self): - categories = self.model.objects.published() \ - .filter(category__isnull=False) \ - .values_list('category', flat=True) - categories = [ (c.title, c.slug, c.slug in self.categories) - for c in Category.objects.filter(id__in=categories) ] - return ( - (_('Categories'), 'categories', categories), - ) - def get_context_data(self, **kwargs): - if not 'filters' in kwargs: - filters = self.get_filters() - for label, fieldName, choices in filters: - if choices: - kwargs['filters'] = filters - break; - else: - kwargs['filters'] = tuple() + kwargs['categories'] = self.model.objects.published() \ + .filter(category__isnull=False) \ + .values_list('category__title', 'category__id') \ + .distinct() return super().get_context_data(**kwargs) @@ -151,6 +134,5 @@ class PageDetailView(BasePageDetailView): comment = form.save(commit=False) comment.page = self.object comment.save() - return self.get(request, *args, **kwargs) From cd360d9ac727d4a090719c24dd2799e89acd380b Mon Sep 17 00:00:00 2001 From: bkfox Date: Tue, 22 Feb 2022 18:48:17 +0100 Subject: [PATCH 2/2] add episode filters --- aircox/filters.py | 10 ++++++++-- aircox/templates/aircox/base.html | 4 ++-- aircox/templates/aircox/page_list.html | 2 +- aircox/views/episode.py | 13 ++----------- aircox/views/mixins.py | 1 - 5 files changed, 13 insertions(+), 17 deletions(-) diff --git a/aircox/filters.py b/aircox/filters.py index 39fa212..7b66b12 100644 --- a/aircox/filters.py +++ b/aircox/filters.py @@ -19,8 +19,14 @@ class PageFilters(filters.FilterSet): class EpisodeFilters(PageFilters): + podcast = filters.BooleanFilter(method='podcast_filter', label=_('Podcast')) + class Meta: model = Episode - fields = { - } + fields = PageFilters.Meta.fields.copy() + + def podcast_filter(self, queryset, name, value): + if value: + return queryset.filter(sound__is_public=True).distinct() + return queryset.filter(sound__isnull=True) diff --git a/aircox/templates/aircox/base.html b/aircox/templates/aircox/base.html index 955e801..7412b83 100644 --- a/aircox/templates/aircox/base.html +++ b/aircox/templates/aircox/base.html @@ -57,6 +57,8 @@ Usefull context: {% endblock %} diff --git a/aircox/templates/aircox/page_list.html b/aircox/templates/aircox/page_list.html index 1d1945e..f23de90 100644 --- a/aircox/templates/aircox/page_list.html +++ b/aircox/templates/aircox/page_list.html @@ -5,7 +5,7 @@ {% block before_list %} {{ block.super }} -{% if view.has_filters %} +{% if view.has_filters and object_list %}
{% block filters %} diff --git a/aircox/views/episode.py b/aircox/views/episode.py index e3cbb1b..96ada74 100644 --- a/aircox/views/episode.py +++ b/aircox/views/episode.py @@ -4,6 +4,7 @@ import datetime from django.utils.translation import gettext_lazy as _ from django.views.generic import ListView +from ..filters import EpisodeFilters from ..models import Diffusion, Episode, Program, StaticPage, Sound from .base import BaseView from .program import ProgramPageDetailView @@ -25,22 +26,12 @@ class EpisodeDetailView(ProgramPageDetailView): class EpisodeListView(PageListView): model = Episode + filterset_class = EpisodeFilters item_template_name = 'aircox/widgets/episode_item.html' has_headline = True parent_model = Program attach_to_value = StaticPage.ATTACH_TO_EPISODES - def get_queryset(self): - qs = super().get_queryset() - if self.filters and 'podcasts' in self.filters: - qs = qs.filter(sound__is_public=True) - return qs - - def get_filters(self): - return super().get_filters() + ( - (_('Podcasts'), 'podcasts', tuple()), - ) - class DiffusionListView(GetDateMixin, AttachedToMixin, BaseView, ListView): """ View for timetables """ diff --git a/aircox/views/mixins.py b/aircox/views/mixins.py index c74235e..ad94155 100644 --- a/aircox/views/mixins.py +++ b/aircox/views/mixins.py @@ -107,4 +107,3 @@ class FiltersMixin: kwargs['get_params'] = params.pop('page', True) and params return super().get_context_data(**kwargs) -