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

8tntrjer  于 2022-11-26  发布在  Go
关注(0)|答案(9)|浏览(202)

我想做的是
我将在我的数据库中保存比赛的数据,我希望能够通过特定的标准来搜索比赛--特别是比赛类型。

关于竞争类型

竞争类型保存在一个元组中。

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

up9lanfz

up9lanfz1#

我找到了一个解决方案,它可以按照我想要的方式工作,而不违反干燥原则。不是很干净,但我想它必须这样做。
根据文档选择不必是元组:
最后,注意choices可以是任何可迭代的对象--不一定是列表或元组。这允许您动态地构造choices。但是,如果您发现自己将choices修改为动态的,那么最好使用带有ForeignKey的适当数据库表。choices适用于不会发生太大变化的静态数据。
所以我现在的解决方案是:

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)

模型保持不变。

4xrmg8kj

4xrmg8kj2#

我尝试了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)
s3fp2yjn

s3fp2yjn3#

更好的选择是更新forminit方法中的字段选项

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

yzckvree4#

根据文件:
用作此字段的选择的2元组的可迭代对象(例如,列表或元组),或返回此类可迭代对象的可调用对象。(https://docs.djangoproject.com/en/dev/ref/forms/fields/
所以,可以简单的说:

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

jw5wzhpr5#

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

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

qzwqbdag6#

只是对叶夫根尼的答案做了一个小改动,检查是否还没有添加空白选项。
如果不进行检查(至少在运行内置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, ('','---------' ) )
smdnsysy

smdnsysy7#

如果你已经有了模型类,为什么不使用ModelForm呢?
最佳解决方案:

表单.py

class CompetitionSearchForm(ModelForm):

    class Meta:
        model = Competition

模型.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

qyyhg6bp

qyyhg6bp8#

  • 派对有点晚了 *

完全不修改选择,只用一个小部件处理它怎么样?

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>
tuwxkamq

tuwxkamq9#

延伸哈维尔的答案。
与其自定义choices的签名(这会导致mypy检查失败),不如使用自定义属性并仅在显示选项中更改它。

class EmptySelect(Select):
    @property
    def custom_choices(self):
        yield BLANK_CHOICE_DASH[0]
        yield from self.choices

    def optgroups(self, name, value, attrs=None):
        """Return a list of optgroups for this widget."""
        groups = []
        has_selected = False
        # START_CHANGES
        for index, (option_value, option_label) in enumerate(self.custom_choices):
            # END_CHANGES
            if option_value is None:
                option_value = ""

            subgroup = []
            if isinstance(option_label, (list, tuple)):
                group_name = option_value
                subindex = 0
                choices = option_label
            else:
                group_name = None
                subindex = None
                choices = [(option_value, option_label)]
            groups.append((group_name, subgroup, index))

            for subvalue, sublabel in choices:
                selected = str(subvalue) in value and (not has_selected or self.allow_multiple_selected)
                has_selected |= selected
                subgroup.append(
                    self.create_option(
                        name,
                        subvalue,
                        sublabel,
                        selected,
                        index,
                        subindex=subindex,
                        attrs=attrs,
                    )
                )
                if subindex is not None:
                    subindex += 1
        return groups

并在任何地方使用此小工具,例如:

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

注意:不要使用type作为文件名,因为它是python内置的关键字,而应将其命名为其他名称,以获得良好的实践。

相关问题