Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
520 views
in Technique[技术] by (71.8m points)

python - 如何指定表单中字段的顺序? [django-crispy-forms](How do I specify the order of fields in a form? [django-crispy-forms])

We are using Django 2.1 for Speedy Net . (我们将Django 2.1用于Speedy Net 。) We have a contact form and recently it has been used by spammers to send us spam. (我们有一个联系表格,最近,垃圾邮件发送者已使用该表格向我们发送垃圾邮件。) I decided to add a "no_bots" field to the form where I'm trying to prevent bots from submitting the form successfully. (我决定在表单中添加一个“ no_bots”字段,以防止机器人成功提交表单。) I checked the form and it works, but the problem is we have 2 sites - on one site (Speedy Net) the order of the fields is correct, and on the other site (Speedy Match) the order of the fields is not correct - the "no_bots" field comes before the "message" field but I want it to be the last field. (我检查了表格,它可以正常工作,但是问题是我们有2个站点-一个站点(Speedy Net)的字段顺序正确,而另一个站点(Speedy Match)的字段顺序不正确- “ no_bots”字段位于“ message”字段之前,但我希望它是最后一个字段。) How do I make it last? (我如何使它持久?) Our template tag contains just {% crispy form %} and I defined the order of the fields in class Meta : (我们的模板标记仅包含{% 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

快速网 快速比赛

By the way, I checked our staging server, and there it's the opposite - the order of fields is correct in Speedy Match but not correct in Speedy Net. (顺便说一句,我检查了我们的登台服务器,情况恰好相反-字段顺序在快速匹配中是正确的,但在快速网络中是不正确的。) This is weird because they both use the same code! (这很奇怪,因为它们都使用相同的代码!) I think it means that the order of the fields is random. (我认为这意味着字段的顺序是随机的。)

Update: I deleted all the *.pyc files on the production server, and now the order of fields is correct in both sites. (更新:我删除了生产服务器上的所有* .pyc文件,现在两个站点中的字段顺序都是正确的。) I also deleted these files on the staging server, and now the order of the fields is not correct in both sites. (我还删除了登台服务器上的这些文件,现在两个站点中的字段顺序都不正确。) I did it again on the staging server and the order of the fields changed again in one of the sites. (我在登台服务器上再次执行此操作,并且其中一个站点中的字段顺序再次更改。)

  ask by Uri translate from so

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

The cause: iterating over an unordered collection (原因:遍历无序集合)

crispy_forms 's FormHelper.render_layout does this: (crispy_formsFormHelper.render_layout为此:)

fields = set(form.fields.keys())
left_fields_to_render = fields - form.rendered_fields
for field in left_fields_to_render:
    ...

At this point, left_fields_to_render is a set : {'text', 'no_bots'} (此时, left_fields_to_render是一个set{'text', 'no_bots'})

A set is an unordered collection. (set是无序集合。)

This sometimes returns False : [a for a in {'text', 'no_bots'}] == ['text', 'no_bots'] (有时会返回False[a for a in {'text', 'no_bots'}] == ['text', 'no_bots'])

You can try this by opening several different instances of the Python interpreter — I noticed that it's usually consistent within an instance of a Python interpreter. (您可以通过打开几个不同的Python解释器实例来尝试这一操作-我注意到它通常在Python解释器实例中是一致的。)

The fix: iterate over a list (解决方法:遍历列表)

Basically: (基本上:)

fields = tuple(form.fields.keys())
left_fields_to_render = list_difference(fields, form.rendered_fields)
for field in left_fields_to_render:
    ...

I have submitted a PR for a more complete fix at django-crispy-forms/django-crispy-forms#952 . (我已经提交了django-crispy-forms / django-crispy-forms#952的PR,以获取更完整的修复程序。)

The workaround: explicitly specify the fields in the layout (解决方法:在布局中显式指定字段)

Arguably, the intended usage of layout if you don't set render_unmentioned_fields = True . (如果没有设置render_unmentioned_fields = True ,则可以说是布局的预期用途。)

You already call self.helper.add_layout for two of the four fields; (您已经为四个字段中的两个调用了self.helper.add_layout ;) you can just go all the way: (您可以一路走下去:)

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...