Как отрендерить форму вручную
S0mebodyРасширение полей формы
Мы все еще можем копнуть глубже и расширить разметку {{field}} (или, если вы делаете это индивидуально, это будут, например, поля {{form.name}} или {{form.email}}). Но теперь все становится немного сложнее, потому что мы говорим о виджетах. Например, поле имени преобразуется в тег <input type = "text">, а поле электронной почты преобразуется в тег <input type = "email">, и, что еще более проблематично, поле сообщения преобразуется в <textarea> </textarea> тег.
На этом этапе Django использует небольшие шаблоны HTML для генерации выходного HTML полей.
Итак, давайте посмотрим, как это делает Django. Если мы откроем шаблоны text.html или email.html из папки виджетов, мы увидим, что он просто включает файл шаблона input.html:
{% include "django/forms/widgets/input.html" %}
Это говорит о том, что шаблон input.html, вероятно, является наиболее общим, особенности рендеринга могут быть внутри него. Итак, давайте посмотрим:
<input type="{{ widget.type }}"
name="{{ widget.name }}"
{% if widget.value != None %} value="{{ widget.value|stringformat:'s' }}"{% endif %}
{% include "django/forms/widgets/attrs.html" %} />
По сути, этот небольшой шаблон устанавливает тип ввода, это имя, которое используется для доступа к данным в объекте запроса. Например, вход с именем message, если он отправлен на сервер, доступен через request.POST ['message'].
По-прежнему в шаблоне input.html он также устанавливает текущее значение поля или оставляет его пустым, если данных нет. Это важный момент в шаблоне, потому что он сохраняет состояние формы после того, как она была отправлена и не была успешно обработана (форма недействительна).
Наконец, он включает шаблон attrs.html, который отвечает за установку таких атрибутов, как maxlength, required, placeholder, style или любого другого атрибута HTML. Это легко настраиваемое определение формы.
Если вам интересно узнать о attrs.html, вот как он выглядит:
<input type="text"
name="name"
id="id_name"
{% if form.name.value != None %}value="{{ form.name.value|stringformat:'s' }}"{% endif %}
maxlength="30"
required>
Или немного лучше:
<input type="text"
name="{{ form.name.name }}"
id="{{ form.name.id_for_label }}"
{% if form.name.value != None %}value="{{ form.name.value|stringformat:'s' }}"{% endif %}
maxlength="{{ form.name.field.max_length }}"
{% if form.name.field.required %}required{% endif %}>
Вы, наверное, уже догадались, что это не лучший способ работы с формами. И, возможно, вы также спрашиваете себя, почему иногда мы ссылаемся на определенный атрибут как на {{form.name. <something>}}, а в других ситуациях мы используем {{form.name.field. <something>}}.
Я не хочу сейчас вдаваться в подробности об этом, но в основном form.name - это экземпляр BoundField(field + data), а затем form.name.field - это определение поля, которое является экземпляром forms.CharField. Вот почему некоторые значения доступны в экземпляре привязанного поля, а другие - в определении поля char.
В любом определении формы __iter__ формы возвращает список экземпляров BoundField, аналогично методы visible_fields() и hidden_fields() также возвращают экземпляры BoundField. Теперь, если вы обращаетесь к form.fields, он относится к списку CharField, EmailField и всех других определений полей и т. Д. Если сейчас для вас слишком много информации, ничего страшного, вам не нужно об этом беспокоиться.
Использование кастомных атрибутов HTML
В некоторых случаях вам нужно добавить только дополнительный атрибут HTML, например класс, стиль или заполнитель. Вам не нужно расширять поле ввода, как в предыдущем примере. Вы можете сделать это прямо в определении формы:
forms.py
class ColorfulContactForm(forms.Form):
name = forms.CharField(
max_length=30,
widget=forms.TextInput(
attrs={
'style': 'border-color: blue;',
'placeholder': 'Write your name here'
}
)
)
email = forms.EmailField(
max_length=254,
widget=forms.EmailInput(attrs={'style': 'border-color: green;'})
)
message = forms.CharField(
max_length=2000,
widget=forms.Textarea(attrs={'style': 'border-color: orange;'}),
help_text='Write here your message!'
)

Далее мы собираемся изучить стороннюю библиотеку, которая может облегчить вашу жизнь.
Использование Django Widget Tweaks
Несмотря на то, что мы можем управлять настраиваемыми атрибутами HTML в определении формы, было бы намного лучше, если бы мы могли установить их непосредственно в шаблоне. В конце концов, атрибуты HTML относятся к представлению входных данных.
Библиотека django-widget-tweaks - подходящий инструмент для этой работы. Это позволяет вам сохранить настройки формы по умолчанию и просто добавить то, что вам нужно. Это очень удобно, особенно при работе с ModelForms, так как сокращает объем кода, который вам нужно писать для выполнения простых задач.
Вот краткое руководство по началу работы:
Сначала установите его с помощью pip:
pip установить django-widget-tweaks
Добавьте его в INSTALLED_APPS:
INSTALLED_APPS = [
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'widget_tweaks',
]
Загрузите его в шаблон:
{% load widget_tweaks %}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Simple is Better Than Complex</title>
</head>
<body>
...
</body>
И мы готовы его использовать! В основном мы будем использовать тег шаблона {% render_field%}. В следующем примере вы увидите, что мы можем просто поместить атрибуты, как если бы мы это делали с необработанным HTML:
<form method="post" novalidate>
{% csrf_token %}
{{ form.non_field_errors }}
{% for hidden_field in form.hidden_fields %}
{{ hidden_field.errors }}
{{ hidden_field }}
{% endfor %}
<table border="1">
{% for field in form.visible_fields %}
<tr>
<th>{{ field.label_tag }}</th>
<td>
{{ field.errors }}
{% render_field field style="border: 2px dashed red;" placeholder=field.name %}
{{ field.help_text }}
</td>
</tr>
{% endfor %}
</table>
<button type="submit">Submit</button>
</form>

