улучшению приложения main
Анализ кода выявил несколько направлений для улучшения — от критических вопросов безопасности до удобства сопровождения.
🔴 Критические / Безопасность
1. Отсутствует rate-limiting на форме обратной связи (forms.py, views.py)
Проблема: ContactView.post() принимает любое количество запросов без ограничений. Злоумышленник может за секунды завалить почту тысячами сообщений.
Решение: Добавить декоратор django-ratelimit или ручную проверку через кэш:
python
# views.py — в ContactView.post()
from django.core.cache import cache
def post(self, request, *args, **kwargs):
ip = request.META.get('REMOTE_ADDR')
cache_key = f'contact_form_{ip}'
attempts = cache.get(cache_key, 0)
if attempts >= 5: # не более 5 отправок в час
messages.error(request, 'Слишком много попыток. Попробуйте позже.')
return redirect(reverse('main:contacts'))
cache.set(cache_key, attempts + 1, 3600)
# ... обычная обработка
2. Race condition в счётчике просмотров (views.py#L436)
Проблема: Page.objects.filter(pk=...).update(views=self.object.views + 1) — значение self.object.views берётся из кэша объекта и не является атомарным при параллельных запросах.
Решение:
python
from django.db.models import F
Page.objects.filter(pk=self.object.pk).update(views=F('views') + 1)
3. Открытые URL статистики логов (urls.py)
Проблема: /log-stats/ и /error-log/ — публичные URL, хотя в view проверяется is_staff/is_superuser. Лучше защищать на уровне URL или middleware, а не полагаться только на view.
Решение: Перенести эти страницы в поддиректорию /admin/ или добавить login_required на уровне URL.
🟠 Производительность
4. SiteSettings.load() вызывается в каждом view без использования кэша context processor (views.py)
Проблема: ContactView, AboutView, PageDetailView каждый раз вызывают SiteSettings.load() напрямую, игнорируя уже готовый site_settings из context processor.
Решение: В этих view брать данные из context['site_settings'] (он уже установлен context processor site_settings), а не делать новый запрос к БД.
python
# Вместо:
site_settings = SiteSettings.load()
# Использовать:
site_settings = context.get('site_settings') or SiteSettings.load()
5. N+1 проблема в sidebar_data context processor (context_processors.py#L140-L144)
Проблема: PortfolioItem.objects.all().order_by("-created_at")[:3] — если в шаблоне потом обращаются к item.category или item.images, это вызовет N+1 запросов.
Решение: Добавить select_related / prefetch_related:
python
PortfolioItem.objects.select_related('category').order_by('-created_at')[:3]
6. admin_dashboard_stats кэшируется на 60 сек, но выполняет ~15 запросов к БД + системные вызовы
Проблема: При каждом обновлении админки все 15 запросов выполняются сразу. На нагруженном сервере это дорого.
Решение: Вынести сбор статистики в фоновую Celery-задачу, которая запускается по расписанию и кладёт результат в кэш. view только читает кэш.
🟡 Надёжность / Корректность
7. get_previous_page() и get_next_page() работают некорректно при одинаковом order (models.py#L613-L633)
Проблема: Фильтрация Q(order=self.order, created_at__lt=self.created_at) не даёт правильного результата, если обе страницы имеют одинаковый order и одинаковое время создания (такое бывает при импорте данных).
Решение: Добавить pk как финальный тай-брейкер:
python
Q(order=self.order, created_at__lt=self.created_at) |
Q(order=self.order, created_at=self.created_at, pk__lt=self.pk)
8. ContactForm не валидирует email/телефон отдельно (forms.py)
Проблема: Поле contact принимает строку без проверки формата — можно ввести абракадабру.
Решение:
python
import re
def clean_contact(self):
contact = self.cleaned_data.get('contact', '').strip()
email_re = r'^[\w.+-]+@[\w-]+\.[a-z]{2,}$'
phone_re = r'^[\+\d\s\-\(\)]{7,20}$'
if not re.match(email_re, contact) and not re.match(phone_re, contact):
raise forms.ValidationError('Введите корректный email или номер телефона.')
return contact
9. PageDetailView кэширует страницу с @cache_page(60 * 10) — счётчик просмотров обновляется только один раз за 10 минут
Проблема: Кэширование всей страницы и попытка инкрементировать счётчик просмотров несовместимы — при кэшированном ответе get() не вызывается.
Решение: Убрать кэш страницы с PageDetailView или перенести подсчёт просмотров в JavaScript (AJAX-запрос на отдельный endpoint), что даёт более точную статистику и не мешает кэшированию.
🟢 Функциональные улучшения
10. Нет сохранения сообщений из ContactForm в базу данных
Проблема: Сейчас сообщение просто отправляется на email. Если письмо не дошло (сбой SMTP) — данные потеряны навсегда.
Решение: Создать модель ContactMessage и сохранять каждое сообщение в БД перед отправкой email. Это даёт историю обращений прямо в admin-панели.
python
# models.py
class ContactMessage(models.Model):
name = models.CharField('Имя', max_length=100)
contact = models.CharField('Контакт', max_length=150)
message = models.TextField('Сообщение')
ip_address = models.GenericIPAddressField('IP', blank=True, null=True)
is_read = models.BooleanField('Прочитано', default=False)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name = 'Сообщение обратной связи'
verbose_name_plural = 'Сообщения обратной связи'
ordering = ['-created_at']
11. SearchView не ищет по отзывам и тикетам
Проблема: Поиск охватывает только News, PortfolioItem и Page. Отзывы и база знаний остаются вне поиска.
Решение: Добавить поиск по reviews.Review и (если есть) knowledge_base.Article по аналогии с существующими блоками.
12. Нет уведомления администратора при создании нового тикета / сообщения в ContactForm
Уже есть send_mail, но нет уведомления в реальном времени. Можно добавить:
Django Channels — WebSocket push в admin-панель
Telegram Bot — уведомление в Telegram (проще и без зависимостей)
13. Нет автоматической очистки кэша при изменении SiteSettings
Проблема: После изменения настроек сайта (логотип, телефон) кэш живёт до 5 минут — пользователи видят старые данные.
Решение: Добавить сигнал post_save:
python
# signals.py
from django.db.models.signals import post_save
from django.core.cache import cache
@receiver(post_save, sender=SiteSettings)
def clear_site_settings_cache(sender, **kwargs):
cache.delete('site_settings')
cache.delete('menu_pages')
cache.delete('dynamic_menus_data')
cache.delete('statistics_banners_user')
cache.delete('statistics_banners_staff')
cache.delete('statistics_banners_admin')
🔵 Качество кода
14. Повторяющийся паттерн f"{page_title} - {site_settings.logo_text}" во всех view
Этот паттерн встречается в ContactView, AboutView, LogStatsView, PageDetailView — 4+ раза.
Решение: Вынести в вспомогательный метод BaseView:
python
class BaseView(TemplateView):
def build_page_title(self, title: str, site_settings=None) -> str:
if site_settings and site_settings.logo_text:
return f"{title} - {site_settings.logo_text}"
return title
15. В admin_dashboard_stats context processor выполняется SiteSettings.load() отдельно от основного кэша
Это дублирует запрос. Нужно использовать cache.get('site_settings') как primary источник.
16. Отсутствуют тесты для ContactView.post() и форм
В tests.py нет покрытия для:
Успешной отправки формы
Невалидных данных формы
Rate limiting
SearchViewс реальными данными
📋 Приоритизация
| № | Улучшение | Приоритет | Сложность |
|---|---|---|---|
| 2 | F('views') + 1 — атомарный счётчик | 🔴 Высокий | ⭐ Очень просто |
| 13 | Инвалидация кэша через post_save | 🟠 Средний | ⭐ Просто |
| 4 | Устранение лишних SiteSettings.load() | 🟠 Средний | ⭐⭐ Просто |
| 1 | Rate-limiting для ContactForm | 🔴 Высокий | ⭐⭐ Просто |
| 8 | Валидация email/телефона | 🟠 Средний | ⭐⭐ Просто |
| 14 | Рефакторинг build_page_title | 🟡 Низкий | ⭐ Очень просто |
| 10 | Сохранение сообщений в БД | 🟠 Средний | ⭐⭐⭐ Средне |
| 9 | Счётчик просмотров через AJAX | 🟡 Низкий | ⭐⭐⭐ Средне |
| 11 | Расширение поиска | 🟡 Низкий | ⭐⭐ Просто |
| 6 | Celery для dashboard stats | 🟡 Низкий | ⭐⭐⭐⭐ Сложно |
Обсуждение статьи
0К этой статье пока нет комментариев. Будьте первым, кто выразит свое мнение!
Оставить комментарий
Связанные новости
4Похожие материалы
Исправления и улучшения Admin-панели
План реализации: Премиальный космический дизайн и инновационный интерфейс панели администратора
План реализации: Перевод описаний на русский и новые премиальные стилевые решения
Улучшения и исправления для приложения Main