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

Change History (1)

comment:1 by Tim Graham, 8 years ago

Resolution: wontfix
Status: newclosed

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.

Note: See TracTickets for help on using tickets.
Back to Top