为了确保我强制用户从下拉列表中选择有效值(而不是不知不觉地保留列表集中的第一个选项而不将其更改为正确的值),我在许多必填表单字段中插入空白选择字段.
模型.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
之外的其他字段,但我从未进入过代码的正确部分。
我怎样才能做到这一点?虽然第一种方法工作得很好,但修改后的代码更加干燥。如果我能让它发挥作用就好了!
这似乎是
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
的副本。