from django.db import models from django.core.exceptions import ValidationError from abc import ABC, abstractmethod class IntegrationType(models.TextChoices): MARKETPLACE = 'marketplace', 'Маркетплейс' PAYMENT = 'payment', 'Платёжная система' SHIPPING = 'shipping', 'Служба доставки' class BaseIntegration(models.Model): """ Абстрактный базовый класс для всех интеграций. Содержит общие поля и логику валидации. """ integration_type = models.CharField( max_length=20, choices=IntegrationType.choices, editable=False, verbose_name="Тип интеграции" ) is_active = models.BooleanField( default=True, verbose_name="Активна", db_index=True, help_text="Интеграция используется только если включена здесь и в системных настройках" ) name = models.CharField( max_length=100, verbose_name="Название", help_text="Произвольное название для удобства" ) created_at = models.DateTimeField( auto_now_add=True, verbose_name="Дата создания" ) updated_at = models.DateTimeField( auto_now=True, verbose_name="Дата обновления" ) # Общие credential поля (можно переопределить в наследниках) api_key = models.CharField( max_length=255, blank=True, verbose_name="API ключ", help_text="Будет зашифрован при сохранении" ) api_secret = models.CharField( max_length=255, blank=True, verbose_name="API секрет", help_text="Будет зашифрован при сохранении" ) # Дополнительные настройки в JSON для гибкости extra_config = models.JSONField( default=dict, blank=True, verbose_name="Доп. настройки" ) class Meta: abstract = True ordering = ['-is_active', 'name'] indexes = [ models.Index(fields=['is_active', 'integration_type']), ] def __str__(self): return f"{self.get_integration_type_display()}: {self.name}" @property def is_configured(self) -> bool: """ Проверить, есть ли необходимые credentials. Можно переопределить в наследниках. """ return bool(self.api_key) def clean(self): """Валидацияcredentials""" if self.is_active and not self.is_configured: raise ValidationError({ 'api_key': 'API ключ обязателен для активной интеграции' })