13 Stimmen

Verwendung von 'django-filter' mit CHOICES-Feld - benötigt "Any"-Option

Ich verwende den sehr coolen django-Filter (via: http://github.com/alex/django-filter ) und komme entweder mit den Unterlagen nicht zurecht oder brauche einfach eine kleine Unterstützung brauchen.

Wenn ich das Filterformular auf einer Objektlistenseite einblende, erhalte ich für ein FK-Feld die Dropdown-Liste, die ein "-----" enthält, was zu einem Filter vom Typ "beliebig" ergibt. Aber ich habe einige Auswahlmöglichkeiten für ein Feld in diesem Modell festgelegt, und ich möchte die gleiche Option vom Typ "any" erhalten. Hier ist ein relevanter Beispielabschnitt aus 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 das Filterformular anzeige, wird ' assigned_to' erhält eine 'any'-Option, da sowie eine Auflistung der verfügbaren Benutzer. Die 'status' Feld ist jedoch auf die im Feld "_CHOICES" aufgeführten Optionen beschränkt.

Wie füge ich den Feldern, die auf _CHOICES basieren, eine Option "any" hinzu?

14voto

mpaf Punkte 6143

DRY'er wäre es, das bereits definierte Argument "choices" für ChoiceFilter zu verwenden.

Sie könnten also einfach Ihre FILTER_CHOICES um Ihre TICKET_STATUS_CHOICES plus eine Option "any" mit der leeren Zeichenfolge erweitern:

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

Und Ihr TicketFilter würde lauten:

class TicketFilter(django_filters.FilterSet):

   status = django_filters.ChoiceFilter(choices=FILTER_CHOICES)

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

8voto

anonymous coward Punkte 12154

Wie in den kurzen, aber aussagekräftigen "Nutzungs"-Dokumenten erwähnt,

Filter nehmen auch beliebige Schlüsselwortargumente entgegen, die an die django.forms.Field Konstrukteur.

Das ergab nicht viel Sinn, bis ich etwas genauer hinsah. In der ./django-filter/docs/ref/ Verzeichnis, gibt es filters.txt die die Filterfelder und die Modellfelder beschreibt, mit denen sie standardmäßig interagieren. (Ich glaube, ich habe die Sprache hier richtig verstanden, wenn nicht, korrigieren Sie mich).

Wir können also feststellen, dass ChoiceFilter wird für jedes Feld "mit Auswahlmöglichkeiten" verwendet.

Wenn man die Django-Dokumentation zu Rate zieht, geht es hier um Formularfelder und wie sie mit Modellen interagieren. (Django Forms verwenden). Wir finden also ChoiceField ( http://docs.djangoproject.com/en/dev/ref/forms/fields/#choicefield ), die besagt

Benötigt ein zusätzliches Argument: ChoiceField.choices Eine Iterable (z. B. eine Liste oder ein Tupel) von 2-Tupeln, die als Auswahlmöglichkeiten für dieses Feld verwendet werden sollen.

Sie können also eine Liste übergeben, nicht nur die ursprüngliche Tupel-Auswahlmenge.

Das ist vielleicht nicht pythonisch oder trocken, aber so habe ich das betreffende Modell 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 meine _CHOICES definiert waren, habe ich diese neue definiert (wie oben erwähnt) und die ursprünglichen Auswahlmöglichkeiten an das Ende angehängt:

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

Ich verwende hier list(), weil die ursprünglichen Choices in einem Tupel angelegt waren, das ich in eine Liste umwandeln möchte. Wenn Sie einen NoneType-Fehler erhalten, stellen Sie sicher, dass Sie nicht versuchen, den "Rückgabewert" von .extend() denn es gibt keine. Ich bin darüber gestolpert, weil ich vergessen hatte, dass es sich um eine Methode eines list und nicht eine Funktion, die eine neue Liste zurückgibt.

Wenn Sie einen einfacheren, trockeneren oder "pythonischen" Weg kennen, um dies zu tun, lassen Sie es mich bitte wissen!

6voto

Paul Bormans Punkte 1202

Noch kürzer ist es, wenn Sie das leere Etikett verwenden:

class TicketFilter(FilterSet):

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

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

3voto

Wim Feijen Punkte 671

Auf der Grundlage der Arbeit von anonymous coward habe ich Folgendes getan:

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']

2voto

Shahar Gino Punkte 95

Sie können einfach Folgendes verwenden null_label y null_value z.B.

    my_field = MultipleChoiceFilter(choices=MY_CHOICES, null_label='Any', null_value='')

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X