Добавил правильные хлебные крошки

This commit is contained in:
NikDizell 2026-02-26 20:00:28 +03:00
parent c65be53032
commit 61c7af35c1
4 changed files with 96 additions and 14 deletions

View File

@ -1,4 +1,4 @@
from programmer.mixins import MenuContextMixin from programmer.mixins import MenuContextMixin, BreadcrumbMixin
from django.views.generic import ListView, DetailView, CreateView from django.views.generic import ListView, DetailView, CreateView
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.contrib import messages from django.contrib import messages
@ -15,13 +15,22 @@ from django.shortcuts import redirect
from django.contrib.admin.views.decorators import staff_member_required from django.contrib.admin.views.decorators import staff_member_required
class ArticleListView(MenuContextMixin, ListView): class ArticleListView(MenuContextMixin, BreadcrumbMixin, ListView):
"""Список статей""" """Список статей"""
model = Article model = Article
template_name = 'blog/article_list.html' template_name = 'blog/article_list.html'
context_object_name = 'articles' context_object_name = 'articles'
paginate_by = 10 paginate_by = 10
def get_breadcrumbs(self):
if 'category_slug' in self.kwargs:
category = Category.objects.get(slug=self.kwargs['category_slug'])
return [
{'title': 'Статьи', 'url_name': 'blog:article_list'},
{'title': category.name, 'url_name': None},
]
return [{'title': 'Статьи', 'url_name': None}]
def get_queryset(self): def get_queryset(self):
category_slug = self.kwargs.get('category_slug') category_slug = self.kwargs.get('category_slug')
return get_published_articles(category_slug) return get_published_articles(category_slug)
@ -51,13 +60,26 @@ class ArticleListView(MenuContextMixin, ListView):
return super().dispatch(*args, **kwargs) return super().dispatch(*args, **kwargs)
class ArticleDetailView(MenuContextMixin, DetailView, CreateView): class ArticleDetailView(MenuContextMixin, BreadcrumbMixin, DetailView, CreateView):
"""Детальная страница статьи + форма комментария""" """Детальная страница статьи + форма комментария"""
model = Article model = Article
template_name = 'blog/article_detail.html' template_name = 'blog/article_detail.html'
context_object_name = 'article' context_object_name = 'article'
form_class = CommentForm form_class = CommentForm
def get_breadcrumbs(self):
article = self.get_object()
return [
{'title': 'Статьи', 'url_name': 'blog:article_list'},
{
'title': article.category.name,
'url_name': 'blog:category_detail',
'category_slug': article.category.slug,
# 'url_args': [article.category.slug]
},
{'title': article.title, 'url_name': None},
]
def get_object(self, queryset=None): def get_object(self, queryset=None):
article = get_article_by_slug(self.kwargs['slug']) article = get_article_by_slug(self.kwargs['slug'])
if article is None: if article is None:
@ -110,7 +132,7 @@ class ArticleDetailView(MenuContextMixin, DetailView, CreateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context.update({ context.update({
'title': "Статии | Инструкции", 'title': "Статьи | Инструкции",
'meta_description': ( 'meta_description': (
"Инструкции по работе в 1С и бизнес-решения" "Инструкции по работе в 1С и бизнес-решения"
"инструкции, статьи помощь 1С." "инструкции, статьи помощь 1С."

View File

@ -48,3 +48,26 @@ class MenuContextMixin(ContextMixin):
return context return context
class BreadcrumbMixin(ContextMixin):
"""
Миксин для добавления хлебных крошек в контекст.
В дочерних классах нужно определить метод get_breadcrumbs().
"""
breadcrumbs = None
def get_breadcrumbs(self):
"""
Должен возвращать список словарей:
[{'title': 'Название', 'url_name': 'url_name', 'url_args': [...], 'url_kwargs': {...}}, ...]
Последний элемент без url_name (текущая страница).
"""
if self.breadcrumbs is not None:
return self.breadcrumbs
return []
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['breadcrumbs'] = self.get_breadcrumbs()
return context

View File

@ -566,10 +566,26 @@
<section class="content"> <section class="content">
<!-- Breadcrumbs --> <!-- Breadcrumbs -->
{% block breadcrumbs %} {% block breadcrumbs %}
<nav class="breadcrumbs"> <nav class="breadcrumbs" aria-label="breadcrumb">
<a href="{% url 'home' %}" class="breadcrumb-link">Главная</a> <a href="{% url 'home' %}" class="breadcrumb-link">Главная</a>
{% for crumb in breadcrumbs %}
<span class="breadcrumb-separator">/</span>
{% if crumb.url_name %}
{% if crumb.category_slug %}
<a href="{% url crumb.url_name category_slug=crumb.category_slug %}" class="breadcrumb-link">{{ crumb.title }}</a>
{% elif crumb.url_args %}
<a href="{% url crumb.url_name crumb.url_kwargs %}" class="breadcrumb-link">{{ crumb.title }}</a>
{% else %}
<a href="{% url crumb.url_name %}" class="breadcrumb-link">{{ crumb.title }}</a>
{% endif %}
{% else %}
<span class="breadcrumb-current">{{ crumb.title }}</span>
{% endif %}
{% empty %}
{# Если крошек нет, показываем просто заголовок страницы #}
<span class="breadcrumb-separator">/</span> <span class="breadcrumb-separator">/</span>
<span class="breadcrumb-current">{{ title }}</span> <span class="breadcrumb-current">{{ title }}</span>
{% endfor %}
</nav> </nav>
{% endblock %} {% endblock %}

View File

@ -17,7 +17,7 @@ from django.db.models import Count, QuerySet
from django.contrib.auth.decorators import login_required, user_passes_test from django.contrib.auth.decorators import login_required, user_passes_test
from django.views.decorators.http import require_GET, require_POST from django.views.decorators.http import require_GET, require_POST
from django.views.generic import TemplateView, ListView, CreateView, UpdateView from django.views.generic import TemplateView, ListView, CreateView, UpdateView
from .mixins import PageViewTrackingMixin, MenuContextMixin from .mixins import PageViewTrackingMixin, MenuContextMixin, BreadcrumbMixin
from .services import get_published_queryset, track_page_view from .services import get_published_queryset, track_page_view
from typing import Any, Dict, Type from typing import Any, Dict, Type
from django.template.loader import render_to_string from django.template.loader import render_to_string
@ -95,10 +95,13 @@ class HomePageView(BasePageView):
return context return context
class AboutPageView(BasePageView): class AboutPageView(BasePageView, BreadcrumbMixin):
"""Страница 'О себе'.""" """Страница 'О себе'."""
template_name = 'programmer/about.html' template_name = 'programmer/about.html'
def get_breadcrumbs(self):
return [{'title': 'Обо мне', 'url_name': None}]
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]: def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
@ -116,7 +119,7 @@ class AboutPageView(BasePageView):
return context return context
class SolutionListView(BaseListView): class SolutionListView(BaseListView, BreadcrumbMixin):
"""Список проектов с пагинацией.""" """Список проектов с пагинацией."""
model = Solution model = Solution
template_name = 'programmer/solution.html' template_name = 'programmer/solution.html'
@ -125,6 +128,9 @@ class SolutionListView(BaseListView):
cards_template = 'programmer/includes/project_cards.html' cards_template = 'programmer/includes/project_cards.html'
ordering = ['-time_create'] ordering = ['-time_create']
def get_breadcrumbs(self):
return [{'title': 'Проекты', 'url_name': None}]
def get_queryset(self) -> QuerySet: def get_queryset(self) -> QuerySet:
"""Возвращает только опубликованные проекты.""" """Возвращает только опубликованные проекты."""
return get_published_queryset(self.model, order_by='-time_create') return get_published_queryset(self.model, order_by='-time_create')
@ -146,7 +152,7 @@ class SolutionListView(BaseListView):
return context return context
class RecallListView(BaseListView): class RecallListView(BaseListView, BreadcrumbMixin):
"""Список отзывов с пагинацией.""" """Список отзывов с пагинацией."""
model = Recall model = Recall
template_name = 'programmer/recall.html' template_name = 'programmer/recall.html'
@ -154,6 +160,9 @@ class RecallListView(BaseListView):
paginate_by = 5 paginate_by = 5
ordering = ['-time_create'] ordering = ['-time_create']
def get_breadcrumbs(self):
return [{'title': 'Отзывы', 'url_name': None}]
def get_queryset(self) -> QuerySet: def get_queryset(self) -> QuerySet:
"""Возвращает только опубликованные отзывы.""" """Возвращает только опубликованные отзывы."""
return get_published_queryset(self.model, order_by='-time_create') return get_published_queryset(self.model, order_by='-time_create')
@ -175,17 +184,23 @@ class RecallListView(BaseListView):
return context return context
class ProfileView(LoginRequiredMixin, PageViewTrackingMixin, MenuContextMixin, TemplateView): class ProfileView(LoginRequiredMixin, PageViewTrackingMixin, MenuContextMixin, BreadcrumbMixin, TemplateView):
template_name = 'programmer/profile.html' template_name = 'programmer/profile.html'
def get_breadcrumbs(self):
return [{'title': 'Профиль', 'url_name': None}]
class RegisterView(PageViewTrackingMixin, MenuContextMixin, SuccessMessageMixin, CreateView):
class RegisterView(PageViewTrackingMixin, MenuContextMixin, SuccessMessageMixin, BreadcrumbMixin, CreateView):
"""Регистрация нового пользователя""" """Регистрация нового пользователя"""
template_name = 'programmer/register.html' template_name = 'programmer/register.html'
form_class = RegistrationForm form_class = RegistrationForm
# success_url = reverse_lazy('profile') # success_url = reverse_lazy('profile')
success_message = '✅ Регистрация успешна!' success_message = '✅ Регистрация успешна!'
def get_breadcrumbs(self):
return [{'title': 'Регистрация', 'url_name': None}]
def get_success_url(self): def get_success_url(self):
# Если есть параметр next, используем его # Если есть параметр next, используем его
next_url = self.request.POST.get('next') or self.request.GET.get('next') next_url = self.request.POST.get('next') or self.request.GET.get('next')
@ -207,12 +222,18 @@ class RegisterView(PageViewTrackingMixin, MenuContextMixin, SuccessMessageMixin,
return context return context
class ProfileEditView(LoginRequiredMixin, PageViewTrackingMixin, MenuContextMixin, UpdateView): class ProfileEditView(LoginRequiredMixin, PageViewTrackingMixin, MenuContextMixin, BreadcrumbMixin, UpdateView):
model = Profile model = Profile
form_class = ProfileForm form_class = ProfileForm
template_name = 'programmer/profile_edit.html' template_name = 'programmer/profile_edit.html'
success_url = reverse_lazy('profile') success_url = reverse_lazy('profile')
def get_breadcrumbs(self):
return [
{'title': 'Профиль', 'url_name': 'profile'},
{'title': 'Редактирование', 'url_name': None},
]
def get_object(self, queryset=None): def get_object(self, queryset=None):
# Возвращаем профиль текущего пользователя # Возвращаем профиль текущего пользователя
return self.request.user.profile return self.request.user.profile