Формы — один из ключевых механизмов веб-разработки. Через формы пользователи взаимодействуют с сайтом: ищут фильмы, добавляют данные, оставляют комментарии, фильтруют списки, отправляют сообщения и выполняют множество других действий.
В этом уроке мы впервые погрузимся в базовый фундамент работы HTML-форм, научимся отправлять данные на сервер методами GET и POST, обрабатывать эти данные в Django и выводить результат в браузере.
Все примеры будут связаны с проектом cinemahub и моделью Movie.
HTML-форма — это специальная разметка, которая позволяет пользователю вводить данные и отправлять их на сервер.
Например:
- Ввести название фильма и выполнить поиск.
- Добавить описание фильма.
- Выбрать год выпуска фильма.
- Отправить текстовый запрос на сервер.
Все формы создаются с помощью тега:
<form></form>А внутри формы размещаются элементы:
<input>— поля для ввода текста, числа, даты и др.<textarea>— многострочное текстовое поле.<select>— список с вариантами выбора.<button>— кнопка отправки формы.
Создадим простую страницу поиска. Форма будет отправлять данные методом GET, поэтому параметры появятся в URL.
Файл: templates/search_movie.html
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8" />
<title>Поиск фильма</title>
</head>
<body>
<h1>Поиск фильма</h1>
<form action="" method="get">
<label for="title">Название фильма:</label>
<input type="text" id="title" name="title" />
<button type="submit">Искать</button>
</form>
{% if movies %}
<h2>Результаты поиска:</h2>
<ul>
{% for movie in movies %}
<li>{{ movie.title }} ({{ movie.year }})</li>
{% endfor %}
</ul>
{% endif %}
</body>
</html>Что происходит?
method="get"— данные будут добавлены в URL.- При отправке формы URL станет таким:
http://localhost:8000/search?title=matrix
- Мы выводим список фильмов, если он передан в контекст.
Файл: views.py
from django.shortcuts import render
from movies.models import Movie
def search_movie_view(request):
title_query = request.GET.get('title') # достаём параметр из URL
movies = None
if title_query:
movies = Movie.objects.filter(title__icontains=title_query)
return render(request, 'search_movie.html', {'movies': movies})Пояснения:
request.GETиспользуется для получения данных, отправленных методом GET.title__icontains— ищем фильмы, содержащие введённый текст.- Если пользователь ещё ничего не отправил → список пустой.
Файл: urls.py
from django.urls import path
from .views import search_movie_view
urlpatterns = [
path('search/', search_movie_view, name='search-movie'),
]Открываем:
http://localhost:8000/search/
Вводим «matrix» → нажимаем «Искать».
Если модель Movie содержит такие фильмы — они появятся на странице.
❗ Частая ошибка:
Пустая страница без ошибок → скорее всего, забыли передать контекст {'movies': movies}.
Метод POST используется, когда:
- Данные не должны появляться в URL
- Мы создаём/изменяем данные
- Мы отправляем большие тексты (описание фильма)
Пример: создадим простейшую учебную форму добавления фильма.
В реальном проекте мы будем использовать Django Forms и ModelForm, но сейчас важно понять базовые механизмы «сырая HTML-форма → Django view».
Файл: templates/add_movie.html
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8" />
<title>Добавить фильм</title>
</head>
<body>
<h1>Добавление фильма</h1>
<form action="" method="post">
{% csrf_token %}
<label for="title">Название фильма:</label>
<input type="text" id="title" name="title" required />
<label for="year">Год выхода:</label>
<input
type="number"
id="year"
name="year"
min="1900"
max="2100"
required
/>
<label for="description">Описание фильма:</label>
<textarea id="description" name="description"></textarea>
<button type="submit">Добавить</button>
</form>
{% if message %}
<p>{{ message }}</p>
{% endif %}
</body>
</html>Важно:
method="post"— данные отправляются в теле запроса, не в URL.{% csrf_token %}— обязательная защита от CSRF-атак.- Добавлена валидация в форме (
required,min/max).
Файл: views.py
from django.shortcuts import render
from movies.models import Movie
def add_movie_view(request):
message = ""
if request.method == "POST":
title = request.POST.get('title')
year = request.POST.get('year')
description = request.POST.get('description')
# Простейшая проверка данных
if not title or not year:
message = "Заполните обязательные поля!"
else:
Movie.objects.create(
title=title,
year=year,
description=description
)
message = "Фильм успешно добавлен!"
return render(request, 'add_movie.html', {'message': message})Разбор:
request.POST— словарь данных, отправленных методом POST.- Проверяем обязательные поля.
- Создаём запись в БД.
- Выводим сообщение.
Файл: urls.py
path('add-movie/', add_movie_view, name='add-movie'),Переходим:
http://localhost:8000/add-movie/
Вводим данные → нажимаем «Добавить».
Если всё хорошо — получаем сообщение: «Фильм успешно добавлен!»
Ошибка:
“403 Forbidden (CSRF token missing)” → вы забыли {% csrf_token %}.
Файл: templates/filter_year.html
<h1>Фильтрация фильмов по году</h1>
<form method="get">
<label for="year">Год:</label>
<input type="number" id="year" name="year" />
<button type="submit">Фильтровать</button>
</form>
<ul>
{% for movie in movies %}
<li>{{ movie.title }} ({{ movie.year }})</li>
{% endfor %}
</ul>Файл: views.py
def filter_by_year_view(request):
year = request.GET.get('year')
movies = Movie.objects.all()
if year:
movies = movies.filter(year=year)
return render(request, 'filter_year.html', {'movies': movies})- Создать форму, которая принимает название фильма и выводит длину строки.
- Создать POST-форму, которая принимает описание фильма и выводит количество слов.
- Создать GET-форму выбора минимального года фильма и вывести фильмы, выпущенные позже этого года.
- Форма с названием и длиной.
Шаблон (templates/title_length.html)
<form method="get">
<input type="text" name="title" />
<button type="submit">Отправить</button>
</form>
{% if length %}
<p>Длина строки: {{ length }}</p>
{% endif %}View
def title_length_view(request):
title = request.GET.get('title')
length = len(title) if title else None
return render(request, 'title_length.html', {'length': length})- Форма количества слов в описании.
Шаблон
<form method="post">
{% csrf_token %}
<textarea name="text"></textarea>
<button type="submit">Посчитать</button>
</form>
{% if count %}
<p>Количество слов: {{ count }}</p>
{% endif %}View
def count_words_view(request):
count = None
if request.method == "POST":
text = request.POST.get('text', '')
count = len(text.split())
return render(request, 'count_words.html', {'count': count})- GET-форма для фильмов после определенного года.
Решение (view)
def filter_from_year_view(request):
year = request.GET.get('year')
movies = Movie.objects.all()
if year:
movies = movies.filter(year__gte=year)
return render(request, 'filter_from_year.html', {'movies': movies})- Чем отличается GET от POST?
- Где находятся данные, отправленные методом GET?
- Где находятся данные, отправленные методом POST?
- Зачем нужен CSRF-токен?
- Можно ли отправить форму POST без CSRF-токена?
- Через какой объект мы читаем GET-данные в Django?
- Через какой объект мы читаем POST-данные в Django?
- Что будет, если забыть передать контекст в render()?
- Когда данные появляются в URL?
- В каком случае стоит использовать POST?