开发者

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, )
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