Django admin default filter

How to change the default filter selection from "ALL"? I have a field called status that has three values: activate , pending and rejected . When I use list_filter in the Django admin, the default is set to "All", but I want it to be delayed by default.

+79
python django-admin
May 12 '09 at 7:52 a.m.
source share
13 answers
 class MyModelAdmin(admin.ModelAdmin): def changelist_view(self, request, extra_context=None): if not request.GET.has_key('decommissioned__exact'): q = request.GET.copy() q['decommissioned__exact'] = 'N' request.GET = q request.META['QUERY_STRING'] = request.GET.urlencode() return super(MyModelAdmin,self).changelist_view(request, extra_context=extra_context) 
+43
May 21 '09 at 6:22
source share

In order to achieve this and have a useful โ€œEverythingโ€ link in the sidebar (that is, which displays everything, but does not appear to be pending), you need to create your own list filter that inherits from django.contrib.admin.filters.SimpleListFilter and filters on " expects "by default. Something in this direction should work:

 from datetime import date from django.utils.translation import ugettext_lazy as _ from django.contrib.admin import SimpleListFilter class StatusFilter(SimpleListFilter): title = _('Status') parameter_name = 'status' def lookups(self, request, model_admin): return ( (None, _('Pending')), ('activate', _('Activate')), ('rejected', _('Rejected')), ('all', _('All')), ) def choices(self, cl): for lookup, title in self.lookup_choices: yield { 'selected': self.value() == lookup, 'query_string': cl.get_query_string({ self.parameter_name: lookup, }, []), 'display': title, } def queryset(self, request, queryset): if self.value() in ('activate', 'rejected'): return queryset.filter(status=self.value()) elif self.value() == None: return queryset.filter(status='pending') class Admin(admin.ModelAdmin): list_filter = [StatusFilter] 

EDIT: Django 1.4 required (thanks to Simon)

+83
May 15 '13 at 4:17
source share

Taking the answer of ha22109 above and changing it to allow the selection of "All" by comparing HTTP_REFERER and PATH_INFO .

 class MyModelAdmin(admin.ModelAdmin): def changelist_view(self, request, extra_context=None): test = request.META['HTTP_REFERER'].split(request.META['PATH_INFO']) if test[-1] and not test[-1].startswith('?'): if not request.GET.has_key('decommissioned__exact'): q = request.GET.copy() q['decommissioned__exact'] = 'N' request.GET = q request.META['QUERY_STRING'] = request.GET.urlencode() return super(MyModelAdmin,self).changelist_view(request, extra_context=extra_context) 
+17
Sep 24 '10 at 2:54
source share

I know this question is pretty old right now, but it is still valid. I believe that this is the most correct way to do this. It is essentially the same as Gregโ€™s method, but is formulated as an extensible class for easy reuse.

 from django.contrib.admin import SimpleListFilter from django.utils.encoding import force_text from django.utils.translation import ugettext as _ class DefaultListFilter(SimpleListFilter): all_value = '_all' def default_value(self): raise NotImplementedError() def queryset(self, request, queryset): if self.parameter_name in request.GET and request.GET[self.parameter_name] == self.all_value: return queryset if self.parameter_name in request.GET: return queryset.filter(**{self.parameter_name:request.GET[self.parameter_name]}) return queryset.filter(**{self.parameter_name:self.default_value()}) def choices(self, cl): yield { 'selected': self.value() == self.all_value, 'query_string': cl.get_query_string({self.parameter_name: self.all_value}, []), 'display': _('All'), } for lookup, title in self.lookup_choices: yield { 'selected': self.value() == force_text(lookup) or (self.value() == None and force_text(self.default_value()) == force_text(lookup)), 'query_string': cl.get_query_string({ self.parameter_name: lookup, }, []), 'display': title, } class StatusFilter(DefaultListFilter): title = _('Status ') parameter_name = 'status__exact' def lookups(self, request, model_admin): return ((0,'activate'), (1,'pending'), (2,'rejected')) def default_value(self): return 1 class MyModelAdmin(admin.ModelAdmin): list_filter = (StatusFilter,) 
+14
Jan 09 '14 at 4:38 on
source share

Here is my general solution using redirection, it just checks if there are any GET parameters, if they do not exist, then it is redirected with the default get parameter. I also have a list_filter set, so it selects this and displays the default value.

 from django.shortcuts import redirect class MyModelAdmin(admin.ModelAdmin): ... list_filter = ('status', ) def changelist_view(self, request, extra_context=None): referrer = request.META.get('HTTP_REFERER', '') get_param = "status__exact=5" if len(request.GET) == 0 and '?' not in referrer: return redirect("{url}?{get_parms}".format(url=request.path, get_parms=get_param)) return super(MyModelAdmin,self).changelist_view(request, extra_context=extra_context) 

