【问题标题】:How do I get Django forms to show the html required attribute?如何让 Django 表单显示 html 必需的属性?
【发布时间】: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 表单中使用正确的属性?

【问题讨论】:

    标签: django forms 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
        猜你喜欢
        • 2019-07-02
        • 2021-05-06
        • 2015-05-09
        • 2017-06-27
        • 2013-06-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多