2009-05-12 12 views
71

Wie kann ich die Standardfilterwahl von 'ALL' ändern? Ich habe ein Feld mit dem Namen status, das drei Werte hat: activate, pending und rejected. Wenn ich list_filter in Django admin verwende, ist der Filter standardmäßig auf 'All' gesetzt, aber ich möchte ihn standardmäßig auf Anstehend setzen.Standardfilter in Django admin

Antwort

43
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) 
+15

Diese Lösung hat den Nachteil, dass, obwohl die Auswahl "Alle" immer noch in der Benutzeroberfläche angezeigt wird, die Auswahl der Option dennoch die Standardfilterung anwendet. – akaihola

+0

Ich habe die gleiche Frage, aber ich kann die Wiederholung verstehen ... sorry, ich bin neu mit Django ... aber vielleicht funktioniert das http://blog.dougalmathews.com/2008/10/filter-the-django -modeladmin-set/ – Asinox

+0

Das ist gut, aber ich musste den get-Parameter in der URL sehen, damit mein Filter ihn aufnehmen und anzeigen kann. Meine Lösung wird in Kürze veröffentlicht. – radtek

2

Beachten Sie, dass, wenn anstelle von Vorauswahl Wert Filter Sie wollen immer die Daten vorab filtern, bevor es im Admin zeigt, sollten Sie die ModelAdmin.queryset() Methode anstelle außer Kraft setzen.

+0

Dies ist eine ziemlich saubere und schnelle Lösung, obwohl es immer noch Probleme verursachen kann. Wenn die Filteroptionen im Admin aktiviert sind, kann der Benutzer scheinbar falsche Ergebnisse erhalten. Wenn das überschriebene Abfrage-Set eine .exclude() - Klausel enthält, werden die aufgezeichneten Datensätze niemals aufgelistet, aber die Optionen für die admin-Filterung, die explizit angezeigt werden sollen, werden weiterhin von der Admin-Benutzeroberfläche angeboten. –

+0

Es gibt andere richtigere Antworten mit niedrigeren Stimmen, die für diese Situation gelten, da das OP eindeutig gefordert hat, dass er einen Filter setzen wird, in dem ein Suchsatz die falsche Lösung wäre, wie auch oben von @TomasAndle gezeigt. – eskhool

+0

Danke, dass du @eskhool darauf hingewiesen hast, ich habe versucht, meine Antwort auf Null zu reduzieren, aber es scheint mir nicht erlaubt zu sein, dich selbst zu verwerfen. – akaihola

1

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

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>

17

Hat ha22109's obige Antwort genommen und modifiziert, um die Auswahl von "All" durch Vergleichen von HTTP_REFERER und PATH_INFO zu ermöglichen.

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) 
+3

Dies ist für mich kaputt, weil HTTP_REFERER nicht immer vorhanden war. Ich habe 'referer = request.META.get (' HTTP_REFERER ',' '); test = referer.split (request.META ['PATH_INFO']) ' –

+0

@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 vollständig behoben, wenn HTTP_REFERER nicht vorhanden ist? –

+0

@the_game yeah, die Idee ist, wenn Sie eckige Klammern verwenden, um auf einen Schlüssel zuzugreifen, der nicht existiert, wirft er 'KeyError', während, wenn Sie die' get() 'Methode des Dikts verwenden, können Sie einen Standard angeben. Ich habe einen Standardwert von leerer Zeichenfolge angegeben, so dass split() "AttributeError" nicht auslöst. Das ist alles. –

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

Ich hatte eine Änderung zu machen, richtig zu erhalten Filterung zu arbeiten. Die vorherige Lösung funktionierte für mich, als die Seite geladen wurde. Wenn eine "Aktion" ausgeführt wurde, ging der Filter zurück zu "Alle" und nicht zu meinen Standardeinstellungen. Diese Lösung lädt die Seite zum Ändern der Administratoren mit dem Standardfilter, behält aber auch Filteränderungen oder den aktuellen Filter bei, wenn andere Aktivitäten auf der Seite stattfinden. Ich habe nicht alle Fälle getestet, aber in Wirklichkeit kann es die Einstellung eines Standardfilters beschränken, der nur beim Laden der Seite auftritt.

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

