Реализована полноценная система мультитенантности на базе django-tenants. Каждый магазин получает изолированную схему БД и поддомен. Основные компоненты: Django-tenants интеграция: - Модели Client (тенант) и Domain в приложении tenants/ - Разделение на SHARED_APPS и TENANT_APPS - Public schema для общей админки - Tenant schemas для изолированных данных магазинов Система регистрации магазинов: - Публичная форма регистрации на /register/ - Модель TenantRegistration для заявок со статусами (pending/approved/rejected) - Валидация schema_name (латиница, 3-63 символа, уникальность) - Проверка на зарезервированные имена (admin, api, www и т.д.) - Админ-панель для модерации заявок с кнопками активации/отклонения Система подписок: - Модель Subscription с планами (триал 90 дней, месяц, квартал, год) - Автоматическое создание триальной подписки при активации - Методы is_expired() и days_left() для проверки статуса - Цветовая индикация в админке (зеленый/оранжевый/красный) Приложения: - tenants/ - управление тенантами, регистрация, подписки - shops/ - точки магазинов/самовывоза (tenant app) - Обновлены миграции для всех приложений Утилиты: - switch_to_tenant.py - переключение между схемами тенантов - Обновлены image_processor и image_service Конфигурация: - urls_public.py - роуты для public schema (админка + регистрация) - urls.py - роуты для tenant schemas (магазины) - requirements.txt - добавлены django-tenants, django-environ, phonenumber-field Документация: - DJANGO_TENANTS_SETUP.md - настройка мультитенантности - TENANT_REGISTRATION_GUIDE.md - руководство по регистрации - QUICK_START.md - быстрый старт - START_HERE.md - общая документация Использование: 1. Пользователь: http://localhost:8000/register/ → заполняет форму 2. Админ: http://localhost:8000/admin/ → активирует заявку 3. Результат: http://{schema_name}.localhost:8000/ - готовый магазин 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
126 lines
5.0 KiB
Python
126 lines
5.0 KiB
Python
"""
|
||
Сервис для получения URL изображений разных размеров.
|
||
Используется в шаблонах и представлениях для удобного доступа к разным версиям.
|
||
"""
|
||
import os
|
||
from django.conf import settings
|
||
from django.core.files.storage import default_storage
|
||
|
||
|
||
class ImageService:
|
||
"""
|
||
Сервис для работы с изображениями разных размеров.
|
||
Динамически строит URL на основе пути к оригинальному файлу.
|
||
"""
|
||
|
||
# Константы для маппинга форматов и расширений файлов
|
||
FORMAT_EXTENSIONS = {
|
||
'JPEG': 'jpg',
|
||
'WEBP': 'webp',
|
||
'PNG': 'png',
|
||
}
|
||
|
||
@staticmethod
|
||
def _get_config():
|
||
"""Получить конфигурацию из settings"""
|
||
return getattr(settings, 'IMAGE_PROCESSING_CONFIG', {})
|
||
|
||
@staticmethod
|
||
def _get_format_config(size_key):
|
||
"""Получить конфигурацию формата для заданного типа изображения"""
|
||
config = ImageService._get_config()
|
||
formats = config.get('formats', {})
|
||
return formats.get(size_key, {'format': 'JPEG', 'quality': 90})
|
||
|
||
@staticmethod
|
||
def _get_file_extension(image_format):
|
||
"""Получить расширение файла для заданного формата"""
|
||
return ImageService.FORMAT_EXTENSIONS.get(image_format, 'jpg')
|
||
|
||
@staticmethod
|
||
def _normalize_size_name(size_key):
|
||
"""Преобразовать 'thumbnail' в 'thumb' для имени файла"""
|
||
return 'thumb' if size_key == 'thumbnail' else size_key
|
||
|
||
@staticmethod
|
||
def get_url(original_image_path, size='medium'):
|
||
"""
|
||
Получает URL изображения нужного размера.
|
||
|
||
Структура хранения: base_path/entity_id/photo_id/size.ext
|
||
Пример: products/123/456/medium.webp
|
||
|
||
Args:
|
||
original_image_path: Путь к оригинальному файлу (из models.image)
|
||
Пример: products/123/456/original.jpg
|
||
size: Размер ('original', 'large', 'medium', 'thumbnail')
|
||
По умолчанию 'medium'
|
||
|
||
Returns:
|
||
str: URL изображения или пустая строка если нет файла
|
||
"""
|
||
if not original_image_path:
|
||
return ''
|
||
|
||
try:
|
||
path_str = str(original_image_path)
|
||
parts = path_str.split('/')
|
||
|
||
if len(parts) < 3:
|
||
return ''
|
||
|
||
# Извлекаем base_path, entity_id, photo_id из пути
|
||
base_path = parts[0] # products, kits, categories
|
||
entity_id = parts[1] # ID сущности
|
||
photo_id = parts[2] # ID фото
|
||
|
||
# Определяем расширение из конфигурации
|
||
format_config = ImageService._get_format_config(size)
|
||
image_format = format_config.get('format', 'JPEG')
|
||
extension = ImageService._get_file_extension(image_format)
|
||
|
||
# Преобразуем thumbnail в thumb
|
||
final_size_name = ImageService._normalize_size_name(size)
|
||
|
||
# Создаем путь и возвращаем URL
|
||
file_path = f"{base_path}/{entity_id}/{photo_id}/{final_size_name}.{extension}"
|
||
return f"{settings.MEDIA_URL}{file_path}"
|
||
|
||
except Exception:
|
||
return ''
|
||
|
||
@staticmethod
|
||
def get_thumbnail_url(original_image_path):
|
||
"""Получить URL миниатюры (150x150)"""
|
||
return ImageService.get_url(original_image_path, 'thumbnail')
|
||
|
||
@staticmethod
|
||
def get_medium_url(original_image_path):
|
||
"""Получить URL среднего размера (400x400)"""
|
||
return ImageService.get_url(original_image_path, 'medium')
|
||
|
||
@staticmethod
|
||
def get_large_url(original_image_path):
|
||
"""Получить URL большого размера (800x800)"""
|
||
return ImageService.get_url(original_image_path, 'large')
|
||
|
||
@staticmethod
|
||
def get_original_url(original_image_path):
|
||
"""Получить URL оригинального изображения"""
|
||
return ImageService.get_url(original_image_path, 'original')
|
||
|
||
@staticmethod
|
||
def get_all_urls(original_image_path):
|
||
"""
|
||
Получить все версии изображения.
|
||
|
||
Returns:
|
||
dict: {'original': url, 'thumbnail': url, 'medium': url, 'large': url}
|
||
"""
|
||
return {
|
||
'original': ImageService.get_original_url(original_image_path),
|
||
'thumbnail': ImageService.get_thumbnail_url(original_image_path),
|
||
'medium': ImageService.get_medium_url(original_image_path),
|
||
'large': ImageService.get_large_url(original_image_path),
|
||
}
|