2009-09-21 19 views
6

Używam bardzo fajnego filtru Django (przez: http://github.com/alex/django-filter) i albo nie mogę zakrywać głowy wokół dokumentów, albo może tylko potrzebuję trochę doładowania.Używanie "django-filter" z polem CHOICES - potrzebuję opcji "Any"

Po wyświetleniu formularza filtru na stronie z listą obiektów, dla pola FK I pojawia się menu rozwijane zawierające "-----", co powoduje, że filtr typu "any" jest oznaczony jako . Ale mam pewne opcje ustawione na pole w tym modelu i chciałbym uzyskać tę samą opcję "dowolny". Oto odnośny przykład część z 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'] 

Kiedy wyświetli formularz filtra "assigned_to' dostaje żadnej opcji«», jak również wykaz dostępnych użytkowników. Pole 'status' jest jednak ograniczone tylko do opcji wymienionych w rzeczywistych "_CHOICES".

Jak dodać opcję "dowolne" do pól na podstawie _CHOICES?

Odpowiedz

6

Jak wspomniano w krótkich ale słodkie docs „użytkowania”,

Filtry wziąć również żadnych argumentów dowolnych słów kluczowych, które przejdzie na konstruktora django.forms.Field.

To nie miało większego sensu, dopóki nie spojrzałem trochę dalej. W katalogu ./django-filter/docs/ref/ znajduje się filters.txt, który opisuje pola filtru i domyślnie, które pola modelu wchodzą w interakcje. (Myślę, że mam tutaj język, jeśli nie, popraw mnie).

Widzimy więc, że ChoiceFilter jest używane do każdego pola "z opcjami".

Uderzając dokumentacją Django, ważne jest tutaj Form Fields i ich interakcja z modelami. (Korzystanie z Django Forms). Więc znaleźć ChoiceField (http://docs.djangoproject.com/en/dev/ref/forms/fields/#choicefield), który mówi

Bierze jeden dodatkowy wymaganego argumentu: ChoiceField.choices iterable (na przykład listy lub krotka) 2-krotek użyć jako wyborów dla tej dziedzinie.

Dzięki temu można przekazać mu listę, a nie tylko oryginalny zestaw opcji Tuple.

Więc to nie może być pythonic lub DRY, ale oto jak zmieniłem model w pytaniu:

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 
      }) 

A nad tym, gdzie zostały określone moje _CHOICES, zdefiniowałem to nowy (wymienione powyżej) oraz zadbał, aby dołączyć oryginalne wyborów do końca:

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

Używam listy() tutaj, ponieważ oryginalne wybory były ustawione w krotce, więc chcę, aby włączyć to do listy. Ponadto, jeśli otrzymujesz błąd NoneType, upewnij się, że nie próbujesz przypisać "wartości zwracanej" z numeru .extend(), ponieważ go nie ma. Potknąłem się o to, ponieważ zapomniałem, że była to metoda list, a nie funkcja, która zwróciła nową listę.

Jeśli znasz łatwiejszy, bardziej suchy lub "pythonic" sposób, aby to zrobić, proszę dać mi znać!

6

DRY'er będzie używał już zdefiniowanego argumentu "choice" dla ChoiceFilter.

Więc może po prostu rozszerzyć FILTER_CHOICES się swoimi TICKET_STATUS_CHOICES plus 'any' opcja z pustym ciągiem:

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

A twój TicketFilter byłoby:

class TicketFilter(django_filters.FilterSet): 

    status = django_filters.ChoiceFilter(choices=FILTER_CHOICES) 

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

Na podstawie prac anonimowego tchórza, zrobiłem co następuje:

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

Połączyłem wszystkie metody powyżej nce Nie podoba mi się określenie wszystkich pól wymagających aktualizacji i zakończyło się na czymś podobnym:

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 

Który wykonuje zadanie.

0

Nawet krótszy jest skorzystanie z pustą etykietę:

class TicketFilter(FilterSet): 

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

    class Meta: 
     model = Ticket 
     fields = ('status',) 
Powiązane problemy