Подтвердить что ты не робот

Фильтр по умолчанию в Django admin

Как изменить выбор фильтра по умолчанию из "ВСЕ"? У меня есть поле с именем status, которое имеет три значения: activate, pending и rejected. Когда я использую list_filter в администраторе Django, по умолчанию по умолчанию устанавливается значение "Все", но я хочу, чтобы он был отложен по умолчанию.

4b9b3361

Ответ 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