Django ModelForm:将空白选项插入多个表单字段

问题描述 投票:0回答:1

为了确保我强制用户从下拉列表中选择有效值(而不是不知不觉地保留列表集中的第一个选项而不将其更改为正确的值),我在许多必填表单字段中插入空白选择字段.

模型.py

class MyModel(models.Model):
    gender = models.CharField(max_length=1, null=True, blank=False, choices=GENDER_CHOICES, default='')

原始 forms.py (显式定义表单字段。这有效。)

from django.db.models.fields import BLANK_CHOICE_DASH
class MyForm(forms.ModelForm):
    gender = forms.TypedChoiceField(required=True, choices=BLANK_CHOICE_DASH+MyModel._meta.get_field('gender').choices)
    class Meta:
        model = MyModel
        fields = '__all__'

现在,因为我在很多领域都这样做,所以我尝试修改 forms.py 中的早期代码:

修订forms.py(设置

__init__()
中的选择。不起作用。)

class MyForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        for type in ('gender', ...):
            self.fields[type].choices.insert(0, (u'',u'---------' ))
    # Not needed anymore.. replaced by code in __init__()
    # gender = forms.TypedChoiceField(required=True, choices=BLANK_CHOICE_DASH+MyModel._meta.get_field('gender').choices)
    class Meta:
        model = MyModel
        fields = '__all__'

在这种情况下,我没有将破折号作为下拉列表中的第一选择。

我尝试通过在模板中使用

pdb
在输出表单字段之前检查表单字段来调查问题。调试器在
both
情况下显示
element.field.choices
(u'', u'---------'), ('f', 'Female'), ...]。然而,输出表单字段的代码仅在原始代码中插入破折号,而不是在修改后的代码中插入破折号。

我尝试单步执行呈现表单字段的 Django 代码,以弄清楚它是否使用了除了我不知道的

.choices
之外的其他字段,但我从未进入过代码的正确部分。

我怎样才能做到这一点?虽然第一种方法工作得很好,但修改后的代码更加干燥。如果我能让它发挥作用就好了!

django django-forms django-templates
1个回答
0
投票

这似乎是

ChoiceWidget.__deepcopy__()
中的错误。

这些工作:

# self.fields[name].choices.insert(0, (u'', u'---------'))
self.fields[name].widget.choices.insert(0, (u'', u'---------'))
# or
self.fields[name].choices = BLANK_CHOICE_DASH + self.fields[name].choices

说明

TypedChoiceField.choices
,继承自
ChoiceField
,是一个
property
:

class TypedChoiceField(ChoiceField):
class ChoiceField(Field):
    ...

    def _get_choices(self):
        return self._choices
    
    def _set_choices(self, value):
        # Setting choices also sets the choices on the widget.
        # ...
        if callable(value):
            value = CallableChoiceIterator(value)
        else:
            value = list(value)
    
        self._choices = self.widget.choices = value
    
    choices = property(_get_choices, _set_choices)
    除了
  • _set_choices()
     之外,
    self.widget.choices
    还分配
    self._choices
  • 小部件
    choices
    是渲染中使用的小部件。
    • 但是,它应该与现场的实例相同。

字段和小部件被深度复制如下:

class ChoiceField(Field):
    widget = Select
    ...

    def __deepcopy__(self, memo):
        result = super().__deepcopy__(memo)
        result._choices = copy.deepcopy(self._choices, memo)
        return result
class Select(ChoiceWidget):
class ChoiceWidget(Widget):
    ...

    def __deepcopy__(self, memo):
        obj = copy.copy(self)
        obj.attrs = self.attrs.copy()
        obj.choices = copy.copy(self.choices)
        memo[id(self)] = obj
        return obj
  • ChoiceWidget.__deepcopy__()
    不会
    memo
    ize
    self.choices
    的副本。
    • choices
      中的
      deepcopy
      无法使用
      ChoiceField
      的副本。
© www.soinside.com 2019 - 2024. All rights reserved.