Comment puis-je ajouter un filtre personnalisé à Django admin (les filtres qui apparaissent sur le côté droit du tableau de bord d'un modèle)? Je sais qu'il est facile d'inclure un filtre basé sur un champ de ce modèle, mais qu'en est-il d'un champ "calculé" comme celui-ci:
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??
Merci à gpilotino de m'avoir donné la Poussée dans la bonne direction pour l'implémentation.
J'ai remarqué que le code de la question utilise un datetime pour savoir quand il est en direct. J'ai donc utilisé le DateFieldFilterSpec et je l'ai sous-classé.
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))
Pour l'utiliser, vous pouvez mettre le code ci-dessus dans un filters.py et l'importer dans le modèle auquel vous souhaitez ajouter le filtre
vous devez écrire un FilterSpec personnalisé (ne pas documenter n'importe où). Regardez ici pour un exemple:
Dans la version de développement actuelle de Django, il existe un support pour les filtres personnalisés: https://docs.djangoproject.com/en/dev/ref/contrib/admin/#Django.contrib .admin.ModelAdmin.list_filter
Pas un moyen optimal (au niveau du processeur) mais simple et cela fonctionnera, donc je le fais de cette façon (pour ma petite base de données). Ma Django est 1.6.
Dans 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)
L'idée clé ici est d'accéder aux champs personnalisés dans un QuerySet via __appel__() une fonction.
Juste une remarque: vous pouvez utiliser les ticks par défaut sur Django admin plus facilement comme ceci:
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
Vous ne pouvez malheureusement pas. Actuellement, les éléments hors champ ne peuvent pas être utilisés comme entrées list_filter.
Notez que votre classe d'administration n'aurait pas fonctionné même s'il s'agissait d'un champ, car un tuple à élément unique a besoin d'une virgule: ('is_live',)
L'utilisateur fournit des marchandises dans certains pays sans frais de port. Je voulais filtrer ces pays:
Tous - tous les pays, Oui - sans frais de port, Non - frais de port facturés.
La réponse principale à cette question n'a pas fonctionné pour moi (Django 1.3) je pense parce qu'il n'y avait pas de field_path
paramètre fourni dans le __init__
méthode. Il a également sous-classé DateFieldFilterSpec
. Le champ postage
est un 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))
Dans self.links, nous fournissons des dict. utilisé pour construire des chaînes de requête HTTP comme ?postage__exact=0
pour chacun des filtres possibles. Les filtres Je pense que sont cumulatifs, donc s'il y avait eu une demande précédente pour "Non" et maintenant nous avons une demande pour "Oui", nous devons supprimer le " Pas de requête. self.removes
spécifie ce qui doit être supprimé pour chaque requête. La méthode choices
construit les chaînes de requête, indique quel filtre a été sélectionné et définit le nom affiché du filtre.
Voici la réponse et implémenté le filtre personnalisé aussi simple que possible, cela pourrait aider