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

Posted

技术标签:

【中文标题】使用元组时,ChoiceField 不显示空标签【英文标题】:ChoiceField doesn't display an empty label when using a tuple 【发布时间】:2010-12-18 10:56:09 【问题描述】:

我想要做什么

我将在我的数据库中保存有关比赛的数据。我希望能够按特定条件搜索比赛——尤其是比赛类型。

关于比赛类型

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

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 中的选择小部件显示一个空标签,但我没有。对此的任何帮助将不胜感激:)

【问题讨论】:

也请参考this Question。 【参考方案1】:

我找到了一个解决方案,可以按照我想要的方式工作,而不会违反 DRY 原则。不是很干净,但我想它必须这样做。

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

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

所以我目前的解决方案是:

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)

模型保持原样。

【讨论】:

从 django.db.models.fields 导入 BLANK_CHOICE_DASH COMP_TYPE_CHOICES_AND_EMPTY = tuple(BLANK_CHOICE_DASH + list(COMP_TYPE_CHOICES))【参考方案2】:

我尝试了 Monika 和 Evgeniy 的解决方案都没有成功,但 Monika 有一个很好的观点,即选择不需要是元组。因此,最简单(也是最干燥)的解决方案是简单地做 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】:

根据文档:

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

所以,你可以简单:

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

【讨论】:

【参考方案5】:

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

否则只需将您的 COMPETITION_TYPE_CHOICES 更改为:

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

【讨论】:

谢谢,这个答案很有用(我没有想到你可以将值定义为'' - 所以我学到了一些东西:))。但不幸的是,这也不是我要寻找的答案......空白=真绝对不是我想要的,并且像你建议的那样更改元组有几个缺点:a)这意味着我有两个'-- ----' 现在管理员中的选项 b) 例如,如果我决定我想要搜索表单中的空标签,但不希望它出现在添加竞争表单中,那么我基本上就完蛋了。【参考方案6】:

只需对Evgeniy's answer 进行小改动,即可检查是否尚未添加空白替代项。

如果没有检查(至少在运行内置的 runserver 时),每次重新加载页面都会添加一个额外的空标签。

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, ('','---------' ) )

【讨论】:

+1 用于检查现有空白。不确定这是否是因为自 2010 年以来情况发生了变化,但我不得不使用 self.fields['type'].widget.choices【参考方案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 以从列表中删除 empty_label

【讨论】:

因为这是一个搜索表单 - 我必须重写很多内容,因此不需要或唯一的验证。否则每次有人尝试搜索时,django 都会说这样的竞争已经存在。【参考方案8】:

聚会有点晚了..

完全不修改选项并仅使用小部件处理它怎么样?

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>

【讨论】:

以上是关于使用元组时,ChoiceField 不显示空标签的主要内容,如果未能解决你的问题,请参考以下文章

关于python的元组操作

将自定义html添加到django中的choicefield标签

为什么在取消引用非元组时,对取消引用的引用元组的匹配不起作用?

为什么在尝试匹配元组时会出现不匹配的类型错误?

在递归二叉树函数中返回元组时遇到问题

将每个元素视为元组时,在 PySpark 中加入 2 个 RDD