I know I already managed to do this but can't remember how nor I can't find any documentation about this..
How can apply a filter by default on a object list view in the admin ?
I have an app开发者_如何学Go which list quotes and those quotes have a status (ex: accepted, rejected, on hold ..).
I want the filter set on status='accepted' by default that is..
A bit more reusable approach:
class DefaultFilterMixIn(admin.ModelAdmin):
def changelist_view(self, request, *args, **kwargs):
from django.http import HttpResponseRedirect
if self.default_filters:
try:
test = request.META['HTTP_REFERER'].split(request.META['PATH_INFO'])
if test and test[-1] and not test[-1].startswith('?'):
url = reverse('admin:%s_%s_changelist' % (self.opts.app_label, self.opts.module_name))
filters = []
for filter in self.default_filters:
key = filter.split('=')[0]
if not request.GET.has_key(key):
filters.append(filter)
if filters:
return HttpResponseRedirect("%s?%s" % (url, "&".join(filters)))
except: pass
return super(DefaultFilterMixIn, self).changelist_view(request, *args, **kwargs)
And then just define a default_filters on your ModelAdmin:
class YourModelAdmin(DefaultFilterMixIn):
....
default_filters = ('snapshot__exact=0',)
Finally, this is what I was looking for:
def changelist_view(self, request, extra_context=None):
if not request.GET.has_key('status__exact'):
q = request.GET.copy()
q['status__exact'] = '1'
request.GET = q
request.META['QUERY_STRING'] = request.GET.urlencode()
return super(SoumissionAdmin,self).changelist_view(request, extra_context=extra_context)
The other way, with the queryset method in the admin class does not work. In fact it does filter the results, but it leaves the filter functionality broken.
The solution I've found is not perfect either, it's not possible when using it to select the "All/ filter. In my case it's not dramatic and it will be good enough though..
You can override the queryset
class QuoteAdmin(admin.ModelAdmin):
def get_queryset(self, request):
return super(QuoteAdmin,self).get_queryset(request).filter(status="accepted")
However by overriding the queryset you won't ever be able to view quotes that do not have status "accepted".
Alternatively, you can link to the following URL, adding the filter to the GET parameters.
/admin/myapp/quote/?status=accepted
I solved this problem with support 'all'.
in models.py:
STATUSES_CHOICE = (
('0', 'Active'),
('1', 'Deactive'),
('2', 'Suspended'),
)
class Client(models.Model):
...
status = models.IntegerField(verbose_name=_('Status'),
default=0,
db_index=True)
in admin.py:
class StatusAdminFilter(SimpleListFilter):
title = _('Status')
parameter_name = 'status'
all_param_value = 'all'
def lookups(self, request, model_admin):
return STATUSES_CHOICE
def queryset(self, request, queryset):
status = self.value()
try:
return (queryset if status == self.all_param_value else
queryset.filter(status=int(status)))
except ValueError:
raise Http404
def choices(self, cl):
yield {'selected': self.value() == self.all_param_value,
'query_string': cl.get_query_string(
{self.parameter_name: self.all_param_value},
[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}
class ClientAdmin(admin.ModelAdmin):
list_filter = (StatusAdminFilter,)
def changelist_view(self, request, extra_context=None):
if not request.GET.has_key('status'):
q = request.GET.copy()
q['status'] = '0' # default value for status
request.GET = q
request.META['QUERY_STRING'] = request.GET.urlencode()
return super(ClientAdmin, self).changelist_view(
request, extra_context=extra_context)
Short and clean solution. Works nice with "All" option when clicked on change list view.
def changelist_view(self, request, extra_context=None):
if not request.META['QUERY_STRING'] and \
not request.META.get('HTTP_REFERER', '').startswith(request.build_absolute_uri()):
return HttpResponseRedirect(request.path + "?status__exact=1")
return super(YourModelAdmin,self).changelist_view(request, extra_context=extra_context)
I think I've found a way to do this without limiting the user. Just look at the referer to determine if the user just arrived at this page. If so redirect them to the default url you want based on that filter.
def changelist_view(self, request, extra_context=None):
try:
test = request.META['HTTP_REFERER'].split(request.META['PATH_INFO'])
if test and test[-1] and not test[-1].startswith('?') and not request.GET.has_key('status__exact'):
return HttpResponseRedirect("/admin/app/model/?status__exact=1")
except: pass # In case there is no referer
return super(MyModelAdmin,self).changelist_view(request, extra_context=extra_context)
This worked for me and avoided having the "All" problem mentioned by h3.
class MyAdmin(admin.ModelAdmin):
def changelist_view(self, request, extra_context=None):
referer = request.META.get('HTTP_REFERER', '')
showall = request.META['PATH_INFO'] in referer and not request.GET.has_key('timeframe')
if not showall and not request.GET.has_key('param_name_here'):
q = request.GET.copy()
q['param_name_here'] = 'default_value_here'
request.GET = q
request.META['QUERY_STRING'] = request.GET.urlencode()
return super(SerializableAdmin,self).changelist_view(request, extra_context=extra_context)
Here is my update for glic3rinu's code (see comment there), which works on Python 3.4 and Django 1.9.7:
class DefaultFilterMixIn(admin.ModelAdmin):
def changelist_view(self, request, *args, **kwargs):
from django.http import HttpResponseRedirect
if self.default_filters:
#try:
test = request.META['HTTP_REFERER'].split(request.META['PATH_INFO'])
if test and test[-1] and not test[-1].startswith('?'):
url = reverse('admin:{}_{}_changelist'.format(self.opts.app_label, self.opts.model_name))
filters = []
for filter in self.default_filters:
key = filter.split('=')[0]
if not key in request.GET:
filters.append(filter)
if filters:
return HttpResponseRedirect("{}?{}".format(url, "&".join(filters)))
#except: pass
return super(DefaultFilterMixIn, self).changelist_view(request, *args, **kwargs)
Here is my attempt to have a default filter set in admin (only tested with Django 1.11):
class ZeroCountListFilter(admin.SimpleListFilter):
title = _('include zero count')
parameter_name = 'count'
def choices(self, changelist):
yield {
'selected': self.value() is None or self.value() == 0,
'query_string': changelist.get_query_string({}, [self.parameter_name]),
'display': _('No'),
}
yield {
'selected': self.value() == '1',
'query_string': changelist.get_query_string({self.parameter_name: '1'}, []),
'display': _("Yes"),
}
def lookups(self, request, model_admin):
return (
('0', _('No')),
('1', _('Yes')),
)
def queryset(self, request, queryset):
if self.value() is None or self.value() == '0':
return queryset.exclude(count=0)
else:
return queryset
The trick is to check self.value() is None
to get the default behaviour
精彩评论