Standardfilter in Django admin

94

Wie kann ich die Standardfilterauswahl von 'ALL' ändern? Ich habe ein Feld mit dem Namen statusdrei Werte : activate, pendingund rejected. Wenn ich list_filterin Django admin verwende, ist der Filter standardmäßig auf "Alle" gesetzt, aber ich möchte ihn standardmäßig auf "Ausstehend" setzen.

ha22109
quelle

Antworten:

102

Um dies zu erreichen und einen verwendbaren Link "Alle" in Ihrer Seitenleiste zu haben (dh einen Link, der alle anzeigt, anstatt ausstehend anzuzeigen), müssen Sie einen benutzerdefinierten Listenfilter erstellen django.contrib.admin.filters.SimpleListFilter, der standardmäßig von "ausstehend" erbt und nach "ausstehend" filtert . Etwas in diese Richtung sollte funktionieren:

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: Benötigt Django 1.4 (danke Simon)

Greg
quelle
3
Dies ist die sauberste Lösung von allen, aber sie hat die wenigsten positiven Stimmen ... es erfordert jedoch Django 1.4, obwohl dies mittlerweile eine Selbstverständlichkeit sein sollte.
Simon
@Greg Wie können Sie die Funktionalität des Filters und der Registerkarte "Filter" vollständig von der Administrationsseite entfernen ?
2
Diese Lösung hat einen kleinen Nachteil. Wenn die Filter leer sind (tatsächlich verwendeter 'ausstehender' Filter), ermittelt Django 1.8 fälschlicherweise die vollständige Ergebnisanzahl und zeigt die Ergebnisanzahl nicht an, wenn show_full_result_count True ist (standardmäßig). -
Alexander Fedotov
Beachten Sie, dass, wenn Sie die choicesMethode in der Lösung nicht überschreiben , ärgerlicherweise weiterhin die eigene Option Alle oben in der Auswahlliste hinzugefügt wird.
Richard
47
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
quelle
18
Diese Lösung hat den Nachteil, dass die Auswahl "Alle" zwar weiterhin in der Benutzeroberfläche angezeigt wird, bei Auswahl jedoch weiterhin die Standardfilterung angewendet wird.
Akaihola
Ich habe die gleiche Frage, aber ich kann die Wiederholung verstehen ... Entschuldigung, ich bin
Asinox
Das ist gut, aber ich musste den get-Parameter in der URL sehen, damit mein Filter ihn aufnehmen und als ausgewählt anzeigen kann. Poste meine Lösung in Kürze.
Radtek
Erklärung fehlt. Nur einen Code zu veröffentlichen, hilft möglicherweise nicht jedem. Darüber hinaus funktioniert es nicht und ohne ein wenig Kontext ist es schwer herauszufinden, warum
EvilSmurf
19

Hat die Antwort von ha22109 oben übernommen und geändert, um die Auswahl von "Alle" durch Vergleichen von HTTP_REFERERund zu ermöglichen 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)
irisierend
quelle
3
Dies ist für mich kaputt gegangen, weil HTTP_REFERER nicht immer vorhanden war. Ich habe 'referer = request.META.get (' HTTP_REFERER ',' '); test = referer.split (request.META ['PATH_INFO']) `
ben author
@Ben Ich verwende Ihre zwei Zeilen referer = request.META.get ('HTTP_REFERER', '') test = referer.split (request.META ['PATH_INFO']). Ich habe nicht viel über HTTP_REFERER. Ist das Problem in diesen Zeilen vollständig behoben, wenn HTTP_REFERER nicht vorhanden ist?
the_game
@the_game Ja, die Idee ist, wenn Sie in eckigen Klammern versuchen, auf einen nicht vorhandenen Schlüssel zuzugreifen, wird dieser KeyErrorausgelöst. Wenn Sie die get()Methode des Diktats verwenden , können Sie einen Standard angeben. Ich habe einen Standardwert für leere Zeichenfolgen angegeben, damit split () nicht ausgelöst wird AttributeError. Das ist alles.
Ben Autor
@ Ben. Danke, dass es bei mir funktioniert. Können Sie diese Frage auch beantworten ? Ich glaube, dies ist eine Erweiterung dieser Frage. Nur stackoverflow.com/questions/10410982/… . Können Sie mir bitte eine Lösung dafür geben?
the_game
1
Das funktioniert gut. has_key()wird jedoch zugunsten von abgelehnt key in d. Aber ich weiß, dass Sie gerade die Antwort von ha22109 übernommen haben. Eine Frage: Warum verwenden, request.META['PATH_INFO']wenn Sie nur verwenden könnten request.path_info(kürzer)?
Nick
19

