Benutzerdefinierter Filter in Django Admin auf Django 1.3 oder niedriger


73

Wie kann ich dem Django-Administrator einen benutzerdefinierten Filter hinzufügen (die Filter, die auf der rechten Seite eines Modell-Dashboards angezeigt werden)? Ich weiß, dass es einfach ist, einen Filter einzuschließen, der auf einem Feld dieses Modells basiert, aber was ist mit einem "berechneten" Feld wie diesem:

class NewsItem(models.Model):
    headline = models.CharField(max_length=4096, blank=False)
    byline_1 = models.CharField(max_length=4096, blank=True)
    dateline = models.DateTimeField(help_text=_("date/time that appears on article"))
    body_copy = models.TextField(blank=False)

    when_to_publish = models.DateTimeField(verbose_name="When to publish",  blank=True, null=True)

    # HOW CAN I HAVE "is_live" as part of the admin filter?  It's a calculated state!!
    def is_live(self):
        if self.when_to_publish is not None:
            if ( self.when_to_publish < datetime.now() ):
                return """ <img alt="True" src="/media/img/admin/icon-yes.gif"/> """
        else:
            return """ <img alt="False" src="/media/img/admin/icon-no.gif"/> """      

    is_live.allow_tags = True

class NewsItemAdmin(admin.ModelAdmin):
    form = NewsItemAdminForm
    list_display = ('headline', 'id', 'is_live')
    list_filter = ('is_live')  #  how can i make this work??

Andere Leute sagten bereits, dass diese Funktion im Kofferraum ist (1.4 dev). Weitere Informationen: Versionshinweis und Dokumentation .
Paolo

1
Hier ist ein besserer Link zur Dokumentation. Die Erweiterung von SimpleListFilter ist der richtige Weg. FilterSpecs sind veraltet. docs.djangoproject.com/de/dev/ref/contrib/admin/…
Fastmultiplication

Siehe Matley-Antwort unten mit einem Link zur offiziellen Dokumentation.
Denilson Sá Maia

Antworten:


57

Vielen Dank an gpilotino, der mir den Schub in die richtige Richtung gegeben hat, um dies umzusetzen.

Ich habe festgestellt, dass der Code der Frage eine Datumszeit verwendet, um herauszufinden, wann sie aktiv ist. Also habe ich die DateFieldFilterSpec verwendet und sie in Unterklassen unterteilt.

from django.db import models
from django.contrib.admin.filterspecs import FilterSpec, ChoicesFilterSpec,DateFieldFilterSpec
from django.utils.encoding import smart_unicode
from django.utils.translation import ugettext as _
from datetime import datetime

class IsLiveFilterSpec(DateFieldFilterSpec):
    """
    Adds filtering by future and previous values in the admin
    filter sidebar. Set the is_live_filter filter in the model field attribute
    'is_live_filter'.    my_model_field.is_live_filter = True
    """

    def __init__(self, f, request, params, model, model_admin):
        super(IsLiveFilterSpec, self).__init__(f, request, params, model,
                                               model_admin)
        today = datetime.now()
        self.links = (
            (_('Any'), {}),
            (_('Yes'), {'%s__lte' % self.field.name: str(today),
                       }),
            (_('No'), {'%s__gte' % self.field.name: str(today),
                    }),

        )


    def title(self):
        return "Is Live"

# registering the filter
FilterSpec.filter_specs.insert(0, (lambda f: getattr(f, 'is_live_filter', False),
                               IsLiveFilterSpec))

Zur Verwendung können Sie den obigen Code in eine filter.py einfügen und in das Modell importieren, zu dem Sie den Filter hinzufügen möchten


2
Können Sie den letzten Teil Ihrer Arbeit mit diesem Code etwas näher erläutern?
Nick Heiner

Rosarch, die letzte Codezeile, registriert den is_live_filter in django, dann haben Sie in Ihrer models.py in der Modellklasse beispielsweise Article ein Feld namens "Publish_date", das Sie "Publish_date.is_live_filter" nennen würden
Mark Ellul

1
Die letzte Zeile filter_specs.insert ist sehr wichtig, da sonst Ihr benutzerdefinierter Filter wahrscheinlich nicht angezeigt wird. Stattdessen wird eine der integrierten Filterspezifikationen für diesen Feldtyp angezeigt. (Ich habe die Antwort zuerst nicht richtig gelesen und verwendete die .register-Methode wie die eingebauten Filterspezifikationen!)
Anentropic

