2011-10-07 7 views
8

Ho un semplice modello di Django come:Filtraggio Django Admin da Null/Non è Null

class Person(models.Model): 
    referrer = models.ForeignKey('self', null=True) 
    ... 

In ModelAdmin di questo modello, come vorrei permettere che venga filtrato da se o non referrer è nullo? Per impostazione predefinita, l'aggiunta del referrer a list_filter provoca la visualizzazione di un menu a discesa che elenca il record ogni persona, che può essere tra centinaia di migliaia, impedendo in modo efficace il caricamento della pagina. Anche se carica, non riesco ancora a filtrare secondo i criteri che voglio.

Ad esempio, come si modifica questo in modo che il menu a discesa elenca solo le scelte "Tutto", "Nullo" o "Non Null"?

Ho visto alcuni posts che pretendono di realizzare qualcosa di simile utilizzando sottoclassi di FilterSpec personalizzate, ma nessuno di loro spiega come usarli. I pochi che ho visto sembrano applicare a tutti i campi di tutti i modelli, cosa che non vorrei. Inoltre, c'è la documentazione zero per FilterSpec, il che mi rende nervoso, perché non voglio investire un sacco di codice personalizzato legato ad una classe interna transitoria che potrebbe scomparire dalla prossima versione.

risposta

1

Ho finito per utilizzare una miscela di the top solution here, insieme a this snippet.

Tuttavia, ho dovuto modificare leggermente lo snippet, eliminando la restrizione del tipo di campo e aggiungendo il nuovo field_path, aggiunto di recente in 1.3.

from django.contrib.admin.filterspecs import FilterSpec 
from django.db import models 
from django.utils.safestring import mark_safe 
from django.utils.translation import ugettext as _ 

class NullFilterSpec(FilterSpec): 
    #fields = (models.CharField, models.IntegerField, models.FileField) 

    @classmethod 
    def test(cls, field): 
     #return field.null and isinstance(field, cls.fields) and not field._choices 
     return field.null and not field._choices 
    #test = classmethod(test) 

    def __init__(self, f, request, params, model, model_admin, field_path=None): 
     super(NullFilterSpec, self).__init__(f, request, params, model, model_admin, field_path) 
     self.lookup_kwarg = '%s__isnull' % f.name 
     self.lookup_val = request.GET.get(self.lookup_kwarg, None) 

    def choices(self, cl): 
     # bool(v) must be False for IS NOT NULL and True for IS NULL, but can only be a string 
     for k, v in ((_('All'), None), (_('Has value'), ''), (_('Omitted'), '1')): 
      yield { 
       'selected' : self.lookup_val == v, 
       'query_string' : cl.get_query_string({self.lookup_kwarg : v}), 
       'display' : k 
      } 

# Here, we insert the new FilterSpec at the first position, to be sure 
# it gets picked up before any other 
FilterSpec.filter_specs.insert(0, 
    # If the field has a `profilecountry_filter` attribute set to True 
    # the this FilterSpec will be used 
    (lambda f: getattr(f, 'isnull_filter', False), NullFilterSpec) 
) 
1

C'è stato un biglietto che rimbalza in giro per questo per 4 anni (https://code.djangoproject.com/ticket/5833). Ha mancato il traguardo di 1,3, ma ha raggiunto lo stato di nuove funzionalità e presumibilmente ha trovato la sua strada nel bagagliaio. Se non ti dispiace correre fuori dal tronco, puoi usarlo ora. La patch è presumibilmente compatibile 1.3, tuttavia, quindi è possibile cavarsela con la patch appena installata.

13

Dal Django 1.4 apporta alcune modifiche ai filtri, ho pensato che salvare qualcuno il tempo che ho appena trascorso la modifica del codice di risposta accettata Cerin a lavorare con Django 1.4 rc1.

Ho un modello che ha TimeField (null = True) denominato "started" e volevo filtrare valori null e non null, quindi è prety lo stesso problema di OP.
Così, qui è quello che ha funzionato per me ...

Definito (di fatto incluse) questi in admin.py:

from django.contrib.admin.filters import SimpleListFilter 

class NullFilterSpec(SimpleListFilter): 
    title = u'' 

    parameter_name = u'' 

    def lookups(self, request, model_admin): 
     return (
      ('1', _('Has value'),), 
      ('0', _('None'),), 
     ) 

    def queryset(self, request, queryset): 
     kwargs = { 
     '%s'%self.parameter_name : None, 
     } 
     if self.value() == '0': 
      return queryset.filter(**kwargs) 
     if self.value() == '1': 
      return queryset.exclude(**kwargs) 
     return queryset 



class StartNullFilterSpec(NullFilterSpec): 
    title = u'Started' 
    parameter_name = u'started' 

che appena li usavano in ModelAdmin:

class SomeModelAdmin(admin.ModelAdmin): 
    list_filter = (StartNullFilterSpec,) 
+3

In 1.4 c'è '' BooleanFieldListFilter'' che lo farà di default. '' list_filter = (('myfield', BooleanFieldListFilter), 'other_field', 'other_field2') ''. Nei campi non booleani ottiene lo stesso effetto di null/not null. –

+1

@KyleMacFarlane Sembra non funzionare per un campo DateTime se – Kos

+1

Utilizzando 1.6 sembra non funzionare anche per ForeignKeys. –

7

I avere una versione più semplice della risposta di frnhr, che in realtà filtra nella condizione __isnull. (Django 1.4+):

from django.contrib.admin import SimpleListFilter 

class NullListFilter(SimpleListFilter): 
    def lookups(self, request, model_admin): 
     return (
      ('1', 'Null',), 
      ('0', '!= Null',), 
     ) 

    def queryset(self, request, queryset): 
     if self.value() in ('0', '1'): 
      kwargs = { '{0}__isnull'.format(self.parameter_name) : self.value() == '1' } 
      return queryset.filter(**kwargs) 
     return queryset 

Poi anche:

class StartNullListFilter(NullListFilter): 
    title = u'Started' 
    parameter_name = u'started' 

e infine:

class SomeModelAdmin(admin.ModelAdmin): 
    list_filter = (StartNullListFilter,) 

Personalmente non mi piace il mio cestino admin.py con decine di classi, così mi si avvicinò con una tale funzione di supporto:

def null_filter(field, title_=None): 
    class NullListFieldFilter(NullListFilter): 
     parameter_name = field 
     title = title_ or parameter_name 
    return NullListFieldFilter 

quale posso poi applico come in:

class OtherModelAdmin(admin.ModelAdmin): 
    list_filter = (null_filter('somefield'), null_filter('ugly_field', _('Beautiful Name')),) 
2

c'è un modo semplice:

class RefererFilter(admin.SimpleListFilter): 
    title = 'has referer' 
    # Parameter for the filter that will be used in the URL query. 
    parameter_name = 'referer__isnull' 

    def lookups(self, request, model_admin): 
     return (
      ('False', 'has referer'), 
      ('True', 'has no referer'), 
     ) 

    def queryset(self, request, queryset): 
     if self.value() == 'False': 
      return queryset.filter(referer__isnull=False) 
     if self.value() == 'True': 
      return queryset.filter(referer__isnull=True) 

Poi, proprio li usavano in ModelAdmin:

class PersonAdmin(admin.ModelAdmin): 
    list_filter = (RefererFilter,)