use django filters + search filter; still need adapts sub-page list views

This commit is contained in:
bkfox 2022-02-22 15:29:57 +01:00
parent e9e09104ad
commit 849a14014c
7 changed files with 145 additions and 73 deletions

26
aircox/filters.py Normal file
View File

@ -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 = {
}

View File

@ -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 """

View File

@ -57,6 +57,19 @@ Usefull context:
{% endblock %}
</div>
<div class="navbar-end">
{% block top-nav-end %}
<div class="navbar-item">
<form action="{% url 'page-list' %}" method="GET">
<div class="control has-icons-left">
<span class="icon is-small is-left">
<i class="fa fa-search"></i>
</span>
<input type="text" name="q" class="input"
placeholder="{% trans "Search" %}" />
</div>
</form>
</div>
{% endblock %}
{% block top-nav-tools %}
{% endblock %}
</div>

View File

@ -24,48 +24,7 @@
{% block main %}{{ block.super }}
{% block before_list %}
{% if filters %}
<form method="GET" action="" class="media">
<div class="media-content">
{% block filters %}
{% for label, name, choices in filters %}
<div class="field is-horizontal">
<div class="field-label">
<label class="label">{{ label }}</label>
</div>
<div class="field-body">
<div class="field is-narrow">
<div class="control">
{% for label, value, checked in choices %}
<label class="checkbox">
<input type="checkbox" class="checkbox" name="{{ name }}"
value="{{ value }}"
{% if checked %}checked{% endif %} />
{{ label }}
</label>
{% endfor %}
</div>
</div>
</div>
</div>
{% endfor %}
{% endblock %}
</div>
<div class="media-right">
<div class="field is-grouped is-grouped-right">
<div class="control">
<button class="button is-primary"/>{% trans "Apply" %}</button>
</div>
<div class="control">
<a href="?" class="button is-secondary">{% trans "Reset" %}</a>
</div>
</div>
</div>
</form>
{% endif %}
{% endblock %}
{% block before_list %}{% endblock %}
<section role="list">
{% block pages_list %}

View File

@ -2,3 +2,62 @@
{% comment %}Display a list of Pages{% endcomment %}
{% load i18n aircox %}
{% block before_list %}
{{ block.super }}
{% if view.has_filters %}
<form method="GET" action="" class="media">
<div class="media-content">
{% block filters %}
<div class="field is-horizontal">
<div class="field-label">
<label class="label">{% trans "Search" %}</label>
</div>
<div class="field-body">
<div class="field">
<div class="control has-icons-left">
<span class="icon is-small is-left">
<i class="fa fa-search"></i>
</span>
<input class="input" type="text" name="q"
value="{{ filterset_data.q }}"
placeholder="{% trans "Search content" %}">
</div>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label">
<label class="label">{% trans "Categories" %}</label>
</div>
<div class="field-body">
<div class="field is-narrow">
<div class="control">
{% for label, value in categories %}
<label class="checkbox">
<input type="checkbox" class="checkbox" name="category__id__in"
value="{{ value }}"
{% if value in filterset_data.category__id__in %}checked{% endif %} />
{{ label }}
</label>
{% endfor %}
</div>
</div>
</div>
</div>
{% endblock %}
</div>
<div class="media-right">
<div class="field is-grouped is-grouped-right">
<div class="control">
<button class="button is-primary"/>{% trans "Apply" %}</button>
</div>
<div class="control">
<a href="?" class="button is-secondary">{% trans "Reset" %}</a>
</div>
</div>
</div>
</form>
{% endif %}
{% endblock %}

View File

@ -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)

View File

@ -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)