add: front (9) хлебные крошки
This commit is contained in:
@@ -1,13 +1,108 @@
|
||||
#
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
from django.shortcuts import render
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
|
||||
# Create your views here.
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class BreadcrumbItem:
|
||||
"""
|
||||
Один пункт хлебных крошек (например, "Главная", "Каталог", "Винил").
|
||||
|
||||
ЧТО ТАКОЕ dataclass (если видите это в первый раз)
|
||||
----------------------------------------------------
|
||||
`@dataclass` — это декоратор из стандартной библиотеки Python (модуль `dataclasses`).
|
||||
Он берёт класс, в котором просто перечислены имена полей и их типы (без ручного
|
||||
__init__), и САМ генерирует за вас служебные методы:
|
||||
- __init__(self, title, url=None) — конструктор класса. Благодаря нему можно
|
||||
писать BreadcrumbItem(title="Каталог", url="/catalog") без единой строчки
|
||||
кода конструктора;
|
||||
- __repr__ — красивое текстовое представление для print()/логов, например:
|
||||
BreadcrumbItem(title='Каталог', url='/catalog');
|
||||
- __eq__ — сравнение двух объектов по значениям полей, то есть
|
||||
BreadcrumbItem('Каталог', '/catalog') == BreadcrumbItem('Каталог', '/catalog')
|
||||
вернёт True (без dataclass пришлось бы сравнивать id() объектов).
|
||||
|
||||
Без dataclass пришлось бы писать руками:
|
||||
|
||||
class BreadcrumbItem:
|
||||
def __init__(self, title, url=None):
|
||||
self.title = title
|
||||
self.url = url
|
||||
|
||||
С dataclass — просто пишем поля (см. ниже) и всё перечисленное выше Python
|
||||
сгенерирует сам. Это НЕ меняет то, как объект используется — просто экономит
|
||||
код и снижает риск ошибок при ручном написании __init__/__repr__/__eq__.
|
||||
|
||||
Чем dataclass отличается от NamedTuple (typing.NamedTuple)?
|
||||
---------------------------------------------------------------
|
||||
Обе конструкции решают одну и ту же задачу — описать "структуру из нескольких
|
||||
полей" без лишнего кода, но по-разному устроены внутри:
|
||||
- `NamedTuple` — это, по сути, обычный tuple с именами полей. Он ВСЕГДА
|
||||
неизменяем (immutable), поддерживает распаковку как обычный tuple
|
||||
(title, url = item) и итерацию (for value in item);
|
||||
- `dataclass` — это обычный класс. По умолчанию он изменяем (можно
|
||||
присвоить item.title = "..." в любой момент), но можно сделать
|
||||
неизменяемым через параметр frozen=True (как сделано ниже). В отличие
|
||||
от NamedTuple, dataclass НЕ поддерживает распаковку как tuple, зато его
|
||||
проще расширять методами, наследованием и сложными полями по умолчанию
|
||||
(списками, словарями и т.п.).
|
||||
|
||||
Для хлебных крошек подошёл бы любой из двух вариантов. Здесь выбран dataclass
|
||||
с frozen=True — то есть объект ведёт себя как неизменяемый: попытка сделать
|
||||
`item.title = "другое значение"` после создания вызовет исключение. Это
|
||||
логично: один и тот же пункт крошек не должен "мутировать" по ходу рендеринга
|
||||
страницы.
|
||||
|
||||
КОНТРАКТ С ШАБЛОНОМ (важно!):
|
||||
-------------------------------
|
||||
Шаблон lpon_site/templates/block/breadcrumbs.html ожидает контекстную
|
||||
переменную `breadcrumbs` — список объектов именно с такими двумя полями:
|
||||
|
||||
Атрибуты:
|
||||
title (str):
|
||||
Видимый текст пункта, например "Каталог", "Винил".
|
||||
url (str | None):
|
||||
Ссылка на пункт. Если None (значение по умолчанию) — пункт
|
||||
считается ТЕКУЩЕЙ страницей: шаблон покажет его БЕЗ ссылки, с
|
||||
атрибутом aria-current="page". Обычно url=None указывают только
|
||||
у ПОСЛЕДНЕГО пункта в списке крошек.
|
||||
|
||||
ВАЖНО: пункт "Главная" в этот список включать НЕ нужно — ссылку на
|
||||
главную страницу (в виде иконки домика) шаблон breadcrumbs.html
|
||||
добавляет сам, одинаково для всех страниц. Список крошек должен
|
||||
начинаться сразу со следующего уровня (например, с "Каталог").
|
||||
|
||||
Пример использования — см. функцию catalog() ниже.
|
||||
"""
|
||||
title: str
|
||||
url: str | None = None
|
||||
|
||||
|
||||
def index(request: HttpRequest | None) -> HttpResponse:
|
||||
return render(request, 'index.html', {})
|
||||
|
||||
|
||||
def catalog(request: HttpRequest | None) -> HttpResponse:
|
||||
return render(request, 'catalog.html', {})
|
||||
"""
|
||||
Страница каталога (пока черновик вёрстки, см. lpon_site/templates/catalog.html).
|
||||
|
||||
ТЕСТОВЫЕ ДАННЫЕ ДЛЯ ХЛЕБНЫХ КРОШЕК:
|
||||
В реальной вьюхе список крошек будет собираться динамически (в зависимости
|
||||
от применённых фильтров, выбранной категории и т.п.). Пока каталог — черновик,
|
||||
здесь захардкожен простой пример из двух пунктов, чтобы продемонстрировать
|
||||
работу block/breadcrumbs.html: "Каталог" — обычный пункт со ссылкой, а
|
||||
"Компакт-кассеты" — текущая страница (url не передан, поэтому он покажется без
|
||||
ссылки). Пункт "Главная" передавать не нужно — его в виде иконки домика
|
||||
сам добавляет шаблон breadcrumbs.html.
|
||||
"""
|
||||
breadcrumbs = [
|
||||
BreadcrumbItem(title="Каталог", url="/catalog"),
|
||||
BreadcrumbItem(title="Компакт-кассеты", url="/catalog/compact-cassettes"), # url передан -> обычный пункт со ссылкой
|
||||
BreadcrumbItem(title="Для перезаписи" ), # url передан -> обычный пункт со ссылкой
|
||||
]
|
||||
return render(request, 'catalog.html', {"breadcrumbs": breadcrumbs})
|
||||
Reference in New Issue
Block a user