Frage Verwenden Sie "Django-Filter" mit CHOICES-Feld - benötigen Sie "Any" -Option


Ich benutze den sehr coolen Django-Filter (via: http://github.com/alex/django-filter) und beide können meinen Kopf nicht um die Dokumente wickeln, oder vielleicht einfach brauche einen kleinen Schub.

Wenn ich das Filterformular auf einer Objektlistenseite für ein FK-Feld I zeige Holen Sie sich die Dropdown-Liste, die ein "-----" enthält, was zu einem "Any" -Filter. Aber ich habe einige Optionen auf ein Feld gesetzt Modell, und ich möchte die gleiche "Any" -Typ-Option erhalten. Hier ist ein relevanter Beispielteil 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,assigned_to' bekommt eine "any" -Option, als sowie die verfügbaren Benutzer auflisten. Das 'status' Feld ist jedoch beschränkt auf nur die Optionen, die in den tatsächlichen '_CHOICES' aufgeführt sind.

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


6
2017-09-21 16:23


Ursprung


Antworten:


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

Sie können also Ihre FILTER_CHOICES einfach um Ihre TICKET_STATUS_CHOICES plus eine beliebige Option mit der leeren Zeichenfolge erweitern:

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

Und Ihr TicketFilter wäre:

class TicketFilter(django_filters.FilterSet):

   status = django_filters.ChoiceFilter(choices=FILTER_CHOICES)

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

7
2018-05-17 11:36



Wie in den kurzen, aber süssen "Usage" -Dokumenten erwähnt,

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

Das machte nicht viel Sinn, bis ich ein bisschen weiter schaute. In dem ./django-filter/docs/ref/ Verzeichnis, da ist filters.txt Diese beschreibt die Filterfelder und die Modellfelder, mit denen sie standardmäßig interagieren. (Ich denke, ich habe die Sprache hier, wenn nicht, korrigieren Sie mich).

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

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

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

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

Das ist vielleicht nicht pythonisch oder DRY, aber hier habe ich das Modell in Frage gestellt:

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 hinaus, wo meine _CHOICES definiert wurden, definierte ich diese neue (oben erwähnte) und stellte sicher, dass die ursprünglichen Entscheidungen an das Ende angehängt wurden:

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

Ich benutze list () hier, weil die ursprünglichen Choices in einem Tupel erstellt wurden, also möchte ich das in eine Liste umwandeln. Wenn Sie einen NoneType-Fehler erhalten, stellen Sie sicher, dass Sie nicht versuchen, den Rückgabewert von anzugeben .extend()weil es keinen gibt. Ich stolperte darüber, weil ich vergessen hatte, dass es eine Methode war listund keine Funktion, die eine neue Liste zurückgegeben hat.

Wenn Sie eine einfachere, trockenere oder "pythonische" Möglichkeit kennen, lassen Sie es mich wissen!


6
2017-09-21 18:13



Basierend auf der Arbeit des anonymen Feiglings 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']

2
2017-08-10 20:42



Ich habe alle oben genannten Methoden kombiniert, da ich es nicht mochte, alle Felder anzugeben, die aktualisiert werden müssen, und endete damit in etwa so:

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 die Arbeit.


1
2018-04-06 18:56



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

class TicketFilter(FilterSet):

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

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

1
2018-05-22 15:29



Du kannst es einfach benutzen null_label und null_value, z.B.

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

0
2017-08-31 13:35