如何让 Django 表单显示 html 必需的属性?

Posted

技术标签:

【中文标题】如何让 Django 表单显示 html 必需的属性?【英文标题】:How do I get Django forms to show the html required attribute? 【发布时间】:2011-11-25 15:05:30 【问题描述】:

我有这个表单域:

email = forms.EmailField(
  required=True,
  max_length=100,
)

它具有 required 属性,但在 html 中它没有添加 html 属性required。事实上,它甚至没有使用 email 作为字段类型,它使用的是 text... 虽然它似乎得到了 max_length 就好了。

实际:

<input id="id_email" type="text" name="email" maxlength="100">

预期:

<input id="id_email" type="email" name="email" maxlength="100" required="true">

如何让 Django 在 html 表单中使用正确的属性?

【问题讨论】:

【参考方案1】:

从 Django 1.10 开始,这是内置的。

来自release notes:

必填表单字段现在具有 required HTML 属性。将新的 Form.use_required_attribute 属性设置为 False 以禁用它。

【讨论】:

非常感谢,很长一段时间我都无法设置此属性。【参考方案2】:

如您所见,将 Field required 属性设置为 True 仅用于后端验证,如 Django documentation 中所述。

您真正想要的是在字段的Widget 中添加一个必需的属性:

email.widget.attrs["required"] = "required"

但是,如果您真的想编写优雅的 DRY 代码,您应该创建一个基本表单类,它可以动态查找所有必填字段并为您修改它们的小部件所需属性(您可以随意命名,但是“BaseForm " 似乎很贴切):

from django.forms import ModelForm

class BaseForm(ModelForm):
    def __init__(self, *args, **kwargs):
        super(BaseForm, self).__init__(*args, **kwargs)
        for bound_field in self:
            if hasattr(bound_field, "field") and bound_field.field.required:
                bound_field.field.widget.attrs["required"] = "required"

然后让你所有的 Form 对象都从它那里继承下来:

class UserForm(BaseForm):
    class Meta:
        model = User
        fields = []

    first_name = forms.CharField(required=True)
    last_name = forms.CharField(required=True)
    email = forms.EmailField(required=True, max_length=100)

【讨论】:

【参考方案3】:

Monkeypatching Widget 是您最好的选择:

from django.forms.widgets import Widget
from django.contrib.admin.widgets import AdminFileWidget
from django.forms import HiddenInput, FileInput

old_build_attrs = Widget.build_attrs

def build_attrs(self, extra_attrs=None, **kwargs):
    attrs = old_build_attrs(self, extra_attrs, **kwargs)

    # if required, and it's not a file widget since those can have files
    # attached without seeming filled-in to the browser, and skip hidden "mock"
    # fileds created for StackedInline and TabbedInline admin stuff
    if (self.is_required
            and type(self) not in (AdminFileWidget, HiddenInput, FileInput)
            and "__prefix__" not in attrs.get("name", "")):
        attrs['required'] = 'required'

    return attrs

Widget.build_attrs = build_attrs

【讨论】:

我是否建议您将 if 语句更改为:if self.is_required and type(self) not in (AdminFileWidget,HiddenInput, FileInput) and "__prefix__" not in attrs["name"]: 这将阻止浏览器在对象已附加文件时添加required="true",并跳过为 StackedInline 和 TabbedInline 管理内容创建的隐藏“模拟”字段。【参考方案4】:

还有使用过滤器的纯模板解决方案。我推荐django-widget-tweaks

% load widget_tweaks %

 form.email|attr:'required:true' 

这很容易。

【讨论】:

【参考方案5】:

结合 Daniel 和 Daniel 的答案,我通常将这个 mixin 用于我的表单:

from django.contrib.admin.widgets import AdminFileWidget
from django.forms.widgets import HiddenInput, FileInput


class HTML5RequiredMixin(object):

    def __init__(self, *args, **kwargs):
        super(HTML5RequiredMixin, self).__init__(*args, **kwargs)
        for field in self.fields:
            if (self.fields[field].required and
               type(self.fields[field].widget) not in
                    (AdminFileWidget, HiddenInput, FileInput) and 
               '__prefix__' not in self.fields[field].widget.attrs):

                    self.fields[field].widget.attrs['required'] = 'required'
                    if self.fields[field].label:
                        self.fields[field].label += ' *'

所以当我必须创建一个新表单或模型表单时,我只需使用:

class NewForm(HTML5RequiredMixin, forms.Form):
    ...

【讨论】:

在我的例子中,我得到了 'Form object has no attribute 'fields'' 因为 python 从左到右评估 'init' 并且 form 对象在HTML5RequiredMixin 'init' 调用的时间。在我像这样更改类声明的顺序以及 NewForm 的 init 的一部分之后,它就起作用了。 ' class NewForm(forms.Form, HTML5RequiredMixin) ',然后NewForm的init,调用'HTML5RequiredMixin.__init__'【参考方案6】:

Django 表单元素是针对&lt;input /&gt; 编写的,因为它存在于 HTML 4 中,其中type="text" 是电子邮件地址的正确选项。也没有required="true"

如果您想要自定义 HTML 属性,您需要 widgetattrs 关键字参数。它看起来像这样:

email = forms.EmailField(
    max_length=100,
    required=True,
    widget=forms.TextInput(attrs= 'required': 'true' ),
)

您可以查看有关小部件here 的更多文档。 attrs 的讨论位于该页面的底部附近。

关于type="email",您可以将其发送到您的attrs 字典,Django 将智能地覆盖其默认值。如果这不是您得到的结果,那么您的路线是继承 forms.TextInput,然后将其传递给 widget 关键字参数。

【讨论】:

有没有办法给表单小部件基类打补丁?必须指定两次所需的属性似乎非常不干燥。 我想。这是django.forms.BaseForm

以上是关于如何让 Django 表单显示 html 必需的属性?的主要内容,如果未能解决你的问题,请参考以下文章

如何从 Django 表单中删除必需的属性

如何让所有 django allauth 表单和对话框显示在弹出窗口中

如何使子类化的自定义 Django 表单字段不再是必需的?

如何让来自 Ajax 的 Django 表单错误出现在用户屏幕上?

如何让我的表单在 Django 中工作?

如何使用 html 文件在 django 中显示 ChoiceField?