How do I get Django forms to show the html required attribute?
I have this form field:
email = forms.EmailField(
required=True,
max_length=100,
)
It has the required attribute, but in the html it is not adding the html attribute required
. In fact it's not even using email
as the field type, it's using text
... though it appears to get max_length just fine.
Actual:
&l开发者_如何学JAVAt;input id="id_email" type="text" name="email" maxlength="100">
Expected:
<input id="id_email" type="email" name="email" maxlength="100" required="true">
How can I get Django to use the correct attributes in html forms?
Django form elements are written against <input />
as it exists in HTML 4, where type="text"
was the correct option for e-mail addresses. There was also no required="true"
.
If you want custom HTML attributes, you need the attrs
keyword argument to the widget. It would look something like this:
email = forms.EmailField(
max_length=100,
required=True,
widget=forms.TextInput(attrs={ 'required': 'true' }),
)
You can check out more documentation about widgets here. Discussion of attrs
is near the bottom of that page.
Regarding type="email"
, you might be able to send that to your attrs
dictionary and Django will intelligently override its default. If that isn't the result you get, then your route is to subclass forms.TextInput
and then pass it to the widget
keyword argument.
Combining Daniel and Daniel answers, I usually use this mixin for my forms:
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 += ' *'
So when i have to create a new form or modelform i just use:
class NewForm(HTML5RequiredMixin, forms.Form):
...
Since Django 1.10, this is built-in.
From the release notes:
Required form fields now have the required HTML attribute. Set the new Form.use_required_attribute attribute to False to disable it.
There's also the template-only solution using a filter. I recommend django-widget-tweaks
:
{% load widget_tweaks %}
{{ form.email|attr:'required:true' }}
That was easy.
Monkeypatching Widget is your best bet:
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
As you've realized, setting your Field required attribute to True
is only for backend validation, as explained in the Django documentation.
What you really want is to add a required attribute to the Widget of the field:
email.widget.attrs["required"] = "required"
But if you really want to write elegant, DRY code, you should make a base form class that dynamically looks for all your required fields and modifies their widget required attribute for you (you can name it whatever you wish, but "BaseForm" seems apt):
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"
And then have all your Form objects descend from it:
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)
精彩评论