230 lines
9.4 KiB
Python
230 lines
9.4 KiB
Python
"""
|
||
Переиспользуемые миксины для представлений mainapp.
|
||
|
||
Этот модуль содержит миксины для стандартизации общей логики в представлениях,
|
||
включая проверку прав доступа, обработку координат и сообщений.
|
||
"""
|
||
|
||
# Standard library imports
|
||
from datetime import datetime
|
||
from typing import Optional, Tuple
|
||
|
||
# Django imports
|
||
from django.contrib import messages
|
||
from django.contrib.auth.mixins import UserPassesTestMixin
|
||
from django.contrib.gis.geos import Point
|
||
|
||
|
||
class RoleRequiredMixin(UserPassesTestMixin):
|
||
"""
|
||
Mixin для проверки роли пользователя.
|
||
|
||
Проверяет, что пользователь имеет одну из требуемых ролей для доступа к представлению.
|
||
|
||
Attributes:
|
||
required_roles (list): Список допустимых ролей для доступа.
|
||
По умолчанию ['admin', 'moderator'].
|
||
|
||
Example:
|
||
class MyView(RoleRequiredMixin, View):
|
||
required_roles = ['admin', 'moderator']
|
||
|
||
def get(self, request):
|
||
# Только пользователи с ролью admin или moderator могут получить доступ
|
||
return render(request, 'template.html')
|
||
"""
|
||
|
||
required_roles = ["admin", "moderator"]
|
||
|
||
def test_func(self) -> bool:
|
||
"""
|
||
Проверяет, имеет ли пользователь требуемую роль.
|
||
|
||
Returns:
|
||
bool: True если пользователь имеет одну из требуемых ролей, иначе False.
|
||
"""
|
||
if not self.request.user.is_authenticated:
|
||
return False
|
||
|
||
if not hasattr(self.request.user, "customuser"):
|
||
return False
|
||
|
||
return self.request.user.customuser.role in self.required_roles
|
||
|
||
|
||
class CoordinateProcessingMixin:
|
||
"""
|
||
Mixin для обработки координат из POST данных форм.
|
||
|
||
Предоставляет методы для извлечения и обработки координат различных типов
|
||
(геолокация, кубсат, оперативники) из POST запроса и применения их к объекту Geo.
|
||
|
||
Example:
|
||
class MyFormView(CoordinateProcessingMixin, FormView):
|
||
def form_valid(self, form):
|
||
geo_instance = Geo()
|
||
self.process_coordinates(geo_instance)
|
||
geo_instance.save()
|
||
return super().form_valid(form)
|
||
"""
|
||
|
||
def process_coordinates(self, geo_instance, prefix: str = "geo") -> None:
|
||
"""
|
||
Обрабатывает координаты из POST данных и применяет их к объекту Geo.
|
||
|
||
Извлекает координаты геолокации, кубсата и оперативников из POST запроса
|
||
и устанавливает соответствующие поля объекта Geo.
|
||
|
||
Args:
|
||
geo_instance: Экземпляр модели Geo для обновления координат.
|
||
prefix (str): Префикс для полей формы (по умолчанию 'geo').
|
||
|
||
Note:
|
||
Метод ожидает следующие поля в request.POST:
|
||
- geo_longitude, geo_latitude: координаты геолокации
|
||
- kupsat_longitude, kupsat_latitude: координаты кубсата
|
||
- valid_longitude, valid_latitude: координаты оперативников
|
||
"""
|
||
# Обрабатываем координаты геолокации
|
||
geo_coords = self._extract_coordinates("geo")
|
||
if geo_coords:
|
||
geo_instance.coords = Point(geo_coords[0], geo_coords[1], srid=4326)
|
||
|
||
# Обрабатываем координаты Кубсата
|
||
kupsat_coords = self._extract_coordinates("kupsat")
|
||
if kupsat_coords:
|
||
geo_instance.coords_kupsat = Point(
|
||
kupsat_coords[0], kupsat_coords[1], srid=4326
|
||
)
|
||
|
||
# Обрабатываем координаты оперативников
|
||
valid_coords = self._extract_coordinates("valid")
|
||
if valid_coords:
|
||
geo_instance.coords_valid = Point(
|
||
valid_coords[0], valid_coords[1], srid=4326
|
||
)
|
||
|
||
def _extract_coordinates(self, coord_type: str) -> Optional[Tuple[float, float]]:
|
||
"""
|
||
Извлекает координаты указанного типа из POST данных.
|
||
|
||
Args:
|
||
coord_type (str): Тип координат ('geo', 'kupsat', 'valid').
|
||
|
||
Returns:
|
||
Optional[Tuple[float, float]]: Кортеж (longitude, latitude) или None,
|
||
если координаты не найдены или невалидны.
|
||
"""
|
||
longitude_key = f"{coord_type}_longitude"
|
||
latitude_key = f"{coord_type}_latitude"
|
||
|
||
longitude = self.request.POST.get(longitude_key)
|
||
latitude = self.request.POST.get(latitude_key)
|
||
|
||
if longitude and latitude:
|
||
try:
|
||
return (float(longitude), float(latitude))
|
||
except (ValueError, TypeError):
|
||
return None
|
||
return None
|
||
|
||
def process_timestamp(self, geo_instance) -> None:
|
||
"""
|
||
Обрабатывает дату и время из POST данных и применяет к объекту Geo.
|
||
|
||
Args:
|
||
geo_instance: Экземпляр модели Geo для обновления timestamp.
|
||
|
||
Note:
|
||
Метод ожидает следующие поля в request.POST:
|
||
- timestamp_date: дата в формате YYYY-MM-DD
|
||
- timestamp_time: время в формате HH:MM
|
||
"""
|
||
timestamp_date = self.request.POST.get("timestamp_date")
|
||
timestamp_time = self.request.POST.get("timestamp_time")
|
||
|
||
if timestamp_date and timestamp_time:
|
||
try:
|
||
naive_datetime = datetime.strptime(
|
||
f"{timestamp_date} {timestamp_time}", "%Y-%m-%d %H:%M"
|
||
)
|
||
geo_instance.timestamp = naive_datetime
|
||
except ValueError:
|
||
# Если формат даты/времени неверный, пропускаем
|
||
pass
|
||
|
||
|
||
class FormMessageMixin:
|
||
"""
|
||
Mixin для стандартизации сообщений об успехе и ошибках в формах.
|
||
|
||
Автоматически добавляет сообщения пользователю при успешной или неуспешной
|
||
обработке формы.
|
||
|
||
Attributes:
|
||
success_message (str): Сообщение при успешной обработке формы.
|
||
error_message (str): Сообщение при ошибке обработки формы.
|
||
|
||
Example:
|
||
class MyFormView(FormMessageMixin, FormView):
|
||
success_message = "Данные успешно сохранены!"
|
||
error_message = "Ошибка при сохранении данных"
|
||
|
||
def form_valid(self, form):
|
||
# Автоматически добавит success_message
|
||
return super().form_valid(form)
|
||
"""
|
||
|
||
success_message = "Операция выполнена успешно"
|
||
error_message = "Произошла ошибка при обработке формы"
|
||
|
||
def form_valid(self, form):
|
||
"""
|
||
Обрабатывает валидную форму и добавляет сообщение об успехе.
|
||
|
||
Args:
|
||
form: Валидная форма Django.
|
||
|
||
Returns:
|
||
HttpResponse: Результат обработки родительского метода form_valid.
|
||
"""
|
||
if self.success_message:
|
||
messages.success(self.request, self.success_message)
|
||
return super().form_valid(form)
|
||
|
||
def form_invalid(self, form):
|
||
"""
|
||
Обрабатывает невалидную форму и добавляет сообщение об ошибке.
|
||
|
||
Args:
|
||
form: Невалидная форма Django.
|
||
|
||
Returns:
|
||
HttpResponse: Результат обработки родительского метода form_invalid.
|
||
"""
|
||
if self.error_message:
|
||
messages.error(self.request, self.error_message)
|
||
return super().form_invalid(form)
|
||
|
||
def get_success_message(self) -> str:
|
||
"""
|
||
Возвращает сообщение об успехе.
|
||
|
||
Может быть переопределен в подклассах для динамического формирования сообщения.
|
||
|
||
Returns:
|
||
str: Сообщение об успехе.
|
||
"""
|
||
return self.success_message
|
||
|
||
def get_error_message(self) -> str:
|
||
"""
|
||
Возвращает сообщение об ошибке.
|
||
|
||
Может быть переопределен в подклассах для динамического формирования сообщения.
|
||
|
||
Returns:
|
||
str: Сообщение об ошибке.
|
||
"""
|
||
return self.error_message
|