Python Django管理中的默认过滤器

Python Django管理中的默认过滤器,python,django,django-admin,Python,Django,Django Admin,如何从“全部”更改默认筛选器选项?我有一个名为status的字段,它有三个值:activate、pending和rejected。当我在Django admin中使用list\u filter时,默认情况下过滤器设置为'All',但我想在默认情况下将其设置为pending。请注意,如果您不想预先选择过滤器值,而是希望在将数据显示在admin中之前始终对其进行预筛选,则应覆盖ModelAdmin.queryset()方法。我知道这不是最好的解决方案,但我更改了管理模板第25行和第37行的index

如何从“全部”更改默认筛选器选项?我有一个名为
status
的字段,它有三个值:
activate
pending
rejected
。当我在Django admin中使用
list\u filter
时,默认情况下过滤器设置为'All',但我想在默认情况下将其设置为pending。

请注意,如果您不想预先选择过滤器值,而是希望在将数据显示在admin中之前始终对其进行预筛选,则应覆盖
ModelAdmin.queryset()
方法。

我知道这不是最好的解决方案,但我更改了管理模板第25行和第37行的index.html,如下所示:

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)
25:


37:

接受了上面ha22109的答案,并通过比较
HTTP\u REFERER
PATH\u 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)
def changelist_视图(self、request、extra_context=None):
默认过滤器=False
尝试:
ref=request.META['HTTP\u REFERER']
pinfo=request.META['PATH\u INFO']
qstr=参考拆分(pinfo)
如果len(qstr)<2:
默认过滤器=真
除:
默认过滤器=真
如果是默认的\u过滤器:
q=request.GET.copy()
q['registered\uuu exact']='1'
request.GET=q
request.META['QUERY_STRING']=request.GET.urlencode()
返回super(InterestAdmin,self).changelist\u视图(请求,额外上下文=额外上下文)

我必须进行修改才能使过滤正常工作。加载页面时,前面的解决方案对我有效。如果执行了“操作”,则过滤器返回“全部”,而不是我的默认设置。此解决方案使用默认筛选器加载admin change页面,但在页面上发生其他活动时,也会维护筛选器更改或当前筛选器。我并没有测试所有的情况,但实际上它可能会限制默认过滤器的设置只在页面加载时出现

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)
def changelist_视图(self、request、extra_context=None):
默认过滤器=False
尝试:
ref=request.META['HTTP\u REFERER']
pinfo=request.META['PATH\u INFO']
qstr=参考拆分(pinfo)
querystr=request.META['QUERY\u STRING']
#检查查询字符串值,否则在
#尝试筛选筛选器将在下面重置
如果querystr为None:
如果len(qstr)<2或qstr[1]='':
默认过滤器=真
除:
默认过滤器=真
如果是默认的\u过滤器:
q=request.GET.copy()
q['registered\uuu isnull']='True'
request.GET=q
request.META['QUERY_STRING']=request.GET.urlencode()
返回super(MyAdmin,self).changelist\u视图(请求,额外上下文=额外上下文)
为了实现这一点,并在侧边栏中有一个可用的“全部”链接(即显示全部而不是显示挂起的链接),您需要创建一个自定义列表筛选器,继承自
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] 

编辑:需要Django 1.4(谢谢Simon)

使用Django选项,Python>=2.5,当然,Django>=1.4,对Greg的答案稍加改进

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] 

感谢Greg提供了很好的解决方案

我知道这个问题已经很老了,但它仍然有效。我相信这是最正确的方法。它本质上与Greg的方法相同,但被表述为易于重用的可扩展类

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,)

有点离题,但我对一个类似问题的探索让我来到了这里。我希望有一个按日期的默认查询(即如果没有提供输入,则只显示带有
时间戳的对象,时间戳为'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\u参数
,可以确保过滤器下拉列表将更新为与
self.used\u参数
匹配的任何选项。因此,您必须确保
self.used_参数
正是其中一个下拉选择所使用的参数(即,在使用“今天”或“过去7天”时,找出
date_参数
,并构建
self.used_参数
以匹配这些参数)


这是为配合Django 1.4.10而构建的,这是我使用重定向的通用解决方案,它只检查是否有任何GET参数,如果没有,则使用默认的GET参数重定向。我还有一个列表过滤器集,所以它会选择并显示默认值

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\u视图功能。以下是过滤器:

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_视图现在只传递默认参数。其想法是通过不使用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)

这可能是一条老线索,但我想我会添加我的解决方案,因为我在谷歌搜索中找不到更好的答案

做什么(不确定它的Deminic Rodger或ha22109是否在ModelAdmin for changelist_视图中回答)

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
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,
        }

您可以简单地使用
返回queryset.filter()
如果self.value()为None
并重写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
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,
        }

这是我能够生成的最干净版本的过滤器,它具有重新定义的“All”和选中的默认值

默认情况下,If显示当前发生的行程

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()
创建了一个可重用的Fil
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, )