Это очень удобно, особенно в тех случаях, когда вам просто нужно добавить класс CSS.
Рендеринг форм Bootstrap 4
Я буду использовать bootstrap 4, потому что так всем проще
В основном, чтобы использовать библиотеку Bootstrap 4, я просто включил ссылку CDN, которую они предоставляют:
<head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous"> <title>Simple is Better Than Complex</title> </head>
Давайте сначала сосредоточимся на представлении входных данных(input), мы вернемся к части ошибок позже. Вот как мы можем представить ту же форму с помощью тегов Bootstrap 4:
<form method="post" novalidate>
{% csrf_token %}
{% for hidden_field in form.hidden_fields %}
{{ hidden_field }}
{% endfor %}
{% for field in form.visible_fields %}
<div class="form-group">
{{ field.label_tag }}
{{ field }}
{% if field.help_text %}
<small class="form-text text-muted">{{ field.help_text }}</small>
{% endif %}
</div>
{% endfor %}
<button type="submit" class="btn btn-primary">Submit</button>
</form>

Поля ввода выглядят сломанными. Давайте исправим это с помощью того, что мы узнали в последнем разделе этой статьи:
{% load widget_tweaks %}
<form method="post" novalidate>
{% csrf_token %}
{% for hidden_field in form.hidden_fields %}
{{ hidden_field }}
{% endfor %}
{% for field in form.visible_fields %}
<div class="form-group">
{{ field.label_tag }}
{% render_field field class="form-control" %}
{% if field.help_text %}
<small class="form-text text-muted">{{ field.help_text }}</small>
{% endif %}
</div>
{% endfor %}
<button type="submit" class="btn btn-primary">Submit</button>
</form>

Намного лучше. Теперь давайте посмотрим на ситуацию с проверкой и ошибками. Я собираюсь использовать компонент предупреждения для ошибок, не связанных с полями, а для полей я просто поиграю с правильными классами CSS, которые предоставляет Bootstrap 4.
{% load widget_tweaks %}
<form method="post" novalidate>
{% csrf_token %}
{% for hidden_field in form.hidden_fields %}
{{ hidden_field }}
{% endfor %}
{% if form.non_field_errors %}
<div class="alert alert-danger" role="alert">
{% for error in form.non_field_errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
{% for field in form.visible_fields %}
<div class="form-group">
{{ field.label_tag }}
{% if form.is_bound %}
{% if field.errors %}
{% render_field field class="form-control is-invalid" %}
{% for error in field.errors %}
<div class="invalid-feedback">
{{ error }}
</div>
{% endfor %}
{% else %}
{% render_field field class="form-control is-valid" %}
{% endif %}
{% else %}
{% render_field field class="form-control" %}
{% endif %}
{% if field.help_text %}
<small class="form-text text-muted">{{ field.help_text }}</small>
{% endif %}
</div>
{% endfor %}
<button type="submit" class="btn btn-primary">Submit</button>
</form>
И вот результат:

Это очень круто, потому что он помечает зеленым цветом поля, прошедшие проверку:

Давайте внимательно посмотрим, что происходит. Мы можем улучшить код, но я предпочел оставить его таким, чтобы вы могли лучше понять логику отрисовки шаблона.
Сначала я вызываю метод form.is_bound. Он сообщает нам, есть ли в форме данные или нет. Когда мы впервые инициализируем форму form = ContactForm(), метод form.is_bound() вернет False. После отправки form.is_bound() вернет True. Итак, мы можем поиграть с ним, чтобы узнать, произошел ли уже процесс проверки или нет.
Затем, когда проверка уже произошла, я просто помечаю поле классом CSS .is-invalid и .is-valid, в зависимости от случая. Они отвечают за окрашивание компонентов формы в красный или зеленый цвет.
Повторное использование компонентов формы
Теперь мы можем скопировать существующий код во внешний файл и повторно использовать наш код для других форм.
includes/bs4_form.html
{% load widget_tweaks %}
{% for hidden_field in form.hidden_fields %}
{{ hidden_field }}
{% endfor %}
{% if form.non_field_errors %}
<div class="alert alert-danger" role="alert">
{% for error in form.non_field_errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
{% for field in form.visible_fields %}
<div class="form-group">
{{ field.label_tag }}
{% if form.is_bound %}
{% if field.errors %}
{% render_field field class="form-control is-invalid" %}
{% for error in field.errors %}
<div class="invalid-feedback">
{{ error }}
</div>
{% endfor %}
{% else %}
{% render_field field class="form-control is-valid" %}
{% endif %}
{% else %}
{% render_field field class="form-control" %}
{% endif %}
{% if field.help_text %}
<small class="form-text text-muted">{{ field.help_text }}</small>
{% endif %}
</div>
{% endfor %}
Тогда теперь наше определение формы могло бы быть таким же простым, как:
<form method="post" novalidate>
{% csrf_token %}
{% include 'includes/bs4_form.html' with form=form %}
<button type="submit" class="btn btn-primary">Submit</button>
</form>
Например, используя приведенный выше фрагмент кода, мы используем его для обработки UserCreationForm, которая является встроенной формой, которая находится внутри модуля django.contrib.auth. Ниже результат:

Выводы
Надеюсь, вы узнали что-то новое или получили удовольствие от чтения этих статей. Если у вас могут возникнуть вопросы или вы хотите продолжить обсуждение этой темы, оставьте комментарий в нашем чате!