使用元组时,ChoiceField不显示空标签

时间:2009-11-19 19:05:26

标签: python django django-forms

我正在尝试做什么

我将在我的数据库中保存有关比赛的数据。我希望能够通过某些标准搜索比赛 - 尤其是比赛类型。

关于比赛类型

比赛类型保存在元组中。一个稍微缩短的例子:

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中的select小部件显示一个空标签,但我没有得到一个。任何有关这方面的帮助将非常感激:)

8 个答案:

答案 0 :(得分:36)

我找到了一种解决方案,可以按照我想要的方式运行而不违反DRY原则。不是很干净,但我认为这是必须的。

根据the documentation选择,不必是元组:

  

最后,请注意选择可以是任何选择   可迭代对象 - 不一定是   列表或元组。这可以让你构建   动态选择。但如果你找到了   自己黑客入选   动态,你可能会更好   使用适当的数据库表   ForeignKey的。选择是为了   静态数据变化不大,   如果有的话。

所以我现在要解决的问题是:

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)

该模型保持原样。

答案 1 :(得分:27)

我尝试了Monika和Evgeniy的解决方案都没有成功,但Monika有一个很好的观点,即选择不需要是元组。因此,最简单(和DRYest)的解决方案就是简单地完成Django在Model Field中的功能。在将它们转换为列表后,只需将空白选项和元组一起添加:

from django.db.models.fields import BLANK_CHOICE_DASH

...

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

答案 2 :(得分:9)

更好的选择是以 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, ('','---------' ) )

答案 3 :(得分:7)

根据文件:

  

要使用2元组的可迭代(例如,列表或元组)作为此字段的选项,或使用返回此类可迭代的可调用对象。 (https://docs.djangoproject.com/en/dev/ref/forms/fields/

所以,你可以很简单:

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

答案 4 :(得分:6)

只需对Evgeniy's 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 :(得分:4)

尝试将blank=True添加到模型字段(假设这是您想要的行为),然后将表单更改为ModelForm并删除字段定义。请注意,验证或保存模型时,不需要为其设置blank=True的任何字段。同样,这可能不是你想要的,但是如果它允许Django自动处理一些事情。

否则只需将COMPETITION_TYPE_CHOICES更改为:

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

答案 6 :(得分:0)

如果您已经拥有模型类,为什么不使用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 以从列表中删除empty_label

答案 7 :(得分:0)

聚会晚一点。

完全不修改选择,而仅使用小部件处理呢?

from django.db.models import BLANK_CHOICE_DASH

class EmptySelect(Select):
    empty_value = BLANK_CHOICE_DASH[0]
    empty_label = BLANK_CHOICE_DASH[1]

    @property
    def choices(self):
        yield (self.empty_value, self.empty_label,)
        for choice in self._choices:
            yield choice

    @choices.setter
    def choices(self, val):
        self._choices = val

然后称呼它:

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

这就是你最终得到的:

print(CompetitionSearchForm().as_p())
<p>
    <label for="id_name">Name:</label>
    <input id="id_name" name="name" type="text" />
</p>
<p>
    <label for="id_type">Type:</label>
    <select id="id_type" name="type">
        <option value="" selected="selected">------</option>
        <option value="1">Olympic Games</option>
        <option value="2">ISU Championships</option>
        <option value="3">Grand Prix Series</option>
    </select>
</p>