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??
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
sie müssen eine benutzerdefinierte FilterSpec schreiben (nirgendwo dokumentieren). Schauen Sie sich hier ein Beispiel an:
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
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.
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
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',)
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.
Hier ist die Antwort und implementiert den benutzerdefinierten Filter so einfach wie möglich, dies könnte helfen