使用元组时,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 不显示空标签的主要内容,如果未能解决你的问题,请参考以下文章
将自定义html添加到django中的choicefield标签