from programmer.mixins import MenuContextMixin 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, ListView): """Список статей""" model = Article template_name = 'blog/article_list.html' context_object_name = 'articles' paginate_by = 10 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 @method_decorator(cache_page(60 * 5)) # кэширование страницы на 5 минут def dispatch(self, *args, **kwargs): return super().dispatch(*args, **kwargs) class ArticleDetailView(MenuContextMixin, DetailView, CreateView): """Детальная страница статьи + форма комментария""" model = Article template_name = 'blog/article_detail.html' context_object_name = 'article' form_class = CommentForm def get_object(self, queryset=None): article = get_article_by_slug(self.kwargs['slug']) if article is None: raise Http404("Статья не найдена") return article def get(self, request, *args, **kwargs): # Увеличиваем счётчик просмотров при GET запросе self.object = self.get_object() increment_article_views(self.object) return super().get(request, *args, **kwargs) def post(self, request, *args, **kwargs): # Проверяем, авторизован ли пользователь if not request.user.is_authenticated: messages.error(request, "❌ Только авторизованные пользователи могут оставлять комментарии.") return redirect(request.path) # redirect('login') или redirect(request.path) чтобы остаться на странице self.object = self.get_object() form = self.get_form() if form.is_valid(): return self.form_valid(form) else: return self.form_invalid(form) def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs['request'] = self.request return kwargs def get_initial(self): """Возвращает пустые начальные данные для формы комментария.""" initial = super().get_initial() initial['content'] = '' return initial def form_valid(self, form): # Для авторизованных пользователей гарантируем наличие имени и email if self.request.user.is_authenticated: form.cleaned_data['author_name'] = self.request.user.get_full_name() or self.request.user.username form.cleaned_data['author_email'] = self.request.user.email comment = add_comment_to_article( article=self.object, data=form.cleaned_data ) messages.success(self.request, "Ваш комментарий отправлен на модерацию.") return redirect(self.object.get_absolute_url()) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context.update({ 'title': "Статии | Инструкции", 'meta_description': ( "Инструкции по работе в 1С и бизнес-решения" "инструкции, статьи помощь 1С." ), 'meta_keywords': ( "проекты 1С, автоматизация склада 1С, интеграция ТСД 1С, " "миграция 1С 7.7, кейсы 1С, примеры работ 1С" ), 'user_is_authenticated': self.request.user.is_authenticated, }) # Добавляем только одобренные комментарии context['moderated_comments'] = self.object.comments.filter(is_moderated=True) return context @method_decorator(staff_member_required, name='dispatch') class ArticleDraftPreviewView(DetailView): model = Article template_name = 'blog/article_detail.html' # используем тот же шаблон context_object_name = 'article' def get_queryset(self): # Для предпросмотра показываем даже неопубликованные статьи return Article.objects.all() # без фильтрации по is_published