Skip to content

Latest commit

 

History

History
406 lines (290 loc) · 11.9 KB

File metadata and controls

406 lines (290 loc) · 11.9 KB

Модуль 7. Урок 40. Использование форм, не связанных с моделями в Django

Формы — один из важнейших инструментов в Django. До этого мы создавали «сырые» HTML-формы вручную, указывая <input>, <textarea>, <form method="post"> и т.д. Такой подход даёт базовое понимание механизма, но имеет существенные минусы:

  • валидировать данные приходится вручную;
  • нет автоматической генерации HTML-кода;
  • ошибки валидации нужно писать самому;
  • нет удобной привязки к полям модели;
  • много однотипного кода.

Django решает эти проблемы с помощью встроенной системы форм.


1. Что такое форма, не связанная с моделью

Иногда нам нужно обрабатывать данные, не создавая запись в БД.

Несколько примеров из проекта cinemahub:

  • форма поиска фильма;
  • форма отправки отзыва (не сохраняем, а отправляем на email);
  • форма фильтрации списка фильмов;
  • форма проверки данных (например, поле «ваше имя» — просто для приветствия на сайте).

Для таких задач создаются обычные формы — классы, которые наследуются от forms.Form.

Эти формы можно использовать почти так же, как ModelForm, но они не создают и не обновляют записи в базе данных.


2. Где объявлять формы

По договорённости Django-сообщества (и по лучшей практике) формы хранятся в файле:

your_app/
    forms.py

В проекте cinemahub у нас есть приложение movies. Поэтому создадим файл:

📄 Файл: movies/forms.py

from django import forms

Этот файл будет хранить все формы, которые относятся к логике приложения movies.


3. Создаём первую форму: AddMovieSimpleForm

Представим задачу: нужно сделать тестовую форму, которая собирает от пользователя информацию о фильме, но не сохраняет её в базу.

Например, мы хотим проверить, может ли студент корректно заполнить форму с минимальной валидацией.

📄 Файл: movies/forms.py

from django import forms

class AddMovieSimpleForm(forms.Form):
    title = forms.CharField(
        max_length=255,
        label="Название фильма"
    )
    year = forms.IntegerField(
        min_value=1900,
        max_value=2100,
        label="Год выхода"
    )
    description = forms.CharField(
        required=False,
        widget=forms.Textarea,
        label="Описание фильма"
    )
    is_featured = forms.BooleanField(
        required=False,
        label="Отображать на главной странице"
    )

Разберём поля:

📌 Что здесь важно

  • CharField → обычное текстовое поле.
  • IntegerField → Django автоматически проверяет, что это число.
  • min_value / max_value → границы значений — год не может быть 1450 или 3000.
  • Textarea → виджет для многострочного текста.
  • BooleanField → чекбокс.
  • required=False → поле необязательное.

Форма уже умеет:

  • строить HTML;
  • валидировать значения;
  • показывать ошибки.

4. Отображение формы в представлении

Мы создадим отдельное представление, которое:

  • показывает форму при GET-запросе;
  • принимает данные и валидирует их при POST-запросе;
  • выводит результат.

📄 Файл: movies/views.py

from django.shortcuts import render
from .forms import AddMovieSimpleForm

def add_movie_simple_view(request):
    result = None

    if request.method == "POST":
        form = AddMovieSimpleForm(request.POST)

        if form.is_valid():
            result = form.cleaned_data
        else:
            # Форма не прошла валидацию — Django сам покажет ошибки
            pass
    else:
        form = AddMovieSimpleForm()

    return render(request, "movies/add_movie_simple.html", {
        "form": form,
        "result": result,
    })

Что здесь происходит:

Пошаговый разбор

  1. Страница открылась → это GET → делаем пустую форму.
  2. Пользователь отправил форму → это POST → создаём форму с данными.
  3. is_valid() запускает встроенную валидацию Django.
  4. Если всё хорошо → результат лежит в cleaned_data.
  5. Ошибки Django выводит автоматически в шаблоне.

5. Шаблон для формы

Формы Django выводятся автоматически — нам остаётся лишь подключить их в HTML.

📄 Файл: movies/templates/movies/add_movie_simple.html

<!DOCTYPE html>
<html lang="ru">
  <head>
    <meta charset="UTF-8" />
    <title>Учебная форма добавления фильма</title>
  </head>
  <body>
    <h1>Учебная форма добавления фильма</h1>

    <form action="" method="post">
      {% csrf_token %} {{ form.as_p }}

      <button type="submit">Отправить</button>
    </form>

    {% if result %}
    <h2>Результат:</h2>
    <pre>{{ result }}</pre>
    {% endif %}
  </body>
