Как изменить выбор фильтра по умолчанию из "ВСЕ"? У меня есть поле с именем status
, которое имеет три значения: activate
, pending
и rejected
. Когда я использую list_filter
в администраторе Django, по умолчанию по умолчанию устанавливается значение "Все", но я хочу, чтобы он был отложен по умолчанию.
Ответ 1
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)
Ответ 2
Чтобы достичь этого и иметь полезную ссылку "Все" на боковой панели (то есть, которая отображает все, а не показывается в ожидании), вам нужно создать собственный фильтр списка, наследующий от django.contrib.admin.filters.SimpleListFilter
и фильтрацию на "ожидает" по умолчанию. Что-то в этом направлении должно работать:
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 (спасибо Саймону)
Ответ 3
Взяв ответ ha22109 выше и изменив его, чтобы разрешить выбор "Все" путем сравнения HTTP_REFERER
и 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)
Ответ 4
Я знаю, что этот вопрос сейчас довольно старый, но он все еще действителен. Я считаю, что это самый правильный способ сделать это. Он по существу тот же, что и метод Грега, но сформулирован как расширяемый класс для легкого повторного использования.
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,)
Ответ 5
Вот мое общее решение с использованием перенаправления, оно просто проверяет, есть ли какие-либо параметры GET, если они не существуют, тогда он перенаправляется с параметром get по умолчанию. У меня также есть набор list_filter, поэтому он выбирает это и отображает значение по умолчанию.
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)
Единственное предостережение - когда вы делаете прямой доступ к странице с помощью?? присутствует в URL-адресе, нет HTTP_REFERER, поэтому он будет использовать параметр по умолчанию и перенаправить. Это прекрасно для меня, он отлично работает, когда вы щелкаете через админ-фильтр.
UPDATE
Чтобы обойти оговорку, я закончил писать пользовательскую функцию фильтра, которая упростила функцию changelist_view. Вот фильтр:
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)
И теперь changelist_view передает только параметр по умолчанию, если он отсутствует. Идея заключалась в том, чтобы избавиться от возможности фильтров generics для просмотра всех без использования параметров get. Чтобы просмотреть все, для этой цели я присвоил статус = 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
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 )
Ответ 7
Вы можете просто использовать return queryset.filter()
или if self.value() is None
и переопределить метод SimpleListFilter
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,
}
Ответ 8
Обратите внимание, что если вместо предварительного выбора значения фильтра вы всегда должны предварительно фильтровать данные, прежде чем показывать их в администраторе, вместо этого вы должны переопределить метод ModelAdmin.queryset()
.
Ответ 9
Я знаю, что это не лучшее решение, но я изменил index.html в шаблоне администратора, строки 25 и 37, как это:
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>
Ответ 10
Мне пришлось внести изменения, чтобы фильтр работал правильно. Предыдущее решение работало для меня при загрузке страницы. Если было выполнено "действие", фильтр вернулся к "Все", а не по умолчанию. Это решение загружает страницу изменения администратора с помощью фильтра по умолчанию, но также поддерживает изменения фильтра или текущий фильтр, когда на странице происходит другая активность. Я не тестировал все случаи, но на самом деле это может ограничивать установку фильтра по умолчанию только при загрузке страницы.
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)
Ответ 11
Небольшое улучшение ответа Грега с использованием DjangoChoices, Python >= 2.5 и, конечно, 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]
Спасибо Грегу за приятное решение!
Ответ 12
Немного не по теме, но мой поиск подобного вопроса привел меня сюда. Я искал запрос по умолчанию по дате (т.е. Если вход не указан, отображать только объекты с timestamp
из "Сегодня" ), что немного усложняет вопрос. Вот что я придумал:
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),
)
Это простое переопределение по умолчанию DateFieldListFilter
. Установив self.date_params
, он гарантирует, что раскрывающийся список фильтра обновится до любой опции, соответствующей self.used_parameters
. По этой причине вы должны убедиться, что self.used_parameters
- это именно то, что будет использоваться одним из этих выпадающих списков (т.е. Узнать, что будет date_params
при использовании "Сегодня" или "Последние 7 дней" и построить self.used_parameters
, чтобы соответствовать этим).
Это было создано для работы с Django 1.4.10
Ответ 13
Это может быть старый поток, но я думал, что добавлю свое решение, поскольку не нашел более подходящих ответов на поисковые запросы Google.
Сделайте то, что (не уверен, что его Деминический Роджер, или ha22109) ответил в ModelAdmin для 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)
Затем нам нужно создать пользовательский 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