Romeo Mihalcea
Romeo Mihalcea

Reputation: 10252

Django show list filter only if condition matches

I want to show certain list filters for Django admin only if a certain condition matches. For example I have 3 filters now: country, state, city. All 3 of them being shown at the same time produces a real mess and a really long sidebar because it combines a long list of cities, states and countries.

What I want to do is show only the country first, when a country is clicked I want to show states in that country and the same for the city filter. Is this doable by default or I have to create a custom filter myself?

list_filter = (
    ('loc_country_code', custom_titled_filter( 'country' )),
    ('loc_state', custom_titled_filter( 'state' )),
    ('loc_city', custom_titled_filter( 'city' )),
)

Upvotes: 2

Views: 1328

Answers (1)

Derek Kwok
Derek Kwok

Reputation: 13058

You can create a custom SimpleListFilter to generate dynamic filters on your admin. In a SimpleListFilter, the filter is disabled (hidden from view also) if lookups method returns an empty tuple/list. This can be used to control when certain filters appear.

Here's a basic filter:

class CountryFilter(admin.SimpleListFilter):

    title = 'Country'
    parameter_name = 'country'

    def lookups(self, request, model_admin):
        """ Return a list of (country_id, country_name) tuples """
        countries = Country.objects.all()
        return [(c.id, c.name) for c in countries]

    def queryset(self, request, queryset):
        ...

Below is a filter where options are restricted based on the filter above:

 class StateFilter(admin.SimpleListFilter):

     title = 'State'
     parameter_name = 'state'

     def lookups(self, request, model_admin):
         """ 
         Return a list of (state_id, state_name) tuples based on 
         country selected 
         """

         # retrieve the current country the user has selected
         country_id = request.GET.get('country')
         if country_id is None:
             # state filter will be hidden
             return []

         # only return states which belong in the country
         states = State.objects.filter(country_id=country_id)
         return [(s.id, s.name) for s in states]

     def queryset(self, request, queryset):
         ...

The general idea is to use lookups on your filter classes to restrict the options on subsequent filters. These filters can be applied to the admin via the list_filter parameter.

class MyAdmin(admin.ModelAdmin):

     list_filter = [CountryFilter, StateFilter, CityFilter, ...]

Upvotes: 3

Related Questions