Использование 'django-filter' с полем CHOICES - нужна опция "Any" - PullRequest
8 голосов
/ 21 сентября 2009

Я использую очень крутой django-фильтр (через: http://github.com/alex/django-filter) и либо не могу обернуть мою голову вокруг документов, или, может быть, просто нужно немного повысить.

Когда я показываю форму фильтра на странице списка объектов, для поля FK я получить раскрывающийся список, который включает в себя «-----», что приводит к Фильтр любого типа. Но у меня есть несколько вариантов выбора модель, и я хотел бы получить тот же вариант «любой» тип. Вот соответствующая часть примера из 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']

Когда я отображаю форму фильтра, «assigned_to' получает вариант« любой », как а также список доступных пользователей. Поле 'status', однако, ограничено только параметрами, перечисленными в фактических параметрах _CHOICES.

Как добавить параметр 'any' в поля на основе _CHOICES?

Ответы [ 6 ]

8 голосов
/ 21 сентября 2009

Как упоминалось в кратких, но приятных документах по использованию,

Фильтры также принимают произвольные аргументы ключевых слов, которые передаются на django.forms.Field конструктор.

Это не имело большого смысла, пока я не посмотрел немного дальше. В каталоге ./django-filter/docs/ref/ есть filters.txt, который описывает поля фильтра и с какими полями модели они взаимодействуют по умолчанию. (Я думаю, что у меня есть язык прямо здесь, если нет, поправьте меня).

Итак, мы видим, что ChoiceFilter используется для любого поля «с выбором».

Подчеркнув документацию по Django, здесь важны поля форм и то, как они взаимодействуют с моделями. (Использование форм Django). Таким образом, мы находим ChoiceField (http://docs.djangoproject.com/en/dev/ref/forms/fields/#choicefield), который говорит

Принимает один дополнительный обязательный аргумент: ChoiceField.choices Итерация (например, список или кортеж) из двух кортежей для использования в качестве выбора для этого поля.

Таким образом, вы можете передать ему список, а не только исходный набор настроек Tuple.

Так что это может быть не питон или СУХОЙ, но вот как я изменил модель:

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

И, кроме того, там, где были определены мои _CHOICES, я определил этот новый (упомянутый выше) и удостоверился, что в конце добавим исходные варианты:

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

Я использую list () здесь, потому что исходные Choices были настроены в кортеже, поэтому я хочу превратить это в список. Кроме того, если вы получаете ошибку NoneType, убедитесь, что вы не пытаетесь присвоить 'возвращаемое значение' .extend(), потому что его нет. Я споткнулся об этом, потому что я забыл, что это был метод list, а не функция, которая возвращала новый список.

Если вам известен более простой, более СУХОЙ или «питонический» способ сделать это, пожалуйста, дайте мне знать!

7 голосов
/ 17 мая 2013

DRY'er будет использовать уже определенный аргумент 'choices' для ChoiceFilter.

Таким образом, вы можете просто расширить ваши FILTER_CHOICES, чтобы они стали вашими TICKET_STATUS_CHOICES плюс опция 'any' с пустой строкой:

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

И ваш TicketFilter будет:

class TicketFilter(django_filters.FilterSet):

   status = django_filters.ChoiceFilter(choices=FILTER_CHOICES)

   class Meta:
      model = Ticket
      fields = ['assigned_to']
2 голосов
/ 11 августа 2014

Основываясь на работе анонимного труса, я сделал следующее:

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 голос
/ 22 мая 2017

Еще короче использовать пустую метку:

class TicketFilter(FilterSet):

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

    class Meta:
        model = Ticket
        fields = ('status', )
1 голос
/ 06 апреля 2015

Я объединил все методы выше, так как мне не понравилось указывать все поля, которые нужно обновить, и в итоге получилось что-то вроде этого:

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

Что делает работу.

0 голосов
/ 31 августа 2018

Вы можете просто использовать null_label и null_value, например,

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