Merge pull request '#11: champ de recherche dans la partie publique du site' (#30) from fix-1.0-11 into develop-1.0

Reviewed-on: #30
This commit is contained in:
Thomas Kairos 2022-03-20 12:30:52 +01:00
commit f65bfb1564
8 changed files with 152 additions and 84 deletions

32
aircox/filters.py Normal file
View File

@ -0,0 +1,32 @@
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):
podcast = filters.BooleanFilter(method='podcast_filter', label=_('Podcast'))
class Meta:
model = Episode
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)

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

@ -59,6 +59,19 @@ Usefull context:
<div class="navbar-end">
{% block top-nav-tools %}
{% endblock %}
{% 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 %}
</div>
</div>
</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 and object_list %}
<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

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

View File

@ -80,3 +80,30 @@ 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)