</html>

📌 Объяснения

  • {{ form.as_p }} — Django сам создаёт HTML <p>…</p> для каждого поля.
  • Ошибки валидации выводятся автоматически рядом с полем.
  • Внизу мы выводим результат обработанных данных (cleaned_data).

6. Маршрут

📄 Файл: movies/urls.py

from django.urls import path
from .views import add_movie_simple_view

urlpatterns = [
    path("add-simple/", add_movie_simple_view, name="add-movie-simple"),
]

7. Проверяем результат в браузере

  1. Запускаем сервер:
python manage.py runserver
  1. Переходим:
http://localhost:8000/movies/add-simple/
  1. Заполняем форму.
  2. Нажимаем «Отправить».

🎉 Если всё заполнено корректно → внизу страницы появится:

{'title': 'Matrix', 'year': 1999, 'description': '...', 'is_featured': True}

Возможные ошибки

"Enter a whole number" — пользователь ввёл текст в поле year.

"Ensure this value is less than or equal to 2100" — год слишком большой.

Форма не отображается Проверь:

  • что указал правильный путь к шаблону;
  • что приложение movies есть в INSTALLED_APPS;
  • что URL включён в корневой urls.py.

Практические задания

  1. Создать форму, которая принимает название фильма и жанр (строкой), а затем выводит их в результат.

  1. Создать форму с 1 полем: year — число от 1900 до 2025. Если число корректное, выводить: "Вы выбрали год: X"

  1. Создать форму, которая принимает email и текст отзыва (textarea). Форма не должна ничего сохранять — просто выводить результат.

Сравнить решение

  1. Форма для названия фильма и жанра.

📄 movies/forms.py

class MovieGenreForm(forms.Form):
    title = forms.CharField(max_length=255, label="Название фильма")
    genre = forms.CharField(max_length=100, label="Жанр")

📄 movies/views.py

def movie_genre_view(request):
    result = None

    if request.method == "POST":
        form = MovieGenreForm(request.POST)
        if form.is_valid():
            result = form.cleaned_data
    else:
        form = MovieGenreForm()

    return render(request, "movies/movie_genre.html", {
        "form": form,
        "result": result,
    })

📄 movies/templates/movies/movie_genre.html

<form method="post">
  {% csrf_token %} {{ form.as_p }}
  <button type="submit">Отправить</button>
</form>

{% if result %}
<p>{{ result.title }} — жанр: {{ result.genre }}</p>
{% endif %}

  1. Форма с проверкой года.

📄 forms.py

class YearCheckForm(forms.Form):
    year = forms.IntegerField(
        min_value=1900,
        max_value=2025,
        label="Год"
    )

📄 views.py

def year_check_view(request):
    result = None
    if request.method == "POST":
        form = YearCheckForm(request.POST)
        if form.is_valid():
            result = form.cleaned_data["year"]
    else:
        form = YearCheckForm()

    return render(request, "movies/year_check.html", {
        "form": form,
        "result": result,
    })

📄 year_check.html

<form method="post">
  {% csrf_token %} {{ form.as_p }}
  <button>Проверить</button>
</form>

{% if result %}
<p>Вы выбрали год: {{ result }}</p>
{% endif %}

  1. Форма для вывода результата текста и email.

📄 forms.py

class FeedbackForm(forms.Form):
    email = forms.EmailField(label="Ваш email")
    message = forms.CharField(widget=forms.Textarea, label="Сообщение")

📄 views.py

def feedback_view(request):
    result = None
    if request.method == "POST":
        form = FeedbackForm(request.POST)
        if form.is_valid():
            result = form.cleaned_data
    else:
        form = FeedbackForm()

    return render(request, "movies/feedback.html", {
        "form": form,
        "result": result,
    })

📄 feedback.html

<form method="post">
  {% csrf_token %} {{ form.as_p }}
  <button>Отправить</button>
</form>

{% if result %}
<p>Email: {{ result.email }}</p>
<p>Сообщение: {{ result.message }}</p>
{% endif %}

Вопросы

  1. Чем форма forms.Form отличается от ModelForm?
  2. Где правильно хранить формы в Django-проекте?
  3. Что делает метод is_valid()?
  4. Какие данные находятся в form.cleaned_data?
  5. Что произойдёт, если убрать {% csrf_token %}?
  6. Как Django выводит ошибки валидации?
  7. Что делает атрибут required=False?
  8. Для чего используются виджеты (Textarea, CheckboxInput и т.д.)?

Предыдущий урок | Следующий урок