django admin filters cascading
I've got a question - can Django Admin interface be customized in such a way, that it presents only those filter options that occur in the subset of data matching currently selected filters?
Say I have a db of three objects:
a.Foo = "One"
a.Bar = "Drink"
b.Foo = "One"
b.Bar = "Shot"
c.Foo = "Two"
c.Bar = "Shot"
And a django admin interface with filters on 'Foo' and 'Bar'. I want the following behavior:
- If no filters are chosen, 'Foo' lists "One","Two"; 'Bar' lists "Drink", "Shot"
- If 'Foo' filter is set to "One", 'Bar' lists both "Drink" and "Shot"
- If 'Foo' filter is set to "Two", 'Bar' lists only "Shot"
- If 'Bar' filter is set to "Shot", 'Foo' lists both "One" and "Two"
- If 'Bar' filter is set to "D开发者_开发问答rink", 'Foo' lists only "One"
Cheers!
To be more specific - after reading some docs:
from django.contrib.admin import SimpleListFilter
class SomeFilter(SimpleListFilter):
title = "Foo"
parameter_name="Some"
def lookups(self, request, model_admin):
qs = model_admin.queryset(request)
print qs.query
return (('Foo', 'Bar'))
def queryset(self, request, queryset):
if (self.value()):
return queryset.filter(Some=self.value())
else:
return queryset
What it does, however, is gets the 'queryset' as it would've been without other filters. How can I pass it through other filters?
I could theoretically parse the request and filter manually - but there surely needs to be a way to pipe all filters.
This kind of dynamic filtering really looks like faceting. While you may be able to achieve this result with a standard queryset, this will probably not be optimal. You may have more chance using a dedicated tool, like Solr.
Haystack has also a doc page on faceting.
in case anyone needs a lightweight solution, this one does a Country filter that is hidden when no Continent is selected in the filter and only offers those countries that exist in the selected Continent:
from django.utils.translation import ugettext_lazy as _
class ContinentCountryListFilter(admin.SimpleListFilter):
# Human-readable title which will be displayed in the
# right admin sidebar just above the filter options.
title = _('country')
# Parameter for the filter that will be used in the URL query.
parameter_name = 'country__iso_code__exact'
def lookups(self, request, model_admin):
"""
Returns a list of tuples. The first element in each
tuple is the coded value for the option that will
appear in the URL query. The second element is the
human-readable name for the option that will appear
in the right sidebar.
"""
continent = request.GET.get('country__continent__iso_code__exact', '')
if continent:
countries = models.Country.objects.filter(continent__iso_code__exact=continent)
else:
countries = models.Country.objects.none()
return countries.values_list('iso_code', 'name')
def queryset(self, request, queryset):
"""
Returns the filtered queryset based on the value
provided in the query string and retrievable via
`self.value()`.
"""
continent = request.GET.get('country__continent__iso_code__exact', '')
# only apply filter if continent is set and country exists in continent
if continent and models.Country.objects.filter(continent__iso_code__exact=continent, iso_code__exact=self.value()).count():
return queryset.filter(country__iso_code__exact=self.value())
return queryset
and then apply with:
list_filter = ('country__continent', ContinentCountryListFilter, )
精彩评论