web-dev-qa-db-fra.com

Filtre personnalisé dans Django Admin sur Django 1.3 ou inférieur)

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??
73
sghael

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

57
Mark Ellul

vous devez écrire un FilterSpec personnalisé (ne pas documenter n'importe où). Regardez ici pour un exemple:

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

23
gpilotino

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

9
matley

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.

3
Dmitrii Sutiagin

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
3
gklka

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',)

3
Daniel Roseman

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.

2
peter2108

Voici la réponse et implémenté le filtre personnalisé aussi simple que possible, cela pourrait aider

filtre de plage de dates admin Django

1
Jay Dave