如何指定表单中字段的顺序? [django-crispy-forms]
How do I specify the order of fields in a form? [django-crispy-forms]
我们正在为 Speedy Net 使用 Django 2.1。我们有一个联系表,最近垃圾邮件发送者使用它向我们发送垃圾邮件。我决定在表单中添加一个 "no_bots" 字段,以防止机器人成功提交表单。我检查了表格并且它有效,但问题是我们有 2 个站点 - 在一个站点 (Speedy Net) 上,字段的顺序是正确的,而在另一个站点 (Speedy Match) 上,字段的顺序不正确 - "no_bots" 字段位于 "message" 字段之前,但我希望它是最后一个字段。我如何让它持久?我们的模板标签仅包含 {% crispy form %}
并且我在 class Meta
:
中定义了字段的顺序
class FeedbackForm(ModelFormWithDefaults):
...
no_bots = forms.CharField(label=_('Type the number "17"'), required=True)
class Meta:
model = Feedback
fields = ('sender_name', 'sender_email', 'text', 'no_bots')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelperWithDefaults()
if (self.defaults.get('sender')):
del self.fields['sender_name']
del self.fields['sender_email']
del self.fields['no_bots']
self.helper.add_input(Submit('submit', pgettext_lazy(context=self.defaults['sender'].get_gender(), message='Send')))
else:
self.fields['sender_name'].required = True
self.fields['sender_email'].required = True
self.helper.add_layout(Row(
Div('sender_name', css_class='col-md-6'),
Div('sender_email', css_class='col-md-6'),
))
self.helper.add_input(Submit('submit', _('Send')))
def clean_text(self):
text = self.cleaned_data.get('text')
for not_allowed_string in self._not_allowed_strings:
if (not_allowed_string in text):
raise ValidationError(_("Please contact us by e-mail."))
return text
def clean_no_bots(self):
no_bots = self.cleaned_data.get('no_bots')
if (not (no_bots == "17")):
raise ValidationError(_("Not 17."))
return no_bots
顺便说一句,我检查了我们的暂存服务器,结果恰恰相反——字段顺序在 Speedy Match 中是正确的,但在 Speedy Net 中不正确。这很奇怪,因为它们都使用相同的代码!我认为这意味着字段的顺序是随机的。
更新:我删除了生产服务器上的所有*.pyc文件,现在两个站点的字段顺序都是正确的。我还在登台服务器上删除了这些文件,现在两个站点的字段顺序都不正确。我在登台服务器上又做了一次,其中一个站点的字段顺序又变了。
您尝试过使用 Layout 方法吗?
来自文档:
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Fieldset, ButtonHolder, Submit
class ExampleForm(forms.Form):
[...]
def __init__(self, *args, **kwargs):
super(ExampleForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
Fieldset(
'first arg is the legend of the fieldset',
'like_website',
'favorite_number',
'favorite_color',
'favorite_food',
'notes'
),
ButtonHolder(
Submit('submit', 'Submit', css_class='button white')
)
https://django-crispy-forms.readthedocs.io/en/latest/layouts.html#fundamentals
原因:遍历无序集合
crispy_forms
的 FormHelper.render_layout
这样做:
fields = set(form.fields.keys())
left_fields_to_render = fields - form.rendered_fields
for field in left_fields_to_render:
...
此时,left_fields_to_render
是一个set
:{'text', 'no_bots'}
一个set
是一个无序集合。
这有时 returns False
: [a for a in {'text', 'no_bots'}] == ['text', 'no_bots']
您可以通过打开 Python 解释器的几个不同实例来尝试 — 我注意到它通常在 Python 解释器的一个实例中是一致的。
修复:遍历列表
基本上:
fields = tuple(form.fields.keys())
left_fields_to_render = list_difference(fields, form.rendered_fields)
for field in left_fields_to_render:
...
我已在 django-crispy-forms/django-crispy-forms#952.
提交了一份 PR 以获得更完整的修复
解决方法:明确指定布局中的字段
可以说,如果您不设置 render_unmentioned_fields = True
,布局的预期用途。
您已经为四个字段中的两个调用了 self.helper.add_layout
;你可以一路走下去:
self.helper.add_layout(MultiWidgetField(
Row(
Div('sender_name', css_class='col-md-6'),
Div('sender_email', css_class='col-md-6'),
),
'text',
'no_bots',
))
我们正在为 Speedy Net 使用 Django 2.1。我们有一个联系表,最近垃圾邮件发送者使用它向我们发送垃圾邮件。我决定在表单中添加一个 "no_bots" 字段,以防止机器人成功提交表单。我检查了表格并且它有效,但问题是我们有 2 个站点 - 在一个站点 (Speedy Net) 上,字段的顺序是正确的,而在另一个站点 (Speedy Match) 上,字段的顺序不正确 - "no_bots" 字段位于 "message" 字段之前,但我希望它是最后一个字段。我如何让它持久?我们的模板标签仅包含 {% crispy form %}
并且我在 class Meta
:
class FeedbackForm(ModelFormWithDefaults):
...
no_bots = forms.CharField(label=_('Type the number "17"'), required=True)
class Meta:
model = Feedback
fields = ('sender_name', 'sender_email', 'text', 'no_bots')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelperWithDefaults()
if (self.defaults.get('sender')):
del self.fields['sender_name']
del self.fields['sender_email']
del self.fields['no_bots']
self.helper.add_input(Submit('submit', pgettext_lazy(context=self.defaults['sender'].get_gender(), message='Send')))
else:
self.fields['sender_name'].required = True
self.fields['sender_email'].required = True
self.helper.add_layout(Row(
Div('sender_name', css_class='col-md-6'),
Div('sender_email', css_class='col-md-6'),
))
self.helper.add_input(Submit('submit', _('Send')))
def clean_text(self):
text = self.cleaned_data.get('text')
for not_allowed_string in self._not_allowed_strings:
if (not_allowed_string in text):
raise ValidationError(_("Please contact us by e-mail."))
return text
def clean_no_bots(self):
no_bots = self.cleaned_data.get('no_bots')
if (not (no_bots == "17")):
raise ValidationError(_("Not 17."))
return no_bots
顺便说一句,我检查了我们的暂存服务器,结果恰恰相反——字段顺序在 Speedy Match 中是正确的,但在 Speedy Net 中不正确。这很奇怪,因为它们都使用相同的代码!我认为这意味着字段的顺序是随机的。
更新:我删除了生产服务器上的所有*.pyc文件,现在两个站点的字段顺序都是正确的。我还在登台服务器上删除了这些文件,现在两个站点的字段顺序都不正确。我在登台服务器上又做了一次,其中一个站点的字段顺序又变了。
您尝试过使用 Layout 方法吗?
来自文档:
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Fieldset, ButtonHolder, Submit
class ExampleForm(forms.Form):
[...]
def __init__(self, *args, **kwargs):
super(ExampleForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
Fieldset(
'first arg is the legend of the fieldset',
'like_website',
'favorite_number',
'favorite_color',
'favorite_food',
'notes'
),
ButtonHolder(
Submit('submit', 'Submit', css_class='button white')
)
https://django-crispy-forms.readthedocs.io/en/latest/layouts.html#fundamentals
原因:遍历无序集合
crispy_forms
的 FormHelper.render_layout
这样做:
fields = set(form.fields.keys())
left_fields_to_render = fields - form.rendered_fields
for field in left_fields_to_render:
...
此时,left_fields_to_render
是一个set
:{'text', 'no_bots'}
一个set
是一个无序集合。
这有时 returns False
: [a for a in {'text', 'no_bots'}] == ['text', 'no_bots']
您可以通过打开 Python 解释器的几个不同实例来尝试 — 我注意到它通常在 Python 解释器的一个实例中是一致的。
修复:遍历列表
基本上:
fields = tuple(form.fields.keys())
left_fields_to_render = list_difference(fields, form.rendered_fields)
for field in left_fields_to_render:
...
我已在 django-crispy-forms/django-crispy-forms#952.
提交了一份 PR 以获得更完整的修复解决方法:明确指定布局中的字段
可以说,如果您不设置 render_unmentioned_fields = True
,布局的预期用途。
您已经为四个字段中的两个调用了 self.helper.add_layout
;你可以一路走下去:
self.helper.add_layout(MultiWidgetField(
Row(
Div('sender_name', css_class='col-md-6'),
Div('sender_email', css_class='col-md-6'),
),
'text',
'no_bots',
))