In order to be sure I force users to pick a valid value from a dropdown (rather than unknowingly leaving the first option in the list set without changing it to the correct value), I am inserting the blank choice field in many required form fields.
为了确保强制用户从下拉列表中选择一个有效的值(而不是无意中保留列表集中的第一个选项而不将其更改为正确的值),我在许多必填表单域中插入了空白的选项域。
models.py
Models.py
class MyModel(models.Model):
gender = models.CharField(max_length=1, null=True, blank=False, choices=GENDER_CHOICES, default='')
Original forms.py (Explicitly defining the form field. This works.)
原始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__'
Now, because I am doing this for many fields, I tried to revise my earlier code in forms.py:
现在,因为我要对许多字段执行此操作,所以我尝试在forms.py中修改前面的代码:
Revised forms.py (Setting the choices in __init__()
. Does not work.)
修订了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__'
In this case, I do not get the dashes as the first choice in the dropdown.
在本例中,我没有将破折号作为下拉列表中的第一选择。
I tried to look into the problem by using pdb
in the template to inspect the form field right before it was outputted. The debugger shows element.field.choices
to be (u'', u'---------'), ('f', 'Female'), ...]
in both cases. Nevertheless, the code that outputs the form field inserts the dashes only in the original code, not in the revised code.
我试图通过在模板中使用PDB在输出之前检查表单域来研究这个问题。调试器显示element.field。选项为(u‘’,u‘-’),(‘f’,‘Female’),...]在这两种情况下。但是,输出表单域的代码只在原始代码中插入破折号,而不在修订后的代码中插入。
I tried stepping through the Django code that renders the form field to figure out if it was using some other field besides .choices
that I'm not aware of, but I never stepped into the right part of the code.
我尝试单步执行呈现表单域的Django代码,以确定它是否使用了我不知道的其他字段,但我从未进入代码的正确部分。
How can I accomplish this? Although it works fine the first way, the revised code is much more DRY. If only I could make it work!
我怎样才能做到这一点呢?尽管第一种方法运行良好,但修订后的代码要枯燥得多。要是我能让它起作用就好了!
更多回答
Is there a reason you aren't just using the built-in empty_label
?
为什么不使用内置的EMPTY_LABEL呢?
@solarissmoke Because the empty choice isn't showing up by default because of the way the model is written. Hence the need to force in the empty choice.
@solarismoke,因为由于模型的编写方式,默认情况下不会显示空选项。因此,有必要强行做出空洞的选择。
This appears to be a bug in ChoiceWidget.__deepcopy__()
.
这似乎是ChoiceWidget.__DeepCopy__()中的错误。
These work:
这些工作包括:
# 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
Explanation
TypedChoiceField.choices
, inherited from ChoiceField
, is a property
:
从ChoiceField继承而来的TyedChoiceField.Chotions是一个属性:
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()
assigns self.widget.choices
in addition to self._choices
.
- The widget
choices
is the one used in the rendering.
- The widget
choices
is supposed to be the same instance as that in the field.
The field and widget are deepcopy'ed as follows:
字段和小工具的深度复制如下:
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__()
does not memo
ize the copy of self.choices
.
- The copy of
choices
is then unavailable to the deepcopy
in ChoiceField
.
- The widget
choices
is no longer the same instance as that in the field.
更多回答
我是一名优秀的程序员,十分优秀!