Move filtering logic from views.generic.models.IndexView to views.generic.base.BaseListingView

BaseListingView now supports passing a django-filters filterset class as `self.filterset_class`, provides properties `filters` and `is_filtering`, and provides a `filter_queryset` method. If a filterset class is specified, this will be used to filter the queryset within `get_queryset`; `filters` and `is_filtering` will be added to the template context; and the `media` context variable will include the filter form's media.
pull/11434/head
Matt Westcott 2024-01-05 00:34:44 +00:00
rodzic 7af866f1c7
commit 130c7ff3c5
2 zmienionych plików z 34 dodań i 27 usunięć

Wyświetl plik

@ -2,6 +2,7 @@ from django.contrib.admin.utils import quote, unquote
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse, reverse_lazy from django.urls import reverse, reverse_lazy
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.views import View from django.views import View
from django.views.generic.base import ContextMixin, TemplateResponseMixin from django.views.generic.base import ContextMixin, TemplateResponseMixin
@ -170,6 +171,7 @@ class BaseListingView(WagtailAdminTemplateMixin, BaseListView):
index_url_name = None index_url_name = None
page_kwarg = "p" page_kwarg = "p"
default_ordering = None default_ordering = None
filterset_class = None
def get_template_names(self): def get_template_names(self):
if self.results_only: if self.results_only:
@ -179,6 +181,23 @@ class BaseListingView(WagtailAdminTemplateMixin, BaseListView):
else: else:
return super().get_template_names() return super().get_template_names()
@cached_property
def filters(self):
if self.filterset_class:
return self.filterset_class(self.request.GET, request=self.request)
@cached_property
def is_filtering(self):
# we are filtering if the filter form has changed from its default state
return (
self.filters and self.filters.is_valid() and self.filters.form.has_changed()
)
def filter_queryset(self, queryset):
if self.filters and self.filters.is_valid():
queryset = self.filters.filter_queryset(queryset)
return queryset
def get_valid_orderings(self): def get_valid_orderings(self):
orderings = [] orderings = []
for col in self.columns: for col in self.columns:
@ -193,6 +212,11 @@ class BaseListingView(WagtailAdminTemplateMixin, BaseListView):
ordering = self.default_ordering ordering = self.default_ordering
return ordering return ordering
def get_queryset(self):
queryset = super().get_queryset()
queryset = self.filter_queryset(queryset)
return queryset
def get_table_kwargs(self): def get_table_kwargs(self):
return { return {
"ordering": self.get_ordering(), "ordering": self.get_ordering(),
@ -231,4 +255,9 @@ class BaseListingView(WagtailAdminTemplateMixin, BaseListView):
else: else:
context["items_count"] = len(context["object_list"]) context["items_count"] = len(context["object_list"])
if self.filters:
context["filters"] = self.filters
context["is_filtering"] = self.is_filtering
context["media"] += self.filters.form.media
return context return context

Wyświetl plik

@ -115,7 +115,6 @@ class IndexView(
search_backend_name = "default" search_backend_name = "default"
is_searchable = None is_searchable = None
search_kwarg = "q" search_kwarg = "q"
filterset_class = None
columns = None # If not explicitly specified, will be derived from list_display columns = None # If not explicitly specified, will be derived from list_display
list_display = ["__str__", UpdatedAtColumn()] list_display = ["__str__", UpdatedAtColumn()]
list_filter = None list_filter = None
@ -123,7 +122,11 @@ class IndexView(
def setup(self, request, *args, **kwargs): def setup(self, request, *args, **kwargs):
super().setup(request, *args, **kwargs) super().setup(request, *args, **kwargs)
self.filterset_class = self.get_filterset_class()
if not self.filterset_class:
# Allow filterset_class to be dynamically constructed from list_filter
self.filterset_class = self.get_filterset_class()
self.setup_search() self.setup_search()
def setup_search(self): def setup_search(self):
@ -159,9 +162,6 @@ class IndexView(
return SearchForm() return SearchForm()
def get_filterset_class(self): def get_filterset_class(self):
if self.filterset_class:
return self.filterset_class
if not self.list_filter or not self.model: if not self.list_filter or not self.model:
return None return None
@ -262,23 +262,6 @@ class IndexView(
return queryset return queryset
@cached_property
def filters(self):
if self.filterset_class:
return self.filterset_class(self.request.GET, request=self.request)
@cached_property
def is_filtering(self):
# we are filtering if the filter form has changed from its default state
return (
self.filters and self.filters.is_valid() and self.filters.form.has_changed()
)
def filter_queryset(self, queryset):
if self.filters and self.filters.is_valid():
queryset = self.filters.filter_queryset(queryset)
return queryset
def search_queryset(self, queryset): def search_queryset(self, queryset):
if not self.search_query: if not self.search_query:
return queryset return queryset
@ -526,11 +509,6 @@ class IndexView(
context["add_url"] = context["header_action_url"] = self.get_add_url() context["add_url"] = context["header_action_url"] = self.get_add_url()
context["header_action_label"] = self.add_item_label context["header_action_label"] = self.add_item_label
if self.filters:
context["filters"] = self.filters
context["is_filtering"] = self.is_filtering
context["media"] += self.filters.form.media
context["index_results_url"] = self.get_index_results_url() context["index_results_url"] = self.get_index_results_url()
context["is_searchable"] = self.is_searchable context["is_searchable"] = self.is_searchable
context["search_url"] = self.get_search_url() context["search_url"] = self.get_search_url()