How can I change the default filter choice from \'ALL\'? I have a field named as status
which has three values: activate
, pending
and
A slight improvement on 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 nice solution!
A bit off-topic but my search for a similar question led me here. I was looking to have a default query by a date (ie if no input is provided, show only objects with timestamp
of 'Today'), which complicates the question 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 insures that the filter dropdown will update to whatever option matches the self.used_parameters
. For this reason, you must insure that the self.used_parameters
are exactly what would be used by one of those dropdown selections (ie, find out what the date_params
would be when using the 'Today' or 'Last 7 Days' and construct the self.used_parameters
to match those).
This was built to work with Django 1.4.10
I had to make a modification to get filtering to work correctly. The previous solution worked for me when the page loaded. If an 'action' was performed, the filter went back to 'All' and not my default. This solution loads the admin change page with the default filter, but also maintains filter changes or the current filter when other activity occurs on the page. I haven't tested all cases, but in reality it may be limiting the setting of a default filter to occur 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)
This may be an old thread, but thought I would add my solution as I couldn't find better answers on google searches.
Do what (not sure if its Deminic Rodger, or ha22109) answered in the 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
In order to achieve this and have a usable 'All' link in your sidebar (ie one that shows all rather than showing pending), you'd need to create a custom list filter, inheriting from django.contrib.admin.filters.SimpleListFilter
and filtering on 'pending' by default. Something along these lines 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: Requires Django 1.4 (thanks Simon)
I know that is not the best solution, but i changed the index.html in the admin template, line 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>