chore(ldap): remove unused ExternalSystemSyncMixin

Миксин больше не используется после перехода на Django сигналы
This commit is contained in:
lizerk 2026-03-18 16:15:33 +03:00
parent 5585758ede
commit f2f8a67dd3

View File

@ -1,261 +0,0 @@
"""
Универсальный миксин для синхронизации DB операций с внешними системами (LDAP, AD, etc).
Использование:
class EmployeeViewSet(ExternalSystemSyncMixin, viewsets.ModelViewSet):
external_sync_service = DirectoryService()
def get_external_sync_method(self, action):
return {
'create': 'create_user_ldap',
'update': 'update_user_ldap',
'destroy': 'delete_user_ldap',
}.get(action)
def prepare_external_data(self, instance, action):
if action == 'create':
return {'user': instance}
return {'user_id': instance.id}
"""
import logging
from functools import wraps
from django.db import transaction
from rest_framework import status
from rest_framework.response import Response
logger = logging.getLogger(__name__)
class ExternalSystemSyncMixin:
"""
Миксин для автоматической синхронизации операций ViewSet с внешними системами.
Паттерн:
1. Выполняет DB операцию в транзакции
2. При успехе - вызывает метод внешней системы
3. При ошибке внешней системы - откатывает DB транзакцию
Attributes:
external_sync_service: Сервис для работы с внешней системой (например, DirectoryService)
external_sync_enabled: Флаг включения синхронизации (по умолчанию True)
external_sync_async: Использовать асинхронную синхронизацию через Celery
"""
external_sync_service = None
external_sync_enabled = True
external_sync_async = False
def get_external_sync_method(self, action):
"""
Возвращает имя метода внешнего сервиса для данного action.
Args:
action: 'create', 'update', 'partial_update', 'destroy'
Returns:
str: имя метода сервиса или None если синхронизация не нужна
"""
raise NotImplementedError("Переопределите get_external_sync_method()")
def prepare_external_data(self, instance, action):
"""
Подготавливает данные для передачи во внешний сервис.
Args:
instance: Инстанс модели Django
action: Тип операции
Returns:
dict: данные для метода внешнего сервиса
"""
return {'instance': instance}
def handle_external_sync_error(self, error, instance, action):
"""
Обработка ошибки синхронизации с внешней системой.
Args:
error: Exception от внешней системы
instance: Инстанс модели
action: Тип операции
Returns:
Response или None (если None - будет поднята ошибка)
"""
logger.error(
f"External sync failed for {action} on {instance}: {error}")
return Response(
{'detail': f'Ошибка синхронизации с внешней системой: {str(error)}'},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
def perform_create(self, serializer):
"""Переопределение create с синхронизацией."""
return self._perform_with_sync(serializer, 'create', super().perform_create)
def perform_update(self, serializer):
"""Переопределение update с синхронизацией."""
action = 'partial_update' if self.request.method == 'PATCH' else 'update'
return self._perform_with_sync(serializer, action, super().perform_update)
def perform_destroy(self, instance):
"""Переопределение destroy с синхронизацией."""
return self._perform_with_sync_instance(instance, 'destroy', super().perform_destroy)
def _perform_with_sync(self, serializer, action, db_operation):
"""
Выполняет DB операцию и синхронизацию с внешней системой.
Args:
serializer: DRF serializer с валидированными данными
action: Тип операции
db_operation: Функция для выполнения DB операции
"""
if not self.external_sync_enabled:
return db_operation(serializer)
sync_method_name = self.get_external_sync_method(action)
if not sync_method_name or not self.external_sync_service:
return db_operation(serializer)
# Атомарная транзакция: сохраняем в БД
with transaction.atomic():
db_operation(serializer)
instance = serializer.instance
# Подготавливаем данные для внешней системы
external_data = self.prepare_external_data(instance, action)
try:
# Вызываем метод внешней системы
sync_method = getattr(
self.external_sync_service, sync_method_name)
result = sync_method(**external_data)
# Если внешняя система вернула данные для обновления БД
if isinstance(result, dict) and result.get('update_db'):
for field, value in result['update_db'].items():
setattr(instance, field, value)
instance.save(update_fields=list(
result['update_db'].keys()))
# Вызываем кастомный обработчик результата если определен
if hasattr(self, 'handle_external_sync_result'):
self.handle_external_sync_result(instance, result, action)
logger.info(
f"External sync success for {action} on {instance}")
except Exception as e:
# При ошибке внешней системы - откатываем транзакцию
logger.error(f"External sync failed, rolling back: {e}")
transaction.set_rollback(True)
# Вызываем обработчик ошибок
error_response = self.handle_external_sync_error(
e, instance, action)
if error_response:
raise ExternalSyncError(error_response)
raise
def _perform_with_sync_instance(self, instance, action, db_operation):
"""
Выполняет DB операцию на инстансе с синхронизацией.
Args:
instance: Инстанс модели
action: Тип операции
db_operation: Функция для выполнения DB операции
"""
if not self.external_sync_enabled:
return db_operation(instance)
sync_method_name = self.get_external_sync_method(action)
if not sync_method_name or not self.external_sync_service:
return db_operation(instance)
# Сохраняем данные перед удалением
external_data = self.prepare_external_data(instance, action)
# Атомарная транзакция: удаляем из БД
with transaction.atomic():
db_operation(instance)
try:
# Вызываем метод внешней системы
sync_method = getattr(
self.external_sync_service, sync_method_name)
sync_method(**external_data)
logger.info(
f"External sync success for {action} on {instance}")
except Exception as e:
# При ошибке внешней системы - откатываем транзакцию
logger.error(f"External sync failed, rolling back: {e}")
transaction.set_rollback(True)
error_response = self.handle_external_sync_error(
e, instance, action)
if error_response:
raise ExternalSyncError(error_response)
raise
class ExternalSyncError(Exception):
"""Исключение с Response для возврата клиенту."""
def __init__(self, response):
self.response = response
super().__init__()
def with_external_sync(external_service_attr='external_sync_service', method_map=None):
"""
Декоратор для методов ViewSet, добавляющий синхронизацию с внешней системой.
Usage:
@with_external_sync(method_map={'create': 'create_user_ldap'})
def create(self, request, *args, **kwargs):
return super().create(request, *args, **kwargs)
Args:
external_service_attr: Имя атрибута ViewSet с сервисом
method_map: Маппинг action -> метод внешнего сервиса
"""
def decorator(func):
@wraps(func)
def wrapper(self, request, *args, **kwargs):
# Выполняем оригинальный метод
with transaction.atomic():
response = func(self, request, *args, **kwargs)
# Получаем сервис и метод для синхронизации
service = getattr(self, external_service_attr, None)
if not service or not method_map:
return response
action = self.action
sync_method_name = method_map.get(action)
if not sync_method_name:
return response
try:
# Получаем созданный/обновленный инстанс
instance = getattr(response, 'data', {}).get('id')
if isinstance(response.data, dict):
instance = self.get_queryset().get(
id=response.data['id'])
# Вызываем метод внешней системы
sync_method = getattr(service, sync_method_name)
sync_method(instance=instance)
except Exception as e:
transaction.set_rollback(True)
logger.error(f"External sync failed: {e}")
raise
return response
return wrapper
return decorator