Views/Templates/URLs

⏱ 25 мин · 🧩 заданий: 6

Три кита 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

Вы не вошли — ответы проверяются, но прогресс не сохранится. Войти или зарегистрироваться.
Задание 1 Написать код

Напишите view book_detail(request, pk): она получает книгу по pk (или отдаёт 404 через get_object_or_404) и рендерит шаблон library/book_detail.html с переменной book.

Задание 2 Один вариант ответа

Что вернёт get_object_or_404(Book, pk=999), если книги нет?

Задание 3 Расставить по порядку

Пользователь открывает /books/3/. Расставьте этапы.

  1. render() заполняет шаблон book_detail.html
  2. Браузер получает HTML-страницу книги
  3. config/urls.py отрезает префикс и передаёт остаток в library/urls.py
  4. Маршрут <int:pk>/ совпадает, вызывается book_detail(request, pk=3)
  5. View получает книгу через get_object_or_404
Задание 4 Написать код

Допишите view book_list: если в адресе есть параметр ?q=..., отфильтруйте книги по вхождению q в название (без учёта регистра).

Задание 5 Один вариант ответа

Как во view узнать, каким методом пришёл запрос?

Задание 6 Ввод ответа

Каким декоратором ограничить view так, чтобы она принимала только POST-запросы? (имя декоратора)

Войдите, чтобы отмечать пройденные уроки.