Um einen brauchbaren ‚All‘ Link in der Sidebar (dh eine, die alle zeigt, anstatt zeigen pending) diese und haben zu erreichen, müssen Sie einen benutzerdefinierte Liste Filter erstellen, erben von django.contrib.admin.filters.SimpleListFilter und filtern standardmäßig auf "ausstehend". 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: Erfordert Django 1.4 (Danke Simon)

+3

Dies ist die sauberste Lösung von allen, aber es hat die wenigsten upvotes ... es erfordert Django 1.4, obwohl, obwohl das jetzt eine Selbstverständlichkeit sein sollte. – Simon

+2

Einfach versucht, es funktioniert auch in Django 1.8. –

+0

@Greg Wie entfernen Sie ** die Funktionalität des Filters ** und den Filter-Tab vollständig aus der Admin-Seite? –

1

Eine leichte Verbesserung auf 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 nette Lösung!

13

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

0

Ein bisschen off-topic, aber meine Suche nach einer ähnlichen Frage führte mich hier. Ich suchte nach einer Standardabfrage nach einem Datum (dh, wenn keine Eingabe bereitgestellt wird, zeige nur Objekte mit timestamp von 'Heute'), was die Frage ein wenig komplizierter macht. Hier ist, was ich kam mit:

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 des Standard DateFieldListFilter.Durch die Einstellung self.date_params wird sichergestellt, dass das Filter-Dropdown-Menü auf die Option self.used_parameters aktualisiert wird. Aus diesem Grund müssen Sie sicherstellen, dass die self.used_parameters genau das sind, was von einer dieser Dropdown-Optionen verwendet würde (dh, herauszufinden, was die date_params wäre, wenn Sie die "Heute" oder "Letzte 7 Tage" verwenden und die self.used_parameters zusammenpassen jene).

Dies wurde mit Django 1.4.10

6

Hier meine generische Lösung ist, überprüft es sind Parameter alle GET, wenn es nur umleiten mit arbeiten gebaut, wenn keine vorhanden sind, dann leitet es automatisch mit dem Parameter Standard erhalten. Ich habe auch ein list_filter set, so dass es das aufgreift 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) 

Der einzige Nachteil ist, wenn Sie einen direkten Zugriff auf die Seite mit "?" In der URL ist kein HTTP_REFERER-Set vorhanden, daher wird der Standardparameter und die Umleitung verwendet. Das ist gut für mich, es funktioniert gut, wenn Sie durch den Admin-Filter klicken.

UPDATE:

Um rund um die Einschränkung zu bekommen, endete ich eine benutzerdefinierte Filterfunktion Schreiben auf, die die changelist_view Funktionalität 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 die Standardparameter, wenn keine vorhanden sind. Die Idee war, die Generics-Filter-Fähigkeit loszuwerden, um alle zu sehen, indem keine get-Parameter verwendet wurden. Sehen Sie alle zugewiesenen ich den Status = 8 zu diesem Zweck .:

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) 
+0

Ich habe einen Fix für meinen Vorbehalt, einen benutzerdefinierten Filter. Ich werde es als eine alternative Lösung vorstellen. – radtek

+0

Danke, ich finde die Umleitung die sauberste und einfachste Lösung. Ich verstehe auch nicht "den Vorbehalt". Ich bekomme immer das gewünschte Ergebnis, entweder durch Klicken oder durch direkte Verbindung (ich habe den benutzerdefinierten Filter nicht verwendet). –

0

Dies kann einen alten Thread, aber dachte, ich würde meine Lösung hinzufügen, da ich nicht bessere Antworten auf Google-Suchen finden konnte.

Tun Sie, was (nicht sicher, ob seine Deminic Rodger oder ha22109) in der Modeladmin beantwortet für 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) 

Dann brauchen wir eine benutzerdefinierte 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

Ich fand, dass ich die Funktion "force_text" (aka force_unicode) im yield-Aufruf in der Auswahlfunktion verwenden musste, sonst würde die ausgewählte Filteroption nicht als "ausgewählt" angezeigt werden. Das ist "'selected': self.value() == force_text (lookup)," – MagicLAMP

3

Sie können einfach return queryset.filter() zum Erstellen oder if self.value() is None und Override-Methode von SimpleListFilter

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