Оптимизация кода с помощью клауда
This commit is contained in:
parent
ad5ac696ca
commit
d048510f68
@ -121,33 +121,7 @@ class CallbackAdmin(admin.ModelAdmin):
|
|||||||
}
|
}
|
||||||
return render(request, 'admin/callback_stats.html', context)
|
return render(request, 'admin/callback_stats.html', context)
|
||||||
|
|
||||||
class ProgrammerAdmin(admin.ModelAdmin):
|
|
||||||
list_display = ('id', 'title', 'time_create', 'photo', 'is_published')
|
|
||||||
list_display_links = ('id', 'title')
|
|
||||||
search_fields = ('title', 'content')
|
|
||||||
list_editable = ('is_published',)
|
|
||||||
list_filter = ('time_create', 'is_published')
|
|
||||||
|
|
||||||
class RecallAdmin(admin.ModelAdmin):
|
|
||||||
list_display = ('id', 'title', 'time_create', 'scan', 'is_published')
|
|
||||||
list_display_links = ('id', 'title')
|
|
||||||
search_fields = ('title', 'content')
|
|
||||||
list_editable = ('is_published',)
|
|
||||||
list_filter = ('time_create', 'is_published')
|
|
||||||
|
|
||||||
class SolutionAdmin(admin.ModelAdmin):
|
|
||||||
list_display = ('id', 'title', 'time_create', 'is_published')
|
|
||||||
list_display_links = ('id', 'title')
|
|
||||||
search_fields = ('title', 'description', 'implementation')
|
|
||||||
list_editable = ('is_published',)
|
|
||||||
list_filter = ('time_create', 'is_published')
|
|
||||||
|
|
||||||
class HomeAdmin(admin.ModelAdmin):
|
|
||||||
list_display = ('id', 'title', 'time_create', 'is_published')
|
|
||||||
list_display_links = ('id', 'title')
|
|
||||||
search_fields = ('title', 'content')
|
|
||||||
list_editable = ('is_published',)
|
|
||||||
list_filter = ('time_create', 'is_published')
|
|
||||||
|
|
||||||
@admin.register(PageView)
|
@admin.register(PageView)
|
||||||
class PageViewAdmin(admin.ModelAdmin):
|
class PageViewAdmin(admin.ModelAdmin):
|
||||||
|
|||||||
@ -20,7 +20,7 @@ class Recall(models.Model):
|
|||||||
return self.title
|
return self.title
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('post', kwargs={'post_id': self.pk})
|
return reverse('recall_detail', kwargs={'pk': self.pk})
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = 'Отзыв'
|
verbose_name = 'Отзыв'
|
||||||
@ -49,7 +49,7 @@ class Competence(models.Model):
|
|||||||
return self.title
|
return self.title
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('post', kwargs={'post_id': self.pk})
|
return reverse('competence_detail', kwargs={'pk': self.pk})
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = 'Компетенция'
|
verbose_name = 'Компетенция'
|
||||||
@ -70,7 +70,7 @@ class Solution(models.Model):
|
|||||||
return self.title
|
return self.title
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('post', kwargs={'post_id': self.pk})
|
return reverse('solution_detail', kwargs={'pk': self.pk})
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = 'Проекты'
|
verbose_name = 'Проекты'
|
||||||
@ -107,7 +107,7 @@ class Home(models.Model):
|
|||||||
return self.title
|
return self.title
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('post', kwargs={'post_id': self.pk})
|
return reverse('home')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = 'Главная страница'
|
verbose_name = 'Главная страница'
|
||||||
@ -189,10 +189,10 @@ class Profile(models.Model):
|
|||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=User)
|
@receiver(post_save, sender=User)
|
||||||
def create_or_save_user_profile(sender, instance, **kwargs):
|
def create_or_save_user_profile(sender, instance, created, **kwargs):
|
||||||
# Получаем или создаём профиль, затем сохраняем
|
# Получаем или создаём профиль, затем сохраняем
|
||||||
profile, created = Profile.objects.get_or_create(user=instance)
|
if created:
|
||||||
profile.save()
|
Profile.objects.get_or_create(user=instance)
|
||||||
|
|
||||||
|
|
||||||
@receiver([post_save, post_delete], sender=Home)
|
@receiver([post_save, post_delete], sender=Home)
|
||||||
|
|||||||
@ -3,11 +3,19 @@
|
|||||||
{% load static %}
|
{% load static %}
|
||||||
{% load seo_tags %}
|
{% load seo_tags %}
|
||||||
|
|
||||||
|
|
||||||
{% block extra_css %}
|
{% block extra_css %}
|
||||||
<link rel="stylesheet" href="{% static 'programmer/css/solution-accordion.css' %}">
|
<link rel="stylesheet" href="{% static 'programmer/css/solution-accordion.css' %}">
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
<article>
|
||||||
|
<h1>{{ solution.title }}</h1>
|
||||||
|
<p>{{ solution.description }}</p>
|
||||||
|
<section>{{ solution.implementation|safe }}</section>
|
||||||
|
<footer>{{ solution.closing|safe }}</footer>
|
||||||
|
</article>
|
||||||
|
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h1 class="page-title">Проекты автоматизации 1С</h1>
|
<h1 class="page-title">Проекты автоматизации 1С</h1>
|
||||||
<p class="page-subtitle">Реализованные решения и кейсы по автоматизации бизнес-процессов</p>
|
<p class="page-subtitle">Реализованные решения и кейсы по автоматизации бизнес-процессов</p>
|
||||||
|
|||||||
@ -14,10 +14,12 @@ urlpatterns = [
|
|||||||
path('', views.HomePageView.as_view(), name='home'),
|
path('', views.HomePageView.as_view(), name='home'),
|
||||||
path('about/', views.AboutPageView.as_view(), name='about'),
|
path('about/', views.AboutPageView.as_view(), name='about'),
|
||||||
path('solutions/', views.SolutionListView.as_view(), name='solution'),
|
path('solutions/', views.SolutionListView.as_view(), name='solution'),
|
||||||
|
path('solutions/<int:pk>/', SolutionDetailView.as_view(), name='solution_detail'),
|
||||||
# path('competence/', ability, name='ability'),
|
# path('competence/', ability, name='ability'),
|
||||||
|
# path('competence/<int:pk>/', CompetenceDetailView.as_view(), name='competence_detail'),
|
||||||
path('recall/', views.RecallListView.as_view(), name='recall'),
|
path('recall/', views.RecallListView.as_view(), name='recall'),
|
||||||
|
path('recalls/<int:pk>/', RecallDetailView.as_view(), name='recall_detail'),
|
||||||
path('blog/', ArticleListView.as_view(), name='blog'),
|
path('blog/', ArticleListView.as_view(), name='blog'),
|
||||||
path('post/<int:post_id>/', show_post, name='post'),
|
|
||||||
path('callback/', callback_request, name='callback'),
|
path('callback/', callback_request, name='callback'),
|
||||||
path('admin/statistics/', statistics_view, name='statistics'),
|
path('admin/statistics/', statistics_view, name='statistics'),
|
||||||
# Sitemap
|
# Sitemap
|
||||||
|
|||||||
@ -8,15 +8,13 @@ from django.urls import reverse_lazy
|
|||||||
from .models import *
|
from .models import *
|
||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import render, redirect
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from .models import Home, Solution, Recall, PageView, Visitor, CallbackRequest
|
|
||||||
from .forms import CallbackForm, ProfileForm, UserEditForm, RegistrationForm
|
from .forms import CallbackForm, ProfileForm, UserEditForm, RegistrationForm
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from .models import PageView, Visitor
|
|
||||||
from django.db.models import Count, QuerySet
|
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, DetailView
|
||||||
from .mixins import PageViewTrackingMixin, MenuContextMixin, BreadcrumbMixin
|
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
|
||||||
@ -271,6 +269,62 @@ class PrivacyPolicyView(TemplateView, MenuContextMixin, BreadcrumbMixin):
|
|||||||
return [{'title': 'Политика конфиденциальности', 'url_name': None}]
|
return [{'title': 'Политика конфиденциальности', 'url_name': None}]
|
||||||
|
|
||||||
|
|
||||||
|
class SolutionDetailView(MenuContextMixin, BreadcrumbMixin, DetailView):
|
||||||
|
model = Solution
|
||||||
|
template_name = 'programmer/solution_detail.html'
|
||||||
|
context_object_name = 'solution'
|
||||||
|
# Отображаются только опубликованные объекты; для неопубликованных выводится ошибка 404
|
||||||
|
queryset = Solution.objects.filter(is_published=True)
|
||||||
|
|
||||||
|
def get_breadcrumbs(self):
|
||||||
|
return [
|
||||||
|
{'title': 'Проекты', 'url_name': 'solution'},
|
||||||
|
{'title': self.object.title, 'url_name': None},
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context['title'] = self.object.get_seo_title()
|
||||||
|
context['meta_description'] = self.object.get_seo_description()
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class RecallDetailView(MenuContextMixin, BreadcrumbMixin, DetailView):
|
||||||
|
model = Recall
|
||||||
|
template_name = 'programmer/recall_detail.html'
|
||||||
|
context_object_name = 'recall'
|
||||||
|
queryset = Recall.objects.filter(is_published=True)
|
||||||
|
|
||||||
|
def get_breadcrumbs(self):
|
||||||
|
return [
|
||||||
|
{'title': 'Отзывы', 'url_name': 'recall'},
|
||||||
|
{'title': self.object.title, 'url_name': None},
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context['title'] = self.object.get_seo_title()
|
||||||
|
context['meta_description'] = self.object.get_seo_description()
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class CompetenceDetailView(MenuContextMixin, BreadcrumbMixin, DetailView):
|
||||||
|
model = Competence
|
||||||
|
template_name = 'programmer/competence_detail.html'
|
||||||
|
context_object_name = 'competence'
|
||||||
|
queryset = Competence.objects.filter(is_published=True)
|
||||||
|
|
||||||
|
def get_breadcrumbs(self):
|
||||||
|
return [
|
||||||
|
{'title': 'Компетенции', 'url_name': 'ability'},
|
||||||
|
{'title': self.object.title, 'url_name': None},
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
@require_POST
|
@require_POST
|
||||||
def callback_request(request: HttpRequest) -> HttpResponse:
|
def callback_request(request: HttpRequest) -> HttpResponse:
|
||||||
"""
|
"""
|
||||||
@ -308,13 +362,6 @@ def callback_request(request: HttpRequest) -> HttpResponse:
|
|||||||
return redirect('home')
|
return redirect('home')
|
||||||
|
|
||||||
|
|
||||||
def show_post(request: HttpRequest, post_id: int) -> HttpResponse:
|
|
||||||
"""
|
|
||||||
Временная функция для отображения детальной страницы поста.
|
|
||||||
TODO: Реализовать полноценный DetailView для каждой модели.
|
|
||||||
"""
|
|
||||||
return HttpResponse(f"Отображение поста № {post_id}")
|
|
||||||
|
|
||||||
|
|
||||||
def pageNotFound(request: HttpRequest, exception: Exception) -> HttpResponseNotFound:
|
def pageNotFound(request: HttpRequest, exception: Exception) -> HttpResponseNotFound:
|
||||||
"""Обработчик 404 ошибки."""
|
"""Обработчик 404 ошибки."""
|
||||||
@ -363,7 +410,7 @@ def statistics_view(request: HttpRequest) -> HttpResponse:
|
|||||||
'unread_callbacks': CallbackRequest.objects.filter(is_read=False).count(),
|
'unread_callbacks': CallbackRequest.objects.filter(is_read=False).count(),
|
||||||
})
|
})
|
||||||
|
|
||||||
return render(request, 'admin/statistics.html', stats)
|
return render(request, 'admin/statistics.html', {'stats': stats})
|
||||||
|
|
||||||
|
|
||||||
@require_GET
|
@require_GET
|
||||||
@ -371,84 +418,3 @@ def robots_txt(request: HttpRequest) -> HttpResponse:
|
|||||||
"""Отдает robots.txt."""
|
"""Отдает robots.txt."""
|
||||||
return render(request, 'robots.txt', content_type='text/plain')
|
return render(request, 'robots.txt', content_type='text/plain')
|
||||||
|
|
||||||
|
|
||||||
def get_client_ip(request):
|
|
||||||
"""Получаем реальный IP клиента"""
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
def should_track_request(request):
|
|
||||||
"""Определяем, нужно ли отслеживать запрос"""
|
|
||||||
|
|
||||||
client_ip = get_client_ip(request)
|
|
||||||
path = request.path
|
|
||||||
|
|
||||||
# Игнорируемые пути (Nextcloud специфичные)
|
|
||||||
nextcloud_paths = [
|
|
||||||
'/index.php',
|
|
||||||
'/status.php',
|
|
||||||
'/cron',
|
|
||||||
'/remote.php',
|
|
||||||
'/ocs',
|
|
||||||
'/apps/',
|
|
||||||
'/custom_apps/',
|
|
||||||
]
|
|
||||||
|
|
||||||
# Игнорируемые IP (Docker сети)
|
|
||||||
docker_ips = [
|
|
||||||
'192.168.64.1',
|
|
||||||
'192.168.65.1',
|
|
||||||
'172.17.0.1',
|
|
||||||
'172.18.0.1',
|
|
||||||
'172.19.0.1',
|
|
||||||
]
|
|
||||||
|
|
||||||
# Игнорируем статические файлы и админку
|
|
||||||
if path.startswith('/static/') or path.startswith('/admin/'):
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Не отслеживаем Nextcloud и Docker запросы
|
|
||||||
if any(path.startswith(p) for p in nextcloud_paths):
|
|
||||||
return False
|
|
||||||
if client_ip in docker_ips:
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def track_page_view(request):
|
|
||||||
"""Основная функция отслеживания просмотров"""
|
|
||||||
if not should_track_request(request):
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
PageView.objects.create(
|
|
||||||
url=request.path,
|
|
||||||
ip_address=get_client_ip(request),
|
|
||||||
user_agent=request.META.get('HTTP_USER_AGENT', '')[:500],
|
|
||||||
referer=request.META.get('HTTP_REFERER', '')[:500],
|
|
||||||
)
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error tracking page: {e}")
|
|
||||||
|
|
||||||
|
|
||||||
def track_view(view_func):
|
|
||||||
"""Декоратор для отслеживания просмотров страниц"""
|
|
||||||
from functools import wraps
|
|
||||||
|
|
||||||
@wraps(view_func)
|
|
||||||
def _wrapped_view(request, *args, **kwargs):
|
|
||||||
# Отслеживаем просмотр перед выполнением view
|
|
||||||
track_page_view(request)
|
|
||||||
return view_func(request, *args, **kwargs)
|
|
||||||
|
|
||||||
return _wrapped_view
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user