web-dev-qa-db-de.com

Benutzerdefinierter Filter in Django Admin am Django 1.3 oder darunter

Wie kann ich Django admin (den Filtern, die auf der rechten Seite eines Modell-Dashboards angezeigt werden) einen benutzerdefinierten Filter hinzufügen? Ich weiß, dass es einfach ist, einen Filter basierend auf einem Feld dieses Modells einzufügen. 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??
73
sghael

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

Mir ist aufgefallen, dass der Code der Frage eine Datumszeit verwendet, um herauszufinden, wann sie aktiv ist. Also habe ich die DateFieldFilterSpec verwendet und unterklassifiziert.

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 filters.py einfügen und in das Modell importieren, zu dem Sie den Filter hinzufügen möchten

57
Mark Ellul

sie müssen eine benutzerdefinierte FilterSpec schreiben (nirgendwo dokumentieren). Schauen Sie sich hier ein Beispiel an:

http://www.djangosnippets.org/snippets/1051/

23
gpilotino

In der aktuellen Django Entwicklungsversion gibt es Unterstützung für benutzerdefinierte Filter: https://docs.djangoproject.com/en/dev/ref/contrib/admin/#Django.contrib .admin.ModelAdmin.list_filter

9
matley

Kein optimaler Weg (in Bezug auf die CPU), aber einfach und funktioniert, so mache ich es (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 wichtigste Idee dabei ist, über auf benutzerdefinierte Felder in einem QuerySet zuzugreifen __Anruf__() Funktion.

3

Nur eine Randnotiz: Sie können die Standard-Ticks für Django admin 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
gklka

Das kannst du leider nicht. Derzeit können Elemente, die keine Felder sind, nicht als list_filter-Einträge verwendet werden.

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

3
Daniel Roseman

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

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

Die Hauptantwort auf diese Frage hat bei mir (Django 1.3) nicht funktioniert, da in der Methode field_path Kein Parameter __init__ Angegeben wurde. Auch es hat DateFieldFilterSpec untergeordnet. Das postage -Feld 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=0 für jeden der möglichen Filter zu erstellen. Filter Ich denke, sind kumulativ. Wenn es also eine vorherige Anfrage nach "Nein" gab und wir jetzt eine Anfrage nach "Ja" haben, müssen wir die entfernen. Nein, Frage. self.removes Gibt an, was für jede Abfrage entfernt werden muss. Die Methode choices erstellt die Abfragezeichenfolgen, gibt an, welcher Filter ausgewählt wurde, und legt den angezeigten Namen des Filters fest.

2
peter2108

Hier ist die Antwort und implementiert den benutzerdefinierten Filter so einfach wie möglich, dies könnte helfen

Django Admin Datumsbereich Filter

1
Jay Dave