Ich weiß, dass diese Frage jetzt ziemlich alt ist, aber sie ist immer noch gültig. Ich glaube, das ist der richtigste Weg, dies zu tun. Es ist im Wesentlichen dasselbe wie Gregs Methode, jedoch als erweiterbare Klasse für eine einfache Wiederverwendung formuliert.

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,)
Andrew Hows
quelle
8

Hier ist meine generische Lösung mit Umleitung. Sie prüft nur, ob GET-Parameter vorhanden sind. Wenn keine vorhanden sind, leitet sie mit dem Standardparameter get um. Ich habe auch einen list_filter gesetzt, damit er diesen aufnimmt und den Standard anzeigt.

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)

Die einzige Einschränkung ist, wenn Sie mit "?" Direkt auf die Seite gelangen. In der URL ist kein HTTP_REFERER festgelegt, daher wird der Standardparameter verwendet und umgeleitet. Das ist in Ordnung für mich, es funktioniert großartig, wenn Sie durch den Admin-Filter klicken.

UPDATE :

Um diese Einschränkung zu umgehen, habe ich eine benutzerdefinierte Filterfunktion geschrieben, die die Funktion changelist_view vereinfacht. Hier ist der Filter:

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)

Und die changelist_view übergibt jetzt nur dann den Standardparameter, wenn keiner vorhanden ist. Die Idee war, die generische Filterfunktion zu beseitigen, mit der alle ohne Verwendung von get-Parametern angezeigt werden können. Um alles anzuzeigen, habe ich zu diesem Zweck den Status = 8 zugewiesen:

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)
radtek
quelle
Ich habe eine Lösung für meine Einschränkung, einen benutzerdefinierten Filter. Ich werde es als alternative Lösung vorstellen.
Radtek
Vielen Dank, ich finde die Weiterleitung die sauberste und einfachste Lösung. Ich verstehe auch "die Einschränkung" nicht. Ich erhalte immer das gewünschte Ergebnis, ob durch Klicken oder Verwenden eines direkten Links (ich habe den benutzerdefinierten Filter nicht verwendet).
Dennis Golomazov
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 )
user1163719
quelle
4

Sie können einfach die Methode return queryset.filter()oder if self.value() is Noneund Override von SimpleListFilter verwenden

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

Beachten Sie, dass Sie die ModelAdmin.queryset()Methode überschreiben sollten, wenn Sie die Daten nicht vorab auswählen, sondern immer vorfiltern möchten, bevor Sie sie im Administrator anzeigen .

Akaihola
quelle
Dies ist eine ziemlich saubere und schnelle Lösung, obwohl sie immer noch Probleme verursachen kann. Wenn die Filteroptionen im Administrator aktiviert sind, erhält der Benutzer möglicherweise scheinbar falsche Ergebnisse. Wenn das überschriebene Abfrageset eine .exclude () -Klausel enthält, werden von dieser erfasste Datensätze niemals aufgelistet, aber die Administratorfilteroptionen, um sie explizit anzuzeigen, werden weiterhin von der Administrator-Benutzeroberfläche angeboten.
Tomas Andrle
Es gibt andere korrektere Antworten mit niedrigeren Stimmen, die für diese Situation gelten, da das OP eindeutig darum gebeten hat, einen Filter einzufügen, in dem ein Abfragesatz die falsche Lösung wäre, wie auch von @TomasAndrle oben ausgeführt.
Eskhool
Vielen Dank, dass Sie auf @eskhool hingewiesen haben. Ich habe versucht, meine Antwort auf Null herabzustimmen, aber es scheint nicht erlaubt zu sein, sich selbst herabzustimmen.
Akaihola
3

Eine leichte Verbesserung von Gregs Antwort mit DjangoChoices, Python> = 2.5 und natürlich 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] 

Danke an Greg für die schöne Lösung!

Ben Konrath
quelle
2

Ich weiß, dass dies nicht die beste Lösung ist, aber ich habe die index.html in der Admin-Vorlage, Zeile 25 und 37, wie folgt geändert:

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>

