Зарегистрируйтесь, чтобы продолжить обучение

Работа с формами Python: Разработка на фреймворке Django

Для создания и обновление сущности в 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']  # Ошибки конкретного поля

Дополнительные материалы

  1. Документация

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы
профессия
от 25 000 ₸ в месяц
Разработка веб-приложений на Django
10 месяцев
с нуля
Старт 20 февраля

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»