2011-09-26 52 views
14

我有这样的表单字段:如何获取Django表单以显示html必需属性?

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

它需要的属性,但在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表单正确的属性?

回答

6

的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 
+3

嘿谢谢我! –

+4

我可能会建议你将if语句改为:如果self.is_required和type(self)不在(AdminFileWidget,HiddenInput,FileInput)和“__prefix__”不在attrs [“name”]中:''''当对象已经附加了一个文件时,浏览器添加'required =“true”',并且跳过为StackedInline和TabbedInline管理员创建的隐藏“模拟”字段。 –

16

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

如果您想要自定义HTML属性,您需要attrs关键字参数到小部件。这将是这个样子:

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

您可以检查出小部件here更多的文档。关于attrs的讨论接近该页面的底部。

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

+0

有没有办法让猴子补丁窗体小部件基类?看起来非常不干的必须指定两次必需的属性。 –

+0

我想。它是'django.forms.BaseForm'。 –

7

结合丹尼尔和丹尼尔的回答,我通常使用这个混入了我的形式:

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 += ' *' 

所以,当我要创建一个新的形式或modelform我只是使用:

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

在我的情况下,我得到'表单对象没有属性'字段'',因为python从左到右评估'__init__',并且在HTML5RequiredMixin'__init__'调用时表单对象不可用。在我改变类声明的顺序之后,它和NewForm的__init__部分工作。 'NewForm(forms.Form,HTML5RequiredMixin)',然后__init__ NewForm,调用'HTML5RequiredMixin .__ init__' – Hobaak

5

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

{% load widget_tweaks %} 

{{ form.email|attr:'required:true' }} 

很简单。

4

正如您已经意识到的那样,将您的Field required属性设置为True仅用于后端验证,如Django documentation中所述。

你真正想要的是一个必需的属性添加到域的Widget

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

但如果你真的想写优雅,DRY的代码,你应该做一个基本形式的类,动态外观您的所有必填字段,并修改自己的小部件所需的属性为您(你可以命名为任何你想要的,但“基本形式”似乎贴切):

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" 

,然后让所有的表单对象从中下降:

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

由于Django 1.10,这是内置的。

release notes

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

+0

非常感谢,我一直在麻烦设置此属性相当长的时间。 – quapka

相关问题