143 lines
6.1 KiB
Python
143 lines
6.1 KiB
Python
from programmer.mixins import MenuContextMixin, BreadcrumbMixin
|
||
from django.views.generic import ListView, DetailView, CreateView
|
||
from django.urls import reverse_lazy
|
||
from django.contrib import messages
|
||
from django.utils.decorators import method_decorator
|
||
from django.views.decorators.cache import cache_page
|
||
from .models import Article, Category
|
||
from .services import (
|
||
get_published_articles, get_article_by_slug,
|
||
increment_article_views, add_comment_to_article
|
||
)
|
||
from .forms import CommentForm
|
||
from django.http import Http404
|
||
from django.shortcuts import redirect
|
||
from django.contrib.admin.views.decorators import staff_member_required
|
||
|
||
|
||
class ArticleListView(MenuContextMixin, BreadcrumbMixin, ListView):
|
||
"""Список статей"""
|
||
model = Article
|
||
template_name = 'blog/article_list.html'
|
||
context_object_name = 'articles'
|
||
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):
|
||
category_slug = self.kwargs.get('category_slug')
|
||
return get_published_articles(category_slug)
|
||
|
||
def get_context_data(self, **kwargs):
|
||
context = super().get_context_data(**kwargs)
|
||
context['categories'] = Category.objects.all()
|
||
context.update({
|
||
'title': "Статии | Инструкции",
|
||
'meta_description': (
|
||
"Инструкции по работе в 1С и бизнес-решения"
|
||
"инструкции, статьи помощь 1С."
|
||
),
|
||
'meta_keywords': (
|
||
"проекты 1С, автоматизация склада 1С, интеграция ТСД 1С, "
|
||
"миграция 1С 7.7, кейсы 1С, примеры работ 1С"
|
||
),
|
||
})
|
||
if 'category_slug' in self.kwargs:
|
||
context['current_category'] = Category.objects.filter(
|
||
slug=self.kwargs['category_slug']
|
||
).first()
|
||
return context
|
||
|
||
def dispatch(self, *args, **kwargs):
|
||
return super().dispatch(*args, **kwargs)
|
||
|
||
|
||
class ArticleDetailView(MenuContextMixin, BreadcrumbMixin, DetailView):
|
||
"""Детальная страница статьи с формой комментария."""
|
||
model = Article
|
||
template_name = 'blog/article_detail.html'
|
||
context_object_name = 'article'
|
||
|
||
def get_object(self, queryset=None):
|
||
"""Получаем статью по slug, только опубликованные."""
|
||
if not hasattr(self, '_article_cache'):
|
||
article = get_article_by_slug(self.kwargs['slug'])
|
||
if article is None:
|
||
raise Http404("Статья не найдена")
|
||
self._article_cache = article
|
||
return self._article_cache
|
||
|
||
def get_breadcrumbs(self):
|
||
article = self.object # already set by the time breadcrumbs are called
|
||
return [
|
||
{'title': 'Статьи', 'url_name': 'blog:article_list'},
|
||
{
|
||
'title': article.category.name,
|
||
'url_name': 'blog:category_detail',
|
||
'category_slug': article.category.slug,
|
||
},
|
||
{'title': article.title, 'url_name': None},
|
||
]
|
||
|
||
def get(self, request, *args, **kwargs):
|
||
"""Устанавливаем self.object один раз, увеличиваем счётчик просмотров."""
|
||
self.object = self.get_object()
|
||
increment_article_views(self.object)
|
||
context = self.get_context_data(object=self.object)
|
||
return self.render_to_response(context)
|
||
|
||
def post(self, request, *args, **kwargs):
|
||
"""Обработка формы комментария."""
|
||
self.object = self.get_object()
|
||
|
||
if not request.user.is_authenticated:
|
||
messages.error(request, "❌ Только авторизованные пользователи могут оставлять комментарии.")
|
||
return redirect(request.path)
|
||
|
||
form = CommentForm(request.POST, request=request)
|
||
if form.is_valid():
|
||
add_comment_to_article(
|
||
article=self.object,
|
||
data=form.cleaned_data
|
||
)
|
||
messages.success(request, "✅ Ваш комментарий отправлен на модерацию.")
|
||
return redirect(self.object.get_absolute_url())
|
||
|
||
# Форма невалидна — показываем страницу снова с ошибками
|
||
context = self.get_context_data(object=self.object, form=form)
|
||
return self.render_to_response(context)
|
||
|
||
def get_context_data(self, **kwargs):
|
||
context = super().get_context_data(**kwargs)
|
||
|
||
# Используем form из kwargs если передана (например при ошибке валидации)
|
||
if 'form' not in kwargs:
|
||
context['form'] = CommentForm(request=self.request)
|
||
|
||
context.update({
|
||
'moderated_comments': self.object.comments.filter(is_moderated=True),
|
||
'user_is_authenticated': self.request.user.is_authenticated,
|
||
'title': self.object.get_seo_title(),
|
||
'meta_description': self.object.get_seo_description(),
|
||
'meta_keywords': ', '.join(
|
||
tag.name for tag in self.object.tags.all()
|
||
),
|
||
})
|
||
return context
|
||
|
||
@method_decorator(staff_member_required, name='dispatch')
|
||
class ArticleDraftPreviewView(MenuContextMixin, BreadcrumbMixin, DetailView):
|
||
model = Article
|
||
template_name = 'blog/article_detail.html' # используем тот же шаблон
|
||
context_object_name = 'article'
|
||
|
||
def get_queryset(self):
|
||
# Для предпросмотра показываем даже неопубликованные статьи
|
||
return Article.objects.all() # без фильтрации по is_published |