web-dev-qa-db-ja.com

Django adminのデフォルトフィルター

デフォルトのフィルター選択を「すべて」から変更するにはどうすればよいですか? statusactivatependingの3つの値を持つrejectedという名前のフィールドがあります。 Django admin)でlist_filterを使用すると、フィルターはデフォルトで「すべて」に設定されますが、デフォルトで保留に設定したいです。

85
ha22109
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)
44
ha22109

これを実現するにはandサイドバーに使用可能な「すべて」リンク(つまり、保留中ではなくすべてを表示するリンク)が必要です。 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が必要です(サイモンに感謝)

90
Greg

上記のha22109の回答を取得し、HTTP_REFERERPATH_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)
18
iridescent

この質問は今ではかなり古いものですが、まだ有効です。これが最も正しい方法だと思います。基本的には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,)
16
Andrew Hows

リダイレクトを使用する私の一般的なソリューションは次のとおりです。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は、デフォルトパラメータが存在しない場合にのみデフォルトパラメータを渡すようになりました。アイデアは、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)
7
radtek
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 )
4
user1163719

単にusereturn queryset.filter()またはif self.value() is NoneとSimpleListFilterのOverrideメソッドを使用できます。

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,
        }
3
Jay Dave

フィルター値を事前に選択する代わりに、管理者に表示する前に常にデータを事前にフィルターしたい場合は、代わりにModelAdmin.queryset()メソッドをオーバーライドする必要があります。

2
akaihola

DjangoChoicesを使用したGregの答えのわずかな改善、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] 

ニースのソリューションを提供してくれたGregに感謝します!

2
Ben Konrath

私はそれが最善の解決策ではないことを知っていますが、管理テンプレートの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>

1
Mauro De Giorgi

フィルタリングが正しく機能するように修正する必要がありました。前のソリューションは、ページが読み込まれたときに機能しました。 「アクション」が実行された場合、フィルターはデフォルトではなく「すべて」に戻りました。このソリューションは、管理者変更ページにデフォルトのフィルターをロードしますが、ページで他のアクティビティが発生した場合、フィルターの変更または現在のフィルターも維持します。すべてのケースをテストしたわけではありませんが、実際には、デフォルトのフィルターの設定がページのロード時にのみ発生するように制限している可能性があります。

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)
1
mhck

少し話題から外れていますが、同様の質問を探してここに来ました。日付までにデフォルトのクエリを探していました(つまり、入力がない場合、timestampが '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_がこれらのドロップダウン選択の1つで使用されるものであることを保証する必要があります(つまり、「Today」または「Last 7」を使用する場合の_date_params_ Days 'および_self.used_parameters_を構築してそれらに一致させます)。

これはDjango 1.4.10で動作するように構築されました

0
alukach

これは古いスレッドかもしれませんが、Google検索でより良い答えを見つけることができなかったため、ソリューションを追加すると考えました。

Changelist_viewのModelAdminで応答したこと(そのDeminic Rodgerまたはha22109かどうかはわかりません)

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
0
warath-coder