Views/Templates/URLs
Три кита Django
Мы по отдельности разобрали URL, представления, шаблоны и модели. Теперь соберём из них мини-приложение — каталог библиотеки со списком книг, страницей книги и поиском.
Модели
library/models.py (из прошлых уроков):
class Author(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name="books")
pages = models.PositiveIntegerField(default=0)
description = models.TextField(blank=True)
class Meta:
ordering = ["title"]
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("library:book_detail", args=[self.pk])
Маршруты
library/urls.py:
from django.urls import path
from . import views
app_name = "library"
urlpatterns = [
path("", views.book_list, name="book_list"),
path("<int:pk>/", views.book_detail, name="book_detail"),
path("authors/<int:pk>/", views.author_detail, name="author_detail"),
]
config/urls.py:
urlpatterns = [
path("admin/", admin.site.urls),
path("books/", include("library.urls")),
]
Представления
library/views.py:
from django.shortcuts import get_object_or_404, render
from .models import Author, Book
def book_list(request):
books = Book.objects.select_related("author")
q = request.GET.get("q", "").strip()
if q:
books = books.filter(title__icontains=q)
return render(request, "library/book_list.html", {"books": books, "q": q})
def book_detail(request, pk):
book = get_object_or_404(Book.objects.select_related("author"), pk=pk)
return render(request, "library/book_detail.html", {"book": book})
def author_detail(request, pk):
author = get_object_or_404(Author, pk=pk)
return render(request, "library/author_detail.html", {
"author": author,
"books": author.books.all(),
})
get_object_or_404
Вместо
try:
book = Book.objects.get(pk=pk)
except Book.DoesNotExist:
raise Http404("Книга не найдена")
пишем одну строку get_object_or_404(Book, pk=pk). Первым аргументом можно передать и QuerySet — так мы добавили select_related. Есть и get_list_or_404() для списков.
Шаблоны
templates/base.html — как в уроке про наследование. library/templates/library/book_list.html:
{% extends "base.html" %}
{% block title %}Книги{% endblock %}
{% block content %}
<h1>Книги</h1>
<form method="get">
<input type="search" name="q" value="{{ q }}" placeholder="Поиск по названию">
<button>Найти</button>
</form>
{% if q %}<p>Результаты по запросу «{{ q }}»: {{ books|length }}</p>{% endif %}
<ul>
{% for book in books %}
<li>
<a href="{{ book.get_absolute_url }}">{{ book.title }}</a>
— <a href="{% url 'library:author_detail' book.author.pk %}">{{ book.author }}</a>
</li>
{% empty %}
<li>Ничего не найдено.</li>
{% endfor %}
</ul>
{% endblock %}
library/templates/library/book_detail.html:
{% extends "base.html" %}
{% block title %}{{ book.title }}{% endblock %}
{% block content %}
<h1>{{ book.title }}</h1>
<p>Автор: <a href="{% url 'library:author_detail' book.author.pk %}">{{ book.author }}</a></p>
<p>Страниц: {{ book.pages }}</p>
{{ book.description|linebreaks }}
<a href="{% url 'library:book_list' %}">← Все книги</a>
{% endblock %}
Объект request
Во view доступна вся информация о запросе:
| Атрибут | Что содержит |
|---|---|
request.method |
"GET", "POST"… |
request.GET |
параметры из адреса (?q=...) |
request.POST |
данные формы |
request.FILES |
загруженные файлы |
request.user |
текущий пользователь |
request.path |
путь /books/3/ |
request.headers |
заголовки |
request.COOKIES, request.session |
куки и сессия |
Одна view — два метода
Частый шаблон: GET показывает форму, POST её обрабатывает.
from django.shortcuts import redirect
from django.views.decorators.http import require_http_methods
@require_http_methods(["GET", "POST"])
def add_author(request):
if request.method == "POST":
name = request.POST.get("name", "").strip()
if name:
Author.objects.create(name=name)
return redirect("library:book_list")
return render(request, "library/add_author.html")
Ручная обработка request.POST быстро становится громоздкой: нужна валидация, сообщения об ошибках, повторное заполнение полей. Для этого в Django есть формы — тема следующего урока.
Другие типы ответов
from django.http import HttpResponse, JsonResponse, FileResponse
HttpResponse("текст", content_type="text/plain")
JsonResponse({"status": "ok", "count": 5})
FileResponse(open("report.pdf", "rb"), as_attachment=True)
HttpResponse(status=204)
Кстати, задания на этом сайте проверяются именно так: браузер отправляет ответ, view возвращает JsonResponse с результатом.
Итог
- URL → view → (модель) → шаблон → ответ — базовый цикл Django.
get_object_or_404превращает «объект не найден» в страницу 404.request.GET— параметры поиска и фильтров,request.method— метод запроса.- Ответ — не только HTML:
JsonResponse,FileResponse, редиректы.
Практика решено 0 из 6
Напишите view book_detail(request, pk): она получает книгу по pk
(или отдаёт 404 через get_object_or_404) и рендерит шаблон library/book_detail.html с переменной book.
from django.shortcuts import get_object_or_404, render
from .models import Book
def book_detail(request, pk):
book = get_object_or_404(Book, pk=pk)
return render(request, "library/book_detail.html", {"book": book})
Что вернёт get_object_or_404(Book, pk=999), если книги нет?
Пользователь открывает /books/3/. Расставьте этапы.
-
render()заполняет шаблонbook_detail.html - Браузер получает HTML-страницу книги
-
config/urls.pyотрезает префикс и передаёт остаток вlibrary/urls.py -
Маршрут
<int:pk>/совпадает, вызываетсяbook_detail(request, pk=3) -
View получает книгу через
get_object_or_404
Допишите view book_list: если в адресе есть параметр ?q=...,
отфильтруйте книги по вхождению q в название (без учёта регистра).
def book_list(request):
books = Book.objects.all()
q = request.GET.get("q")
if q:
books = books.filter(title__icontains=q)
return render(request, "library/book_list.html", {"books": books, "q": q})
Как во view узнать, каким методом пришёл запрос?
Каким декоратором ограничить view так, чтобы она принимала только POST-запросы? (имя декоратора)
Войдите, чтобы отмечать пройденные уроки.