Mauro De Giorgi
quelle
1

Ich musste eine Änderung vornehmen, damit die Filterung korrekt funktioniert. Die vorherige Lösung hat bei mir funktioniert, als die Seite geladen wurde. Wenn eine 'Aktion' ausgeführt wurde, kehrte der Filter zu 'Alle' zurück und nicht zu meiner Standardeinstellung. Diese Lösung lädt die Seite mit den Administratoränderungen mit dem Standardfilter, behält jedoch auch Filteränderungen oder den aktuellen Filter bei, wenn andere Aktivitäten auf der Seite auftreten. Ich habe nicht alle Fälle getestet, aber in Wirklichkeit kann es sein, dass die Einstellung eines Standardfilters nur beim Laden der Seite eingeschränkt wird.

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

Ein bisschen abseits des Themas, aber meine Suche nach einer ähnlichen Frage führte mich hierher. Ich wollte eine Standardabfrage nach Datum haben (dh wenn keine Eingabe bereitgestellt wird, zeigen Sie nur Objekte mit timestamp'Heute' an), was die Frage etwas kompliziert macht. Folgendes habe ich mir ausgedacht:

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

Dies ist eine einfache Überschreibung der Standardeinstellung DateFieldListFilter. Durch die Einstellung self.date_paramswird sichergestellt, dass die Filter-Dropdown-Liste auf die entsprechende Option aktualisiert wird self.used_parameters. Aus diesem Grund müssen Sie sicherstellen, dass self.used_parametersgenau das ist, was von einer dieser Dropdown-Auswahl verwendet wird (dh finden Sie heraus, was das date_paramswäre, wenn Sie "Heute" oder "Letzte 7 Tage" verwenden, und konstruieren Sie dasself.used_parameters entsprechenden Einstellungen).

Dies wurde für Django 1.4.10 entwickelt

Alukach
quelle
1

Dies mag ein alter Thread sein, aber ich dachte, ich würde meine Lösung hinzufügen, da ich bei Google-Suchen keine besseren Antworten finden konnte.

Tun Sie, was (nicht sicher, ob sein Deminic Rodger oder ha22109) im ModelAdmin für changelist_view geantwortet hat

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)

Dann müssen wir einen benutzerdefinierten SimpleListFilter erstellen

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
Warath-Codierer
quelle
Ich habe festgestellt, dass ich die Funktion 'force_text' (auch bekannt als force_unicode) im Yield-Aufruf in der Auswahlfunktion verwenden muss, da sonst die ausgewählte Filteroption nicht als 'ausgewählt' angezeigt wird. Das ist "'selected': self.value () == force_text (lookup)"
MagicLAMP
1

Hier ist die sauberste Version, die ich aus einem Filter mit einem neu definierten 'Alle' und einem ausgewählten Standardwert generieren konnte.

Wenn zeigt mir standardmäßig die aktuell stattfindenden Reisen.

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()
Jerome Millet
quelle
0

Erstellt eine wiederverwendbare Filter-Unterklasse, inspiriert von einigen der Antworten hier (hauptsächlich Gregs).

Vorteile:

Wiederverwendbar - In alle Standardklassen ModelAdminsteckbar

Erweiterbar - Einfach, zusätzliche / benutzerdefinierte Logik für hinzuzufügenQuerySet Filtern

Einfache Verwendung - In der einfachsten Form müssen nur ein benutzerdefiniertes Attribut und eine benutzerdefinierte Methode implementiert werden (abgesehen von denen, die für die SimpleListFilter-Unterklasse erforderlich sind).

Intuitiver Administrator - Der Filterlink "Alle" funktioniert wie erwartet. wie alle anderen auch

Keine Weiterleitungen - Keine Notwendigkeit, die GETAnforderungsnutzlast zu überprüfen , unabhängig von HTTP_REFERER(oder anderen anforderungsbezogenen Dingen in ihrer Grundform)

Keine (Änderungslisten-) Ansichtsmanipulation - Und keine Vorlagenmanipulationen (Gott bewahre)

Code:

(Die meisten imports sind nur für Typhinweise und Ausnahmen)

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

Beispiel für die vollständige Verwendung:

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

Hoffe das hilft jemandem; Feedback immer geschätzt.

JohnGalt
quelle