Opened 8 years ago
Closed 8 years ago
#27116 closed New feature (wontfix)
Deferrable Admin Filters
Reported by: | Austin Pua | Owned by: | nobody |
---|---|---|---|
Component: | contrib.admin | Version: | dev |
Severity: | Normal | Keywords: | admin SimpleListFilter |
Cc: | Triage Stage: | Unreviewed | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
Hi Guys!
I implemented a custom SimpleListFilter
for one of my projects, but due to the nature of what it does, it is a very expensive process. It involves, from a base queryset, splitting it into two copies and then performing additional filtering (different for each split), prefetching different objects per split, performing validation vs those prefetch objects, and then return pk
values of all positive matches as a brand new queryset using __in
lookup.
I read the django admin code, and due to the lazy evaluation of querysets, my custom SimpleListFilter
ends up having to go through all entries of the model, even though there are other filters in place. Maybe my own code can use some optimizations, but is there any plausible use case for deferrable admin filters?
Basically, it should perform all other admin filtering first, so that the base queryset will be as small as possible which will limit the entries and also limit the number of Prefetch()
objects. I was able to hack up some code (shown below) that does this, but is it sound to implement it out-of-the-box?
from django.contrib import admin from django.contrib.admin.views.main import ChangeList class MyTestFilter(admin.SimpleListFilter): # ... defer = True # ... class MyTestChangeList(ChangeList): # ... def get_filters(self, request): filter_specs, status, lookup_params, use_distinct = super(MyTestChangeList, self).get_filters(request) revised_filter_specs = [] deferred_filter_specs = [] for filter_spec in filter_specs: if hasattr(filter_spec, 'defer') and filter_spec.defer: deferred_filter_specs.append(filter_spec) else: revised_filter_specs.append(filter_spec) self.deferred_filter_specs = deferred_filter_specs return revised_filter_specs, status, lookup_params, use_distinct def get_queryset(self, request): qs = super(MyTestChangeList, self).get_queryset(request) for filter_spec in self.deferred_filter_specs: new_qs = filter_spec.queryset(request, qs) if new_qs is not None: qs = new_qs return qs
At first glance, it seems to add a non-trivial amount of complexity for what likely isn't a common use case. Feel free to raise the idea on the DevelopersMailingList to get more feedback and see if anyone else has a similar need.