#11: champ de recherche dans la partie publique du site #30
32
aircox/filters.py
Normal file
32
aircox/filters.py
Normal 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)
|
||||
|
|
@ -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 """
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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 %}
|
||||
|
||||
|
|
|
@ -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 """
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user