Как мне получить формы Django для отображения атрибута html?

У меня есть это поле формы:

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

Он имеет обязательный атрибут, но в html он не добавляет атрибут html required. На самом деле он даже не использует email в качестве типа поля, используя text... хотя кажется, что max_length просто отлично.

Actual:

<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

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 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

Ответ 2

Элементы формы Django записываются с помощью <input />, как он существует в HTML 4, где type="text" был правильной опцией для адресов электронной почты. Также не было required="true".

Если вам нужны пользовательские атрибуты HTML, вам понадобится аргумент ключевого слова attrs для виджета. Он будет выглядеть примерно так:

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

Вы можете посмотреть дополнительную документацию о виджетах здесь. Обсуждение attrs находится в нижней части этой страницы.

Что касается type="email", вы можете отправить его в словарь attrs, и Django разумно переопределит его значение по умолчанию. Если это не результат, который вы получите, тогда ваш маршрут будет подклассом forms.TextInput, а затем передать его в аргумент ключевого слова widget.

Ответ 3

Объединяя ответы Даниила и Дэниела, я обычно использую этот 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):
    ...

Ответ 4

Также существует только шаблонное решение с использованием фильтра. Я рекомендую django-widget-tweaks:

{% load widget_tweaks %}

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

Это было легко.

Ответ 5

Как вы поняли, установка атрибута Field required на True выполняется только для проверки бэкэнд, как описано в документации Django.

Что вы действительно хотите, так это добавить обязательный атрибут в поле Widget поля:

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

Но если вы действительно хотите написать элегантный, СУХОЙ код, вы должны создать базовый класс формы, который динамически ищет все необходимые поля и изменяет их необходимый атрибут виджета (вы можете называть его любым желаемым, но "BaseForm" кажется 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"

И затем все объекты вашей формы спускаются из него:

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)

Ответ 6

Так как Django 1.10, он встроен.

Из примечания к выпуску:

Обязательные поля формы теперь имеют требуемый атрибут HTML. Установите для нового атрибута Form.use_required_attribute значение False, чтобы отключить его.