320 lines
10 KiB
Markdown
320 lines
10 KiB
Markdown
---
|
||
status: processing
|
||
type: concept
|
||
tags:
|
||
- django
|
||
- routing
|
||
- templates
|
||
- cbv
|
||
- fbv
|
||
created: 2026-05-30
|
||
updated: 2026-06-05
|
||
source:
|
||
- '[[lec2-django_lec_urls_views_templates-1.pdf]]'
|
||
- '[[lec3-django_cbv-1.pdf]]'
|
||
- '[[p4-django_cbv-1.pdf]]'
|
||
aliases:
|
||
- 'Django: Представления, шаблоны и CBV'
|
||
---
|
||
|
||
# Django: Представления, шаблоны и CBV
|
||
|
||
## Маршрутизация (URL Routing)
|
||
- Описывается в `urlpatterns` с помощью `path()` и `re_path()`.
|
||
- Конвертеры: `<int:pk>`, `<slug:slug>`, `<uuid:id>`.
|
||
- Включение других файлов: `path('blog/', include('blog.urls'))`.
|
||
- Реверсирование: `{% url 'name' %}` в шаблонах или `reverse('name')` в Python.
|
||
|
||
Конвертеры вытаскивают часть URL, проверяют её тип и передают во view как аргумент:
|
||
- `<str:name>` — непустая строка без `/`; используется по умолчанию.
|
||
- `<int:pk>` — целое положительное число.
|
||
- `<slug:slug>` — строка для человекочитаемого URL: буквы, цифры, `_`, `-`.
|
||
- `<uuid:id>` — UUID, например `075194d3-6885-417e-a8a8-6c931e272f00`.
|
||
- `<path:subpath>` — строка, которая может содержать `/`.
|
||
|
||
Маршрутизация связывает URL с view-функцией или view-классом. Имя маршрута (`name`) нужно, чтобы не хардкодить URL в коде и шаблонах.
|
||
|
||
```python
|
||
# project/urls.py
|
||
from django.urls import include, path
|
||
|
||
urlpatterns = [
|
||
path("blog/", include("blog.urls")),
|
||
]
|
||
```
|
||
|
||
```python
|
||
# blog/urls.py
|
||
from django.urls import path
|
||
|
||
from . import views
|
||
|
||
app_name = "blog"
|
||
|
||
urlpatterns = [
|
||
path("", views.post_list, name="post_list"),
|
||
path("<int:pk>/", views.post_detail, name="post_detail"),
|
||
path("tag/<slug:slug>/", views.posts_by_tag, name="posts_by_tag"),
|
||
path("files/<path:subpath>/", views.file_detail, name="file_detail"),
|
||
]
|
||
```
|
||
|
||
```python
|
||
def post_detail(request, pk):
|
||
# pk уже int
|
||
...
|
||
|
||
|
||
def posts_by_tag(request, slug):
|
||
# slug — строка из URL
|
||
...
|
||
```
|
||
|
||
```python
|
||
from django.urls import reverse
|
||
|
||
url = reverse("blog:post_detail", kwargs={"pk": 1})
|
||
```
|
||
|
||
`path()` обычно принимает:
|
||
|
||
- `route` — строка маршрута;
|
||
- `view` — функция или `ClassView.as_view()`;
|
||
- `kwargs` — дополнительные аргументы view;
|
||
- `name` — имя маршрута для `{% url %}` и `reverse()`.
|
||
|
||
`app_name` задаёт namespace приложения. Тогда маршрут вызывается как `blog:post_detail`, а не просто `post_detail`.
|
||
|
||
---
|
||
|
||
## Представления (Views)
|
||
Views принимают `HttpRequest` и возвращают `HttpResponse`.
|
||
|
||
View — это точка входа для запроса. В ней обычно читают данные из БД, проверяют условия и возвращают HTML, JSON, редирект или ошибку.
|
||
|
||
### Функциональные представления (FBV)
|
||
FBV — обычная функция. Удобна, когда логика короткая и нестандартная.
|
||
|
||
```python
|
||
from django.shortcuts import render, get_object_or_404
|
||
|
||
def index(request):
|
||
latest_items = Model.objects.all()[:5]
|
||
return render(request, "app/index.html", {"items": latest_items})
|
||
```
|
||
|
||
У `render()` первым аргументом должен идти `request`:
|
||
|
||
```python
|
||
return render(request, "index.html", context)
|
||
```
|
||
|
||
```python
|
||
from django.shortcuts import get_object_or_404, render
|
||
|
||
from .models import Post
|
||
|
||
def post_detail(request, pk):
|
||
post = get_object_or_404(Post, pk=pk)
|
||
return render(request, "blog/detail.html", {"post": post})
|
||
```
|
||
|
||
### Классовые представления (CBV)
|
||
Абстрагируют типичные задачи. Вызываются в URL через `.as_view()`.
|
||
|
||
- **TemplateView:** Отображение статического шаблона.
|
||
- **ListView:** Автоматический список объектов (данные в `object_list`).
|
||
- **DetailView:** Детальная информация об одном объекте (ищет по `pk` или `slug`).
|
||
|
||
CBV — класс с готовым поведением. Django сам вызывает нужные методы класса: например, `get()` для GET-запроса или `get_queryset()` для получения списка объектов.
|
||
|
||
**Пример ListView:**
|
||
```python
|
||
from django.views.generic import DetailView, ListView, TemplateView
|
||
|
||
from .models import Post
|
||
|
||
|
||
class AboutView(TemplateView):
|
||
template_name = "blog/about.html"
|
||
|
||
|
||
class PostListView(ListView):
|
||
model = Post
|
||
template_name = "blog/list.html"
|
||
context_object_name = "posts"
|
||
paginate_by = 10
|
||
|
||
def get_queryset(self):
|
||
return Post.objects.filter(is_published=True).order_by("-created_at")
|
||
|
||
|
||
class PostDetailView(DetailView):
|
||
model = Post
|
||
template_name = "blog/detail.html"
|
||
context_object_name = "post"
|
||
```
|
||
|
||
Если в `ListView` не указать ни `model`, ни `queryset`, Django не поймёт, какие объекты показывать, и выдаст ошибку.
|
||
|
||
Стандартные имена в контексте:
|
||
|
||
- `ListView` — `object_list` и дополнительно `<model>_list`, например `post_list`;
|
||
- `DetailView` — `object` и дополнительно `<model>`, например `post`.
|
||
|
||
Методы, которые часто переопределяют:
|
||
|
||
- `get_queryset()` — изменить список объектов;
|
||
- `get_object()` — изменить получение одного объекта;
|
||
- `get_context_data()` — добавить данные в шаблон.
|
||
|
||
При переопределении `get_context_data()` обычно сначала вызывают базовую логику:
|
||
|
||
```python
|
||
def get_context_data(self, **kwargs):
|
||
context = super().get_context_data(**kwargs)
|
||
context["title"] = "Посты"
|
||
return context
|
||
```
|
||
|
||
Паджинация в `ListView` включается через `paginate_by = 10`. В шаблоне появляются `page_obj`, `paginator`, `is_paginated`.
|
||
|
||
Шаблоны по умолчанию:
|
||
|
||
- `ListView` ищет `<app>/<model>_list.html`;
|
||
- `DetailView` ищет `<app>/<model>_detail.html`.
|
||
|
||
```python
|
||
# blog/urls.py
|
||
from django.urls import path
|
||
|
||
from .views import AboutView, PostDetailView, PostListView
|
||
|
||
app_name = "blog"
|
||
|
||
urlpatterns = [
|
||
path("about/", AboutView.as_view(), name="about"),
|
||
path("", PostListView.as_view(), name="post_list"),
|
||
path("<int:pk>/", PostDetailView.as_view(), name="post_detail"),
|
||
]
|
||
```
|
||
|
||
---
|
||
|
||
## Шаблоны (Templates)
|
||
Используют **Django Template Language (DTL)**.
|
||
|
||
### Синтаксис
|
||
- **Переменные:** `{{ var.attr }}`.
|
||
- **Теги:** `{% if %}`, `{% for %}`, `{% url %}`, `{% csrf_token %}`.
|
||
- **Фильтры:** `{{ date|date:"Y-m-d" }}`, `{{ list|length }}`, `{{ text|lower }}`.
|
||
|
||
Шаблон получает контекст из view. Например, если view передала `{"posts": posts}`, в шаблоне доступна переменная `posts`.
|
||
|
||
Если обратиться к переменной, которой нет в контексте, Django-шаблон обычно выведет пустую строку, а не Python-ошибку.
|
||
|
||
```html
|
||
{% for post in posts %}
|
||
<article>
|
||
<h2>
|
||
<a href="{% url 'blog:post_detail' post.pk %}">
|
||
{{ post.title|upper }}
|
||
</a>
|
||
</h2>
|
||
|
||
{% if post.is_published %}
|
||
<p>{{ post.created_at|date:"Y-m-d" }}</p>
|
||
{% endif %}
|
||
</article>
|
||
{% empty %}
|
||
<p>Постов нет.</p>
|
||
{% endfor %}
|
||
```
|
||
|
||
### Наследование шаблонов
|
||
Позволяет избежать дублирования HTML-кода.
|
||
- **Базовый шаблон (`base.html`):** Определяет блоки `{% block content %}{% endblock %}`.
|
||
- **Дочерний шаблон:** Использует `{% extends "base.html" %}` и переопределяет блоки.
|
||
|
||
```html
|
||
<!-- templates/base.html -->
|
||
<!doctype html>
|
||
<html lang="ru">
|
||
<head>
|
||
<title>{% block title %}Сайт{% endblock %}</title>
|
||
</head>
|
||
<body>
|
||
<main>
|
||
{% block content %}{% endblock %}
|
||
</main>
|
||
</body>
|
||
</html>
|
||
```
|
||
|
||
```html
|
||
<!-- templates/blog/list.html -->
|
||
{% extends "base.html" %}
|
||
|
||
{% block title %}Посты{% endblock %}
|
||
|
||
{% block content %}
|
||
<h1>Посты</h1>
|
||
|
||
{% for post in posts %}
|
||
<a href="{% url 'blog:post_detail' post.pk %}">
|
||
{{ post.title }}
|
||
</a>
|
||
{% endfor %}
|
||
{% endblock %}
|
||
```
|
||
|
||
---
|
||
|
||
## Статические файлы (Static)
|
||
- Подключаются через `{% load static %}`.
|
||
- Настройки в `settings.py`: `STATIC_URL`, `STATICFILES_DIRS`.
|
||
- В продакшне используется команда `python manage.py collectstatic`.
|
||
|
||
Статика — это CSS, JavaScript, изображения и другие файлы, которые не генерируются view.
|
||
|
||
```python
|
||
# settings.py
|
||
STATIC_URL = "static/"
|
||
STATICFILES_DIRS = [BASE_DIR / "static"]
|
||
```
|
||
|
||
```html
|
||
{% load static %}
|
||
|
||
<link rel="stylesheet" href="{% static 'css/app.css' %}">
|
||
<script src="{% static 'js/app.js' %}"></script>
|
||
```
|
||
|
||
```bash
|
||
python manage.py collectstatic
|
||
```
|
||
|
||
## Простыми словами для экзамена
|
||
|
||
FBV — view как функция. CBV — view как класс с готовой логикой.
|
||
|
||
Маршрут соединяет URL и view:
|
||
|
||
```python
|
||
path("find/", views.find, name="find")
|
||
```
|
||
|
||
В шаблоне ссылка на именованный маршрут создаётся так:
|
||
|
||
```html
|
||
<a href="{% url 'myapp:find' %}">Поиск</a>
|
||
```
|
||
|
||
Шаблонные переменные пишутся через `{{ variable }}`. Шаблонные теги пишутся через `{% tag %}`. Комментарии пишутся через `{# comment #}`.
|
||
|
||
Ключ словаря в шаблоне читается через точку:
|
||
|
||
```django
|
||
{{ user_data.name }}
|
||
```
|