Как я могу изменить выбор фильтра по умолчанию с «ВСЕ»? У меня есть поле с именем , как у status
которого есть три значения: activate
, pending
и rejected
. Когда я использую list_filter
в админке Django, для фильтра по умолчанию установлено значение «Все», но я хочу установить его как ожидающий по умолчанию.
python
django
django-admin
ha22109
источник
источник
choices
метод в решении, он продолжит досадно добавлять свой собственный параметр « Все» вверху списка вариантов.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)
источник
KeyError
, а если вы используете метод dict,get()
вы можете указать значение по умолчанию. Я указал по умолчанию пустую строку, чтобы split () не генерировалAttributeError
. Вот и все.has_key()
устарел в пользуkey in d
. Но я знаю, что вы только что взяли из ответа ha22109. Один вопрос: зачем использовать,request.META['PATH_INFO']
если можно было просто использоватьrequest.path_info
(короче)?Я знаю, что этот вопрос довольно старый, но он все еще актуален. Я считаю, что это наиболее правильный способ сделать это. По сути, это то же самое, что и метод Грега, но сформулированный как расширяемый класс для удобства повторного использования.
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 теперь передает параметр по умолчанию, только если его нет. Идея заключалась в том, чтобы избавиться от возможности универсальных фильтров просматривать все без использования параметров получения. Чтобы просмотреть все, я присвоил для этой цели статус = 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
и Override метод SimpleListFilterfrom 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, }
источник
Обратите внимание: если вместо предварительного выбора значения фильтра вы хотите всегда предварительно фильтровать данные перед их отображением в админке, вам следует
ModelAdmin.queryset()
вместо этого переопределить метод.источник
Небольшое улучшение ответа Грега с использованием 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]
Спасибо Грегу за прекрасное решение!
источник
Я знаю, что это не лучшее решение, но я изменил 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>
источник
Мне пришлось внести изменения, чтобы фильтрация работала правильно. Предыдущее решение сработало у меня при загрузке страницы. Если было выполнено «действие», фильтр возвращался к «Все», а не к моему значению по умолчанию. Это решение загружает страницу изменения администратора с фильтром по умолчанию, но также сохраняет изменения фильтра или текущий фильтр, когда на странице происходит другая активность. Я не проверял все случаи, но на самом деле это может ограничивать настройку фильтра по умолчанию только при загрузке страницы.
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)
источник
Немного не по теме, но мои поиски аналогичного вопроса привели меня сюда. Я искал запрос по умолчанию по дате (то есть, если ввод не предоставлен, показывать только объекты с
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.
источник
Это может быть старый поток, но я подумал, что добавлю свое решение, так как я не мог найти лучших ответов по поиску в Google.
Сделайте то, что (не уверен, что его Deminic Rodger или 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
источник
Вот самая чистая версия, которую мне удалось создать, фильтра с переопределенным «Все» и выбранным значением по умолчанию.
Если по умолчанию отображает текущие поездки.
class HappeningTripFilter(admin.SimpleListFilter): """ Filter the Trips Happening in the Past, Future or now. """ default_value = 'now' title = 'Happening' parameter_name = 'happening' def lookups(self, request, model_admin): """ List the Choices available for this filter. """ return ( ('all', 'All'), ('future', 'Not yet started'), ('now', 'Happening now'), ('past', 'Already finished'), ) def choices(self, changelist): """ Overwrite this method to prevent the default "All". """ value = self.value() or self.default_value for lookup, title in self.lookup_choices: yield { 'selected': value == force_text(lookup), 'query_string': changelist.get_query_string({ self.parameter_name: lookup, }, []), 'display': title, } def queryset(self, request, queryset): """ Returns the Queryset depending on the Choice. """ value = self.value() or self.default_value now = timezone.now() if value == 'future': return queryset.filter(start_date_time__gt=now) if value == 'now': return queryset.filter(start_date_time__lte=now, end_date_time__gte=now) if value == 'past': return queryset.filter(end_date_time__lt=now) return queryset.all()
источник
Создал подкласс многоразового фильтра, вдохновленный некоторыми ответами здесь (в основном Грегом).
Преимущества:
Многоразовый - подключается к любым стандартным
ModelAdmin
классамРасширяемость - легко добавлять дополнительную / настраиваемую логику для
QuerySet
фильтрацииПростота использования - в самой базовой форме необходимо реализовать только один настраиваемый атрибут и один настраиваемый метод (помимо тех, которые требуются для создания подклассов SimpleListFilter)
Интуитивно понятный администратор - ссылка фильтра «Все» работает должным образом; как и все остальные
Без перенаправлений - нет необходимости проверять
GET
полезную нагрузку запроса, независимо отHTTP_REFERER
(или любого другого материала, связанного с запросом, в его базовой форме)Нет (список изменений) манипуляции с просмотром - И никаких манипуляций с шаблоном (не дай бог)
Код:
(большинство
import
s предназначены только для подсказок типов и исключений)from typing import List, Tuple, Any from django.contrib.admin.filters import SimpleListFilter from django.contrib.admin.options import IncorrectLookupParameters from django.contrib.admin.views.main import ChangeList from django.db.models.query import QuerySet from django.utils.encoding import force_str from django.utils.translation import gettext_lazy as _ from django.core.exceptions import ValidationError class PreFilteredListFilter(SimpleListFilter): # Either set this or override .get_default_value() default_value = None no_filter_value = 'all' no_filter_name = _("All") # Human-readable title which will be displayed in the # right admin sidebar just above the filter options. title = None # Parameter for the filter that will be used in the URL query. parameter_name = None def get_default_value(self): if self.default_value is not None: return self.default_value raise NotImplementedError( 'Either the .default_value attribute needs to be set or ' 'the .get_default_value() method must be overridden to ' 'return a URL query argument for parameter_name.' ) def get_lookups(self) -> List[Tuple[Any, str]]: """ Returns a list of tuples. The first element in each tuple is the coded value for the option that will appear in the URL query. The second element is the human-readable name for the option that will appear in the right sidebar. """ raise NotImplementedError( 'The .get_lookups() method must be overridden to ' 'return a list of tuples (value, verbose value).' ) # Overriding parent class: def lookups(self, request, model_admin) -> List[Tuple[Any, str]]: return [(self.no_filter_value, self.no_filter_name)] + self.get_lookups() # Overriding parent class: def queryset(self, request, queryset: QuerySet) -> QuerySet: """ Returns the filtered queryset based on the value provided in the query string and retrievable via `self.value()`. """ if self.value() is None: return self.get_default_queryset(queryset) if self.value() == self.no_filter_value: return queryset.all() return self.get_filtered_queryset(queryset) def get_default_queryset(self, queryset: QuerySet) -> QuerySet: return queryset.filter(**{self.parameter_name: self.get_default_value()}) def get_filtered_queryset(self, queryset: QuerySet) -> QuerySet: try: return queryset.filter(**self.used_parameters) except (ValueError, ValidationError) as e: # Fields may raise a ValueError or ValidationError when converting # the parameters to the correct type. raise IncorrectLookupParameters(e) # Overriding parent class: def choices(self, changelist: ChangeList): """ Overridden to prevent the default "All". """ value = self.value() or force_str(self.get_default_value()) for lookup, title in self.lookup_choices: yield { 'selected': value == force_str(lookup), 'query_string': changelist.get_query_string({self.parameter_name: lookup}), 'display': title, }
Пример полного использования:
from django.contrib import admin from .models import SomeModelWithStatus class StatusFilter(PreFilteredListFilter): default_value = SomeModelWithStatus.Status.FOO title = _('Status') parameter_name = 'status' def get_lookups(self): return SomeModelWithStatus.Status.choices @admin.register(SomeModelWithStatus) class SomeModelAdmin(admin.ModelAdmin): list_filter = (StatusFilter, )
Надеюсь, это кому-то поможет; обратная связь всегда ценится.
источник