Compare commits

...

2 Commits

Author SHA1 Message Date
d54d77dd88 Merge pull request 'Оптимизировал просмотры страниц' (#22) from fix_test into main
Some checks failed
Auto-update README / update-readme (push) Has been cancelled
Reviewed-on: #22
2026-06-19 22:10:39 +03:00
NikDizell
5a9c0bbcc6 Оптимизировал просмотры страниц 2026-06-19 22:09:48 +03:00
7 changed files with 19 additions and 69 deletions

View File

@ -45,6 +45,9 @@ X_FRAME_OPTIONS = 'ALLOW-FROM https://metrika.yandex.ru'
# ОБЯЗАТЕЛЬНО укажите ваши домены
ALLOWED_HOSTS = os.getenv('DJANGO_ALLOWED_HOSTS', 'localhost,127.0.0.1').split(',')
# Исключения для отслеживания
EXCLUDED_IPS = os.getenv('EXCLUDED_IPS', 'localhost,127.0.0.1').split(',')
# Важно для работы за прокси
if not DEBUG:
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

View File

@ -1,4 +1,3 @@
# blog/signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import Article

View File

@ -96,7 +96,6 @@ class ArticleDetailView(MenuContextMixin, BreadcrumbMixin, DetailView):
def post(self, request, *args, **kwargs):
"""Обработка формы комментария."""
self.object = self.get_object()
print("POST data:", request.POST)
if not request.user.is_authenticated:
messages.error(request, "❌ Только авторизованные пользователи могут оставлять комментарии.")
@ -106,7 +105,6 @@ class ArticleDetailView(MenuContextMixin, BreadcrumbMixin, DetailView):
if form.is_valid():
parent_id = form.cleaned_data.get('parent_id')
print(f"parent_id from cleaned_data: {parent_id}")
parent = None
if parent_id:

View File

@ -1,6 +1,8 @@
from .models import PageView, Visitor
from django.utils import timezone
from django.db import transaction
from .services import track_page_view
import logging
logger = logging.getLogger(__name__)
class CSPMiddleware:
@ -20,45 +22,7 @@ class PageViewMiddleware:
def __call__(self, request):
# Игнорируем статические файлы и админку
if not request.path.startswith('/static/') and not request.path.startswith('/admin/'):
self.track_page_view(request)
track_page_view(request)
response = self.get_response(request)
return response
def track_page_view(self, request):
try:
with transaction.atomic():
# Сохраняем просмотр страницы
PageView.objects.create(
url=request.path,
ip_address=self.get_client_ip(request),
user_agent=request.META.get('HTTP_USER_AGENT', ''),
referer=request.META.get('HTTP_REFERER', '')
)
# Обновляем статистику посетителя
ip = self.get_client_ip(request)
visitor, created = Visitor.objects.get_or_create(
ip_address=ip,
defaults={
'first_visit': timezone.now(),
'last_visit': timezone.now()
}
)
if not created:
visitor.last_visit = timezone.now()
visitor.visit_count += 1
visitor.save()
except Exception as e:
# Логируем ошибку, но не прерываем выполнение
print(f"Error tracking page view: {e}")
def get_client_ip(self, request):
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0]
else:
ip = request.META.get('REMOTE_ADDR')
return ip

View File

@ -3,17 +3,6 @@ from django.views.generic.base import ContextMixin
from .services import track_page_view
class PageViewTrackingMixin(ContextMixin):
"""
Миксин для отслеживания просмотров страниц.
Добавляет трекинг при вызове view.
"""
def dispatch(self, request, *args, **kwargs):
# Отслеживаем просмотр перед обработкой запроса
track_page_view(request)
return super().dispatch(request, *args, **kwargs)
class MenuContextMixin(ContextMixin):
"""
Миксин для добавления меню в контекст.

View File

@ -1,7 +1,7 @@
import logging
from typing import Optional
from django.http import HttpRequest
from django.conf import settings
from .models import PageView, Visitor
from django.utils import timezone
@ -28,14 +28,11 @@ def should_track_request(request: HttpRequest) -> bool:
'/cron', '/remote.php', '/ocs', '/apps/', '/custom_apps/',
]
docker_ips = [
'192.168.64.1', '192.168.65.1',
'172.17.0.1', '172.18.0.1', '172.19.0.1',
]
if any(path.startswith(p) for p in ignored_paths):
return False
if client_ip in docker_ips:
excluded_ips = getattr(settings, 'EXCLUDED_IPS', [])
if client_ip in excluded_ips:
return False
return True

View File

@ -16,7 +16,7 @@ from django.db.models import Count, QuerySet
from django.contrib.auth.decorators import login_required, user_passes_test
from django.views.decorators.http import require_GET, require_POST
from django.views.generic import TemplateView, ListView, CreateView, UpdateView, DetailView
from .mixins import PageViewTrackingMixin, MenuContextMixin, BreadcrumbMixin
from .mixins import MenuContextMixin, BreadcrumbMixin
from .services import get_published_queryset, track_page_view
from typing import Any, Dict, Type
from django.template.loader import render_to_string
@ -26,7 +26,7 @@ from products.models import Order
import os
class BasePageView(PageViewTrackingMixin, MenuContextMixin, TemplateView):
class BasePageView(MenuContextMixin, TemplateView):
"""
Базовый класс для всех страниц сайта.
Включает отслеживание просмотров и контекст меню.
@ -34,7 +34,7 @@ class BasePageView(PageViewTrackingMixin, MenuContextMixin, TemplateView):
extra_context = {}
class BaseListView(PageViewTrackingMixin, MenuContextMixin, ListView):
class BaseListView(MenuContextMixin, ListView):
"""
Базовый класс для всех страниц со списками объектов.
Включает отслеживание просмотров и контекст меню.
@ -194,14 +194,14 @@ class RecallListView(BaseListView, BreadcrumbMixin):
return context
class ProfileView(LoginRequiredMixin, PageViewTrackingMixin, MenuContextMixin, BreadcrumbMixin, TemplateView):
class ProfileView(LoginRequiredMixin, MenuContextMixin, BreadcrumbMixin, TemplateView):
template_name = 'programmer/profile.html'
def get_breadcrumbs(self):
return [{'title': 'Профиль', 'url_name': None}]
class RegisterView(PageViewTrackingMixin, MenuContextMixin, SuccessMessageMixin, BreadcrumbMixin, CreateView):
class RegisterView(MenuContextMixin, SuccessMessageMixin, BreadcrumbMixin, CreateView):
"""Регистрация нового пользователя"""
template_name = 'programmer/register.html'
form_class = RegistrationForm
@ -240,7 +240,7 @@ class RegisterView(PageViewTrackingMixin, MenuContextMixin, SuccessMessageMixin,
super().__init__(*args, **kwargs)
class ProfileEditView(LoginRequiredMixin, PageViewTrackingMixin, MenuContextMixin, BreadcrumbMixin, UpdateView):
class ProfileEditView(LoginRequiredMixin, MenuContextMixin, BreadcrumbMixin, UpdateView):
model = Profile
form_class = ProfileForm
template_name = 'programmer/profile_edit.html'