The only caveat is when you do direct access to the page using ?? is present in the url, there is no HTTP_REFERER, so it will use the default parameter and redirect. This is great for me, it works great when you click through the admin filter.

UPDATE

To get around the caveat, I ended up writing a custom filter function that simplified the changelist_view function. Here is the filter:

 class MyModelStatusFilter(admin.SimpleListFilter): title = _('Status') parameter_name = 'status' def lookups(self, request, model_admin): # Available Values / Status Codes etc.. return ( (8, _('All')), (0, _('Incomplete')), (5, _('Pending')), (6, _('Selected')), (7, _('Accepted')), ) def choices(self, cl): # Overwrite this method to prevent the default "All" from django.utils.encoding import force_text for lookup, title in self.lookup_choices: yield { 'selected': self.value() == force_text(lookup), 'query_string': cl.get_query_string({ self.parameter_name: lookup, }, []), 'display': title, } def queryset(self, request, queryset): # Run the queryset based on your lookup values if self.value() is None: return queryset.filter(status=5) elif int(self.value()) == 0: return queryset.filter(status__lte=4) elif int(self.value()) == 8: return queryset.all() elif int(self.value()) >= 5: return queryset.filter(status=self.value()) return queryset.filter(status=5) 

And now changelist_view only passes the default parameter if it is missing. The idea was to get rid of the ability of generics filters to view all without using get parameters. To view everything, for this purpose I assigned status = 8 .:

 class MyModelAdmin(admin.ModelAdmin): ... list_filter = ('status', ) def changelist_view(self, request, extra_context=None): if len(request.GET) == 0: get_param = "status=5" return redirect("{url}?{get_parms}".format(url=request.path, get_parms=get_param)) return super(MyModelAdmin, self).changelist_view(request, extra_context=extra_context) 
+6
Sep 11 '14 at 15:23
source share
 def changelist_view( self, request, extra_context = None ): default_filter = False try: ref = request.META['HTTP_REFERER'] pinfo = request.META['PATH_INFO'] qstr = ref.split( pinfo ) if len( qstr ) < 2: default_filter = True except: default_filter = True if default_filter: q = request.GET.copy() q['registered__exact'] = '1' request.GET = q request.META['QUERY_STRING'] = request.GET.urlencode() return super( InterestAdmin, self ).changelist_view( request, extra_context = extra_context ) 
+4
Feb 17 '12 at 16:22
source share

You can simply use return queryset.filter() or if self.value() is None and override the SimpleListFilter method

 from django.utils.encoding import force_text def choices(self, changelist): for lookup, title in self.lookup_choices: yield { 'selected': force_text(self.value()) == force_text(lookup), 'query_string': changelist.get_query_string( {self.parameter_name: lookup}, [] ), 'display': title, } 
+3
Jan 25 '17 at 10:23
source share

Note that if, instead of pre-selecting a filter value, you should always pre-filter the data before displaying it in the administrator, instead you must override the ModelAdmin.queryset() method.

+2
May 28 '09 at 11:09
source share

I know this is not the best solution, but I changed index.html in the admin template, lines 25 and 37, like this:

25: <th scope="row"><a href="{{ model.admin_url }}{% ifequal model.name "yourmodelname" %}?yourflag_flag__exact=1{% endifequal %}">{{ model.name }}</a></th>

37: <td><a href="{{ model.admin_url }}{% ifequal model.name "yourmodelname" %}?yourflag__exact=1{% endifequal %}" class="changelink">{% trans 'Change' %}</a></td>

+1
Oct 13 '09 at 15:20
source share

I had to make changes so that the filter worked correctly. The previous solution worked for me when loading the page. If an โ€œactionโ€ was performed, the filter returned to โ€œAll,โ€ not the default. This solution loads the admin change page using the default filter, but also supports filter changes or the current filter when another activity occurs on the page. I have not tested all cases, but in fact this can limit the installation of the default filter only when the page loads.

 def changelist_view(self, request, extra_context=None): default_filter = False try: ref = request.META['HTTP_REFERER'] pinfo = request.META['PATH_INFO'] qstr = ref.split(pinfo) querystr = request.META['QUERY_STRING'] # Check the QUERY_STRING value, otherwise when # trying to filter the filter gets reset below if querystr is None: if len(qstr) < 2 or qstr[1] == '': default_filter = True except: default_filter = True if default_filter: q = request.GET.copy() q['registered__isnull'] = 'True' request.GET = q request.META['QUERY_STRING'] = request.GET.urlencode() return super(MyAdmin, self).changelist_view(request, extra_context=extra_context) 
