Фильтр по умолчанию в Django admin
как я могу изменить выбор фильтра по умолчанию из "все"? У меня есть поле с именем status
, который имеет три значения: activate
,pending
и rejected
. Когда я использую list_filter
в Django admin фильтр по умолчанию установлен на "все", но я хочу установить его в ожидание по умолчанию.
13 ответов:
чтобы достичь этого и у вас есть полезная ссылка " Все " на боковой панели (т. е. та, которая показывает все, а не показывает ожидание), вам нужно будет создать пользовательский фильтр списка, наследующий от
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 (спасибо Саймон)
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)
взял ответ 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)
Я знаю, что этот вопрос довольно старый сейчас, но он все еще действителен. Я считаю, что это самый правильный способ сделать это. Это по существу то же самое, что и метод Грега, но сформулированный как расширяемый класс для легкого повторного использования.
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,)
вот мое общее решение с помощью перенаправления, он просто проверяет, есть ли какие-либо параметры 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, поэтому он будет использовать параметр по умолчанию и перенаправление. Это нормально для меня, он отлично работает, когда вы нажимаете через фильтр администратора.
обновление:
чтобы обойти предостережение, я закончил тем, что написал пользовательскую функцию фильтра, которая упростила функциональность 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)
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 )
вы можете просто использовать
return queryset.filter()
илиif self.value() is None
и переопределить метод SimpleListFilterdef 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, }
обратите внимание, что если вместо предварительного выбора значения фильтра вы хотите всегда предварительно фильтровать данные перед их отображением в Администраторе, вы должны переопределить
ModelAdmin.queryset()
метод вместо этого.
Я знаю, что это не лучшее решение, но я изменил индекс.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>
мне пришлось внести изменения, чтобы заставить фильтрацию работать правильно. Предыдущее решение работало для меня, когда страница загружается. Если было выполнено "действие", фильтр вернулся к "все", а не к моему умолчанию. Это решение загружает страницу изменения администратора с фильтром по умолчанию, но также поддерживает изменения фильтра или текущий фильтр, когда на странице происходят другие действия. Я не проверял все случаи, но на самом деле это может быть ограничение настройки фильтра по умолчанию, чтобы произойти только тогда, когда страница нагрузки.
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)
небольшое улучшение ответа Грега с использованием 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]
спасибо Грегу за хорошее решение!
немного не по теме, но мой поиск подобного вопроса привел меня сюда. Я искал запрос по умолчанию по дате (т. е. если ввод не предусмотрен, показывать только объекты с
timestamp
of 'Today'), что немного усложняет вопрос. Вот что я придумал: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
Это может быть старый поток, но я думал, что добавлю свое решение, поскольку я не мог найти лучших ответов на поиск 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