ChoiceField не отображает пустую метку при использовании кортежа

Что я пытаюсь сделать

Я собираюсь хранить данные о конкурсах в моей базе данных. Я хочу, чтобы иметь возможность искать соревнования по определенным критериям - тип соревнований в частности.

О типах соревнований

Типы соревнований хранятся в кортеже. Немного укороченный пример:

COMPETITION_TYPE_CHOICES = (
    (1, 'Olympic Games'),
    (2, 'ISU Championships'),
    (3, 'Grand Prix Series'),
)

Они используются в модели следующим образом (опять же - это сокращенная/упрощенная версия модели):

class Competition(models.Model):
    name = models.CharField(max_length=256)
    type = models.IntegerField(choices=COMPETITION_TYPE_CHOICES) 

Форма поиска

Я не хочу, чтобы поля были необходимы в форме поиска, поэтому форма определяется следующим образом:

class CompetitionSearchForm(forms.Form):
    name = forms.CharField(required=False)
    type = forms.ChoiceField(choices=COMPETITION_TYPE_CHOICES,required=False)

Проблема

Я хочу, чтобы виджет выбора в ChoiceField отображал пустую метку, но я ее не получаю. Любая помощь с этим будет высоко оценена:)

Ответ 1

Я нашел решение, которое работает так, как я хочу, без нарушения принципа DRY. Не очень чистый, но я должен это сделать.

Согласно выбор документации не должен быть кортежем:

Наконец, обратите внимание, что выбор может быть любым итерируемый объект - необязательно список или кортеж. Это позволяет построить выбор динамически. Но если вы найдете вы сами выбираете варианты взлома динамичный, вам, вероятно, лучше используя соответствующую таблицу базы данных с Внешний ключ. выбор предназначен для статические данные, которые не сильно меняются, если когда-либо.

Итак, решение, на котором я собираюсь на данный момент:

COMPETITION_TYPE_CHOICES = [
     (1, 'Olympic Games'),
     (2, 'ISU Championships'),
     (3, 'Grand Prix Series'),
]

COMP_TYPE_CHOICES_AND_EMPTY = [('','All')] + COMPETITION_TYPE_CHOICES

И затем:

class CompetitionSearchForm(forms.Form):
    name = forms.CharField(required=False)
    type = forms.ChoiceField(choices=COMP_TYPE_CHOICES_AND_EMPTY, required=False)

Модель остается такой же, как и она.

Ответ 2

Я пробовал решения Monika и Evgeniy без успеха, но у Моники есть хороший момент в том, что выбор не обязательно должен быть кортежами. Поэтому самым простым (и DRYest) решением является просто сделать то, что Django уже делает в поле модели. Просто добавьте пустой выбор и кортежи вместе после преобразования их в список:

from django.db.models.fields import BLANK_CHOICE_DASH

...

type = forms.ChoiceField(choices=BLANK_CHOICE_DASH + list(COMPETITION_TYPE_CHOICES), required=False)

Ответ 3

Лучший выбор - обновить выбор полей в форме init.

COMPETITION_TYPE_CHOICES = (
    (1, 'Olympic Games'),
    (2, 'ISU Championships'),
    (3, 'Grand Prix Series'),
)


class CompetitionSearchForm(forms.Form):
    name = forms.CharField(required=False)
    type = forms.ChoiceField(choices=COMPETITION_TYPE_CHOICES,required=False)

    def __init__(self, *args, **kwargs):
        super(CompetitionSearchForm, self).__init__(*args, **kwargs)
        self.fields['type'].choices.insert(0, ('','---------' ) )

Ответ 4

Просто небольшое изменение Evgeniy answer, которое проверяет, не добавлена ​​ли пустая альтернатива.

Без проверки (по крайней мере, при запуске встроенного сервера) добавляется дополнительная лишняя метка для каждой перезагрузки страницы.

COMPETITION_TYPE_CHOICES = (
    (1, 'Olympic Games'),
    (2, 'ISU Championships'),
    (3, 'Grand Prix Series'),
)

class CompetitionSearchForm(forms.Form):
    name = forms.CharField(required=False)
    type = forms.ChoiceField(choices=COMPETITION_TYPE_CHOICES,required=False)

    def __init__(self, *args, **kwargs):
        super(CompetitionSearchForm, self).__init__(*args, **kwargs)
        if not self.fields['type'].choices[0][0] == '':
            self.fields['type'].choices.insert(0, ('','---------' ) )

Ответ 5

Согласно документации:

Можно либо итерабельный (например, список или кортеж) двухкортежей для использования в качестве вариантов для этого поля, либо вызываемый, который возвращает такой итерабельный. (https://docs.djangoproject.com/en/dev/ref/forms/fields/)

Итак, вы можете просто:

sample_field = forms.ChoiceField(choices=(('', '---'),) + Model.YOUR_CHOICES)

Ответ 6

Попробуйте добавить blank=True в поля модели (при условии, что вы хотите), затем измените форму на ModelForm и удалите определения полей. Обратите внимание, что любые поля, для которых вы устанавливаете blank=True, не потребуются при проверке или сохранении модели. Опять же, это может быть не то, что вы хотите, но если это позволит Django автоматически позаботиться о нескольких вещах.

В противном случае просто измените свой COMPETITION_TYPE_CHOICES на:

COMPETITION_TYPE_CHOICES = (
    ('', '---------'),
    ('1', 'Olympic Games'),
    ('2', 'ISU Championships'),
    ('3', 'Grand Prix Series'),
)

Ответ 7

Почему вы не используете ModelForm, если у вас уже есть класс модели?

Лучшее решение:

forms.py

class CompetitionSearchForm(ModelForm):

    class Meta:
        model = Competition

models.py

class Competition(models.Model):
    name = models.CharField(max_length=256)
    type = models.IntegerField(choices=COMPETITION_TYPE_CHOICES, default=COMPETITION_TYPE_CHOICES[0][0], blank=True)

Вы можете установить blank = False, чтобы удалить пустую_клавишу из списка