+1
Mar 13 '13 at 17:36
source share

A slight improvement in Greg's answer using DjangoChoices, Python> = 2.5 and, of course, Django> = 1.4.

 from django.utils.translation import ugettext_lazy as _ from django.contrib.admin import SimpleListFilter class OrderStatusFilter(SimpleListFilter): title = _('Status') parameter_name = 'status__exact' default_status = OrderStatuses.closed def lookups(self, request, model_admin): return (('all', _('All')),) + OrderStatuses.choices def choices(self, cl): for lookup, title in self.lookup_choices: yield { 'selected': self.value() == lookup if self.value() else lookup == self.default_status, 'query_string': cl.get_query_string({self.parameter_name: lookup}, []), 'display': title, } def queryset(self, request, queryset): if self.value() in OrderStatuses.values: return queryset.filter(status=self.value()) elif self.value() is None: return queryset.filter(status=self.default_status) class Admin(admin.ModelAdmin): list_filter = [OrderStatusFilter] 

Thanks to Greg for the pleasant decision!

+1
Sep 20 '13 at 8:22
source share

A little off topic, but my search for a similar question brought me here. I searched for the default query by date (i.e. if no input is specified, display only objects with timestamp from "Today"), which complicates the issue a bit. Here is what I came up with:

 from django.contrib.admin.options import IncorrectLookupParameters from django.core.exceptions import ValidationError class TodayDefaultDateFieldListFilter(admin.DateFieldListFilter): """ If no date is query params are provided, query for Today """ def queryset(self, request, queryset): try: if not self.used_parameters: now = datetime.datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) self.used_parameters = { ('%s__lt' % self.field_path): str(now + datetime.timedelta(days=1)), ('%s__gte' % self.field_path): str(now), } # Insure that the dropdown reflects 'Today' self.date_params = self.used_parameters return queryset.filter(**self.used_parameters) except ValidationError, e: raise IncorrectLookupParameters(e) class ImagesAdmin(admin.ModelAdmin): list_filter = ( ('timestamp', TodayDefaultDateFieldListFilter), ) 

This is a simple override of the default DateFieldListFilter . By setting self.date_params , it ensures that the filter drop-down list is updated to any option matching self.used_parameters . For this reason, you must make sure that self.used_parameters is exactly what one of these drop-down lists will use (that is, find out what date_params will be when using Today or Last 7 Days and build self.used_parameters to match these).

This was created to work with Django 1.4.10

0
Jun 18 '14 at 19:55
source share

It may be an old thread, but I thought I would add my solution, because I did not find more suitable answers to Google search queries.

Do what (not sure if his Demin Roger, or ha22109) answered in ModelAdmin for changelist_view

 class MyModelAdmin(admin.ModelAdmin): list_filter = (CustomFilter,) def changelist_view(self, request, extra_context=None): if not request.GET.has_key('decommissioned__exact'): q = request.GET.copy() q['decommissioned__exact'] = 'N' request.GET = q request.META['QUERY_STRING'] = request.GET.urlencode() return super(MyModelAdmin,self).changelist_view(request, extra_context=extra_context) 

Then we need to create a custom SimpleListFilter

 class CustomFilter(admin.SimpleListFilter): title = 'Decommissioned' parameter_name = 'decommissioned' # i chose to change it def lookups(self, request, model_admin): return ( ('All', 'all'), ('1', 'Decommissioned'), ('0', 'Active (or whatever)'), ) # had to override so that we could remove the default 'All' option # that won't work with our default filter in the ModelAdmin class def choices(self, cl): yield { 'selected': self.value() is None, 'query_string': cl.get_query_string({}, [self.parameter_name]), # 'display': _('All'), } for lookup, title in self.lookup_choices: yield { 'selected': self.value() == lookup, 'query_string': cl.get_query_string({ self.parameter_name: lookup, }, []), 'display': title, } def queryset(self, request, queryset): if self.value() == '1': return queryset.filter(decommissioned=1) elif self.value() == '0': return queryset.filter(decommissioned=0) return queryset 
0
Nov 06 '14 at 15:38
source share



All Articles