Для создания и обновление сущности в CRUD, нужно принять данные от пользователя. Для этого существуют формы - элемент взаимодействия между пользователем и веб-приложением. Основная цель форм это собирать данные от пользователей структурированным и безопасным способом. Когда вы регистрируетесь на сайте, оставляете комментарий, делаете заказ в интернет-магазине - все эти данные проходят через формы.
Но создавать формы вручную утомительно. Сотни строк одинакового кода, обработка ошибок, защита от атак — всё это придется делать постоянно.
Обычно у фреймворков есть встроенная поддержка генерации форм. Она состоит из набора функций, которые автоматизируют рутину. Django не является исключением. Он предоставляет ряд инструментов и библиотек, которые помогут создавать формы для отправки пользователем информации, а затем обрабатывать и отвечать на нее. В этом уроке разберем, как генерировать формы.
Django Forms
В Django формы выполняют несколько важных функций:
- Генерация HTML-разметки - Django может автоматически создавать HTML-код для форм.
- Валидация данных - формы проверяют корректность введенных данных. Например, является ли email действительным адресом электронной почты, достаточно ли длинный пароль, заполнены ли обязательные поля.
- Безопасность - Django-формы защищают от распространенных атак и помогают предотвратить внедрение вредоносного кода.
В Django существует два способа создания форм: создать форму с нуля или использовать ModelForm
, форму на базе существующей модели.
Сперва рассмотрим первый. Представим, что мы хотим добавить возможность оставлять комментарии к статьям на нашем блоге. Для ввода комментария нам понадобится форма.
Создадим файл forms.py
внутри приложения. В этом файле, подобно аналогичным файлам с сущностями models.py или views.py, мы будем писать все формы нашего приложения:
hexlet_django_blog
└── article
└── forms.py
Теперь добавим следующий код:
from django import forms # Импортируем формы Django
class CommentArticleForm(forms.Form):
content = forms.CharField(label='Комментарий', max_length=200) # Текст комментария
В коде выше мы определили класс нашей формы, используя базовый Django класс Form
, подобно тому как мы это делали с вью. В классе мы указали поле content
типа CharField
, встроенного типа для текстовых данных. Также мы добавили для поля человекочитаемый лейбл label=Комментарий
и ограничение на максимальную длин поля.
Когда Django отрендерит форму, то она превратится в следующий HTML:
<label for="id_content">Комментарий:</label>
<textarea name="content" cols="40" rows="10" maxlength="200" required id="id_content"></textarea>
Заметьте, что HTML не включает тег <form>
или кнопку отправки. Нам нужно самим добавить их в шаблон.
Теперь создадим вью с использованием формы:
from django.shortcuts import render
from .forms import CommentArticleForm
class CommentArticleView(View):
# если метод POST, то мы обрабатываем данные
def post(self, request, *args, **kwargs):
form = CommentArticleForm(request.POST) # Получаем данные формы из запроса
if form.is_valid(): # Проверяем данные формы на корректность
comment = Comment(
name = form.cleaned_data['content'], # Получаем очищенные данные из поля content
) # и создаем новый комментарий
comment.save()
# если метод GET, то создаем пустую форму
def get(self, request, *args, **kwargs):
form = CommentArticleForm() # Создаем экземпляр нашей формы
return render(request, 'comment.html', {'form': form}) # Передаем нашу форму в контексте
Порядок работы с формами следующий - если пользователь запрашивает вью методом GET, то создаем пустую форму и отправляем ее в шаблоне. Если пользователь отправляет данные через POST, то получаем данные из формы, проверяем их корректность методом .is_valid()
и затем используем эти данные для создания нового комментария.
Наконец, создадим шаблон с нашей формой:
<form action="{% url 'comment_create' %}" method="post">
{% csrf_token %}
<table border="1">
{{ form }}
</table>
<input type="submit" value="Сохранить">
</form>
В шаблоне для отображения формы достаточно указать только переменную {{ form }}
. Django подставит вместо нее используемую форму. Мы также обязательно добавляем {% csrf_token %}
в форму. Данная инструкция встраивает скрытое поле со случайным кодом, который проверяется Django и защищает от CSRF-атак.
Django ModelForm
Второй подход в создании форм - создание на основе существующей модели. Для этого в Django есть вспомогательный класс, который позволяет создавать Form-класс из модели Django:
# models.py
from django.db import models
class ArticleComment(models.Model):
content = models.CharField('content', max_length=100)
# forms.py
from django.forms import ModelForm
class ArticleCommentForm(ModelForm):
class Meta:
model = ArticleComment
fields = ['content']
Такой класс при генерации будет иметь все перечисленные поля в атрибуте fields
указанной модели в атрибуте model
.
Обработка ModelForm
отличается от Forms
только тем, что в ModelForm
у нас уже есть связь с моделью. Поэтому мы можем после проверки формы сразу перейти к сохранению данных:
class ArticleCommentFormView(View):
def post(self, request, *args, **kwargs):
form = ArticleCommentForm(request.POST) # Получаем данные формы из запроса
if form.is_valid(): # Проверяем данных формы на корректность
form.save() # Сохраняем форму
Если нам нужно дополнительно заполнить или обработать поля формы, то мы можем указать Django, что данные не нужно сразу сохранять:
class ArticleCommentFormView(View):
def post(self, request, *args, **kwargs):
form = ArticleCommentForm(request.POST) # Получаем данные формы из запроса
if form.is_valid(): # Проверяем данные формы на корректность
comment = form.save(commit=False) # Получаем заполненную модель
# Дополнительно обрабатываем модель
comment.content = check_for_spam(form.data['content'])
comment.save()
В данном примере мы при помощи commit=False
говорим Django, что данные пока сохранять не нужно, и получаем объект нашей модели. После дополнительной обработки модели ее необходимо сохранить с помощью вызова метода .save()
.
Валидация форм
Одно из правил веба - не доверять вводимым данным от пользователей. В этом нам помогают формы как промежуточный слой в приложении между вью (клиентской частью) и моделью (серверной частью). Слой форм позволяет проверять, валидировать, данные пользователей, защищая данные нашего приложения и их цельность.
В Django для валидации используется метод .is_valid()
, который запускает многоэтапный метод проверки формы:
- Базовая валидация
У каждого тип поля формы (CharField
, EmailField
, IntegerField
и т.д.) есть свои встроенные правила валидации:
class ArticleForm(forms.Form):
title = forms.CharField(max_length=100) # Проверяет длину
email = forms.EmailField() # Проверяет формат email
age = forms.IntegerField(min_value=0) # Проверяет, что число >= 0
- Проверка обязательных полей
Django проверяет, заполнены ли все обязательные поля:
class ArticleForm(forms.Form):
title = forms.CharField(required=True) # Должно быть заполнено
subtitle = forms.CharField(required=False) # Может быть пустым
- Пользовательская валидация на уровне поля
Мы можем добавить свои правила валидации для отдельных полей через метод clean_fieldname
:
class ArticleForm(forms.Form):
title = forms.CharField(max_length=100)
def clean_title(self):
title = self.cleaned_data['title']
if title.lower() == 'test':
raise forms.ValidationError("Заголовок не может быть 'test'")
return title
- Валидация на уровне формы
Метод clean()
может валидировать несколько полей сразу, это позволяет проверять взаимозависимости между полями. Например, частая ситуация проверки подтверждения пароля:
class PasswordChangeForm(forms.Form):
password = forms.CharField(widget=forms.PasswordInput)
password_confirm = forms.CharField(widget=forms.PasswordInput)
def clean(self):
cleaned_data = super().clean()
password = cleaned_data.get('password')
password_confirm = cleaned_data.get('password_confirm')
if password and password_confirm and password != password_confirm:
raise forms.ValidationError("Пароли не совпадают")
return cleaned_data
- Валидация ModelForm
В случае ModelForm
Django валидирует по ограничениям полям модели:
class Article(models.Model):
title = models.CharField(max_length=100, unique=True)
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = ['title']
# is_valid() проверит уникальность title автоматически
Когда вызывается метод is_valid()
, то выполнятся по порядку все проверки выше. После валидации мы уже можем получить очищенные данные так и словарь всех ошибок:
if form.is_valid():
# Данные валидны
form.cleaned_data # Очищенные данные
else:
form.errors # Все ошибки
form.errors['title'] # Ошибки конкретного поля
Дополнительные материалы

Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.