2009-09-21 7 views
6

Ich benutze den sehr coolen Django-Filter (via: http://github.com/alex/django-filter) und beide können nicht meinen Kopf um die Dokumente wickeln, oder vielleicht nur brauchen einen kleinen Schub.'Django-Filter' mit CHOICES-Feld verwenden - brauche "Any" -Option

Wenn ich das Filterformular auf einer Objektlistenseite zeige, bekomme ich für ein FK-Feld I das Dropdown, das ein "-----" enthält, was zu einem "Any" -Filter führt. Aber ich habe einige Optionen auf ein Feld in diesem Modell eingestellt, und ich möchte die gleiche "Any" Art Option bekommen. Hier ist ein relevantes Beispiel Teil von models.py:

TICKET_STATUS_CHOICES = (
    ('new', 'New'), 
    ('accepted', 'Accepted'), 
    ('assigned', 'Assigned'), 
    ('reopened', 'Reopened'), 
    ('closed', 'Closed'), 
) 

class Ticket(models.Model): 
    assigned_to = models.ForeignKey(User, null=True, blank=True) 
    status = models.CharField(max_length=20, 
choices=TICKET_STATUS_CHOICES, default='new') 

import django_filters 

class TicketFilter(django_filters.FilterSet): 
    class Meta: 
     model = Ticket 
     fields = ['assigned_to', 'status'] 

Wenn ich die Filterform anzuzeigen, jede ‚Option" assigned_to' einen bekommt‘, wie auch die verfügbaren Benutzer auflistet. Das Feld 'status' ist jedoch , das nur auf die Optionen beschränkt ist, die in den tatsächlichen '_CHOICES' aufgeführt sind.

Wie füge ich eine 'any' -Option zu den Feldern basierend auf _CHOICES hinzu?

Antwort

6

Wie in den kurzen, aber süß ‚Nutzung‘ docs erwähnt,

Filter nehmen auch alle beliebigen Schlüsselwort-Argumente, die auf den django.forms.Field Konstruktor übergeben bekommen.

Das machte nicht viel Sinn, bis ich ein bisschen weiter schaute. Im Verzeichnis ./django-filter/docs/ref/ gibt es filters.txt, in der die Filterfelder und die Modellfelder beschrieben werden, mit denen sie standardmäßig interagieren. (Ich denke, ich habe die Sprache hier, wenn nicht, korrigieren Sie mich).

So können wir sehen, dass ChoiceFilter für jedes Feld "mit Auswahlmöglichkeiten" verwendet wird.

Für die Django-Dokumentation sind Form Fields und ihre Interaktion mit Models wichtig. (Verwenden von Django-Formularen). So finden wir ChoiceField (http://docs.djangoproject.com/en/dev/ref/forms/fields/#choicefield), die sagt

ein zusätzliches erforderliches Argument: ChoiceField.choices Ein iterable (beispielsweise eine Liste oder Tupel) von 2-Tupeln als Auswahlmöglichkeiten für dieses Feld zu verwenden.

So können Sie eine Liste übergeben, nicht nur die ursprüngliche Tuple-Auswahl.

So könnte dies nicht pythonic oder trocken sein, aber hier ist, wie ich das Modell in Frage geändert:

class TicketFilter(django_filters.FilterSet): 
    class Meta: 
     model = Ticket 
     fields = ['assigned_to', 'priority', 'status'] 

    def __init__(self, *args, **kwargs): 
     super(TicketFilter, self).__init__(*args, **kwargs) 
     self.filters['priority'].extra.update(
      { 
       'choices': CHOICES_FOR_PRIORITY_FILTER 
      }) 

Und darüber, wo mein _Choices definiert wurde, habe ich diesen neuen definiert (siehe oben) und sorgt dafür, dass die ursprüngliche Auswahl an das Ende anhängen:

CHOICES_FOR_PRIORITY_FILTER = [ 
    ('', 'Any'), 
] 
CHOICES_FOR_PRIORITY_FILTER.extend(list(TICKET_PRIORITY_CHOICES)) 

I Liste bin mit() hier, weil die ursprünglichen Auswahl wurde in einem Tupel einrichten, so mag ich, dass in eine Liste machen. Wenn Sie einen NoneType-Fehler erhalten, stellen Sie sicher, dass Sie nicht versuchen, den Rückgabewert von .extend() zuzuweisen, da es keinen gibt. Ich stolperte darüber, weil ich vergaß, dass es eine Methode von list war, und keine Funktion, die eine neue Liste zurückgab.

Wenn Sie einen einfacheren, mehr DRY oder "Pythonic" Weg wissen, lassen Sie es mich wissen!

6

DRY'er wäre die Verwendung des bereits definierten Arguments 'Choice' für ChoiceFilter.

So können Sie einfach Ihre FILTER_CHOICES erweitern TICKET_STATUS_CHOICES plus eine 'any' Option mit dem leeren String sein:

FILTER_CHOICES = (
    ('new', 'New'), 
    ('accepted', 'Accepted'), 
    ('assigned', 'Assigned'), 
    ('reopened', 'Reopened'), 
    ('closed', 'Closed'), 
    ('', 'Any'), 
) 

Und Ihre TicketFilter wäre:

class TicketFilter(django_filters.FilterSet): 

    status = django_filters.ChoiceFilter(choices=FILTER_CHOICES) 

    class Meta: 
     model = Ticket 
     fields = ['assigned_to'] 
2

Basierend auf der Arbeit von anonymem Feigling, tat ich folgendes:

import django_filters 

from .models import Experiment 

EMPTY_CHOICE = ('', '---------'), # Don't forget the trailing comma 

class ExperimentFilter(django_filters.FilterSet): 
    class Meta: 
     model = Experiment 
     fields = ['method__year',] 

    def __init__(self, *args, **kwargs): 
     super(ExperimentFilter, self).__init__(*args, **kwargs) 
     self.filters['method__year'].extra['choices'] = EMPTY_CHOICE + \ 
      self.filters['method__year'].extra['choices'] 
1

Ich kombinierte alle oben genannten Methoden Ich mochte nicht alle Felder, die aktualisiert werden müssen und endete mit etwas in der Art:

import django_filters 

from django.db import models 
from django_filters.filters import ChoiceFilter 

EMPTY_CHOICE = ('', '---------') 

class TicketFilter(django_filters.FilterSet): 
    def __init__(self, *args, **kwargs): 
     super(TicketFilter, self).__init__(*args, **kwargs) 
     # add empty choice to all choice fields: 
     choices = filter(
      lambda f: isinstance(self.filters[f], ChoiceFilter), 
      self.filters) 

     for field_name in choices: 
      extended_choices = ((EMPTY_CHOICE,) + 
           self.filters[field_name].extra['choices']) 
      self.filters[field_name].extra['choices'] = extended_choices 

Was macht den Job.

0

Noch kürzere ist die leere Label zu verwenden:

class TicketFilter(FilterSet): 

    status = ChoiceFilter(choices=Ticket.CHOICES, empty_label=ugettext_lazy(u'Any')) 

    class Meta: 
     model = Ticket 
     fields = ('status',)