9
NB: In Django 1.4 wurden Filterspezifikationen (die immer ein interner Hack waren) in ListFilter überarbeitet und bieten einen saubereren Pfad zur Anpassung: Siehe code.djangoproject.com/ticket/5833
Philippe Ombredanne,



3

Das kannst du leider nicht. Derzeit können Nicht-Feldelemente nicht als list_filter-Einträge verwendet werden.

Beachten Sie, dass Ihre Admin-Klasse nicht funktioniert hätte, selbst wenn es sich um ein Feld handelte, da ein Tupel mit einem einzelnen Element ein Komma benötigt: ('is_live',)


2
FWIW, ein Fix für # 5833 ist jetzt im Django-Kofferraum für Django 1.4
Philippe Ombredanne

3

Nur eine Randnotiz: Sie können die taubstummen Häkchen auf dem Django-Administrator einfacher wie folgt verwenden:

def is_live(self):
    if self.when_to_publish is not None:
        if ( self.when_to_publish < datetime.now() ):
            return True
    else:
        return False

is_live.boolean = True

3

Kein optimaler Weg (CPU-weise), aber einfach und wird funktionieren, also mache ich es so (für meine kleine Datenbank). Meine Django-Version ist 1.6.

In admin.py:

class IsLiveFilter(admin.SimpleListFilter):
    title = 'Live'
    parameter_name = 'islive'
    def lookups(self, request, model_admin):
        return (
            ('1', 'islive'),
        )
    def queryset(self, request, queryset):
        if self.value():
            array = []
            for element in queryset:
                if element.is_live.__call__() == True:
                    q_array.append(element.id)
            return queryset.filter(pk__in=q_array)

...

class NewsItemAdmin(admin.ModelAdmin):
    form = NewsItemAdminForm
    list_display = ('headline', 'id', 'is_live')
    list_filter = (IsLiveFilter)

Die Schlüsselidee hierbei ist der Zugriff auf benutzerdefinierte Felder in einem QuerySet über die Funktion __call __ () .


Vielleicht nicht der optimierteste Weg, aber ein unkomplizierter und funktionierender. Es hat mir Kopfschmerzen erspart.
d6bels

2

Der Benutzer liefert Waren in einige Länder portofrei. Ich wollte diese Länder filtern:

Alle - alle Länder, Ja - portofrei, Nein - Porto.

Die Hauptantwort auf diese Frage hat bei mir nicht funktioniert (Django 1.3). Ich denke, weil field_pathdie __init__Methode keinen Parameter enthält . Auch es wurde untergeordnet DateFieldFilterSpec. Das postageFeld ist ein FloatField

from django.contrib.admin.filterspecs import FilterSpec

class IsFreePostage(FilterSpec):

    def __init__(self, f, request, params, model, model_admin, field_path=None):
        super(IsFreePostage, self).__init__(f, request, params, model,
            model_admin, field_path)

        self.removes = {
            'Yes': ['postage__gt'],
            'No': ['postage__exact'],
            'All': ['postage__exact', 'postage__gt'] }

        self.links = (
            ('All', {}),
            ('Yes', {'postage__exact': 0}),
            ('No', {'postage__gt': 0}))

        if request.GET.has_key('postage__exact'):
            self.ttl = 'Yes'
        elif request.GET.has_key('postage__gt'):
            self.ttl = 'No'
        else:
            self.ttl = 'All'

    def choices(self, cl):
        for title, param_dict in self.links:
            yield {'selected': title == self.ttl,
                   'query_string': cl.get_query_string(param_dict,
                       self.removes[title]),
                   'display': title}
    def title(self):
        return 'Free Postage'

FilterSpec.filter_specs.insert(0,
    (lambda f: getattr(f, 'free_postage', False), IsFreePostage))

In self.links liefern wir Diktate. wird verwendet, um HTTP-Abfragezeichenfolgen wie ?postage__exact=0für jeden der möglichen Filter zu erstellen. Filter sind meiner Meinung nach kumulativ. Wenn also zuvor eine Anfrage für "Nein" gestellt wurde und wir jetzt eine Anfrage für "Ja" haben, müssen wir die Abfrage "Nein" entfernen. self.removesGibt an, was für jede Abfrage entfernt werden muss. Die choicesMethode erstellt die Abfragezeichenfolgen, gibt an, welcher Filter ausgewählt wurde, und legt den angezeigten Namen des Filters fest.


Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.