From 2f557f3f9b32a12452c264b98b7883fdce514d61 Mon Sep 17 00:00:00 2001 From: Andrey Smakotin Date: Sat, 25 Oct 2025 16:50:08 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD=D1=8B=20?= =?UTF-8?q?=D1=84=D0=B0=D0=B9=D0=BB=D1=8B=20=D0=B4=D0=BE=D0=BA=D1=83=D0=BC?= =?UTF-8?q?=D0=B5=D0=BD=D1=82=D0=B0=D1=86=D0=B8=D0=B8=20=D0=BF=D0=BE=20?= =?UTF-8?q?=D0=B8=D0=B7=D0=BE=D0=B1=D1=80=D0=B0=D0=B6=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FRONTEND_IMAGES_GUIDE.md | 397 ------------------------ IMAGE_CONFIGURATION_REPORT.md | 234 --------------- IMAGE_STORAGE_STRATEGY.md | 377 ----------------------- IMAGE_SYSTEM_EXAMPLES.md | 547 ---------------------------------- IMPLEMENTATION_SUMMARY.txt | 138 --------- QUICK_START_IMAGES.md | 249 ---------------- 6 files changed, 1942 deletions(-) delete mode 100644 FRONTEND_IMAGES_GUIDE.md delete mode 100644 IMAGE_CONFIGURATION_REPORT.md delete mode 100644 IMAGE_STORAGE_STRATEGY.md delete mode 100644 IMAGE_SYSTEM_EXAMPLES.md delete mode 100644 IMPLEMENTATION_SUMMARY.txt delete mode 100644 QUICK_START_IMAGES.md diff --git a/FRONTEND_IMAGES_GUIDE.md b/FRONTEND_IMAGES_GUIDE.md deleted file mode 100644 index c766545..0000000 --- a/FRONTEND_IMAGES_GUIDE.md +++ /dev/null @@ -1,397 +0,0 @@ -# Руководство по использованию изображений на фронтенде - -## Обзор - -Система автоматически служит изображения разных размеров в зависимости от контекста: - -- **Списки товаров**: миниатюры (150×150) - быстрая загрузка -- **Карточки товаров**: средний размер (400×400) - хороший баланс -- **Галереи и модальные окна**: большой размер (800×800) - высокое качество -- **Полноэкранный просмотр**: оригинал - максимальное качество - -## Использованные размеры в шаблонах - -### 1. all_products_list.html (Объединённый список товаров и комплектов) - -```django - -{{ item.name }} -``` - -**Размер на диске**: 438B (93% экономия) -**Использование**: Таблица со списком товаров, быстрая загрузка - ---- - -### 2. product_list.html (Список только товаров) - -```django - -{{ product.name }} -``` - -**Размер на диске**: 438B (93% экономия) -**Использование**: Табличное отображение товаров - ---- - -### 3. productkit_list.html (Список комплектов) - -```django - -{{ kit.name }} -``` - -**Размер на диске**: 438B (93% экономия) -**Использование**: Табличное отображение комплектов - ---- - -### 4. product_detail.html (Детали товара с галереей) - -**Миниатюры в сетке:** -```django - -Фото товара -``` - -**Файл**: 438B -**Использование**: Маленькие превью для клика - ---- - -**Модальное окно галереи (carousel):** -```django - -Фото товара -``` - -**Файл**: 5.6K -**Использование**: Полноэкранный просмотр, хорошее качество - ---- - -### 5. productkit_detail.html (Детали комплекта) - -**Боковая панель с фото:** -```django - -{{ kit.name }} -``` - -**Файл**: 2.9K -**Использование**: Кликабельные превью в боку - ---- - -**Модальное окно при клике:** -```django - - -``` - -**Файл**: 5.6K -**Использование**: Полноэкранный просмотр одного фото - ---- - -### 6. category_detail.html (Детали категории) - -```django - -Фото категории -``` - -**Файл**: 2.9K -**Использование**: Картинка категории в сетке - ---- - -## Доступные методы - -Каждая Photo-модель имеет методы для получения URL разных размеров: - -```python -photo = ProductPhoto.objects.first() - -# Получить URL миниатюры (150×150) -photo.get_thumbnail_url() -# → /media/products/thumbnails/image_12345.jpg (438B) - -# Получить URL среднего размера (400×400) -photo.get_medium_url() -# → /media/products/medium/image_12345.jpg (2.9K) - -# Получить URL большого размера (800×800) -photo.get_large_url() -# → /media/products/large/image_12345.jpg (5.6K) - -# Получить URL оригинала (без изменений) -photo.get_original_url() -# → /media/products/originals/image_12345.jpg (6.1K) - -# Получить все URL за раз -photo.get_all_urls() -# → { -# 'thumbnail': '/media/products/thumbnails/...', -# 'medium': '/media/products/medium/...', -# 'large': '/media/products/large/...', -# 'original': '/media/products/originals/...' -# } -``` - -То же самое для ProductKitPhoto и ProductCategoryPhoto. - ---- - -## Рекомендации по использованию - -### Списки и таблицы -```django - -{% if item.photos.all %} - {{ item.name }} -{% endif %} -``` - -**Почему**: Список может содержать 50+ товаров, миниатюры загружаются мгновенно - ---- - -### Карточки товаров -```django - -
- {{ product.name }} -
-``` - -**Почему**: Карточка требует хорошего качества, но не нужна полная 800×800 - ---- - -### Модальные окна и галереи -```django - - -``` - -**Почему**: Пользователь просматривает один товар, качество важнее - ---- - -### Ссылка на оригинал -```django - - - Скачать оригинал - -``` - -**Почему**: Для печати или отправки по почте нужно максимальное качество - ---- - -## Производительность - -### Пример загрузки страницы: - -**Список товаров (20 товаров)**: -- 20 × 438B = 8.76 КБ -- Время загрузки: ~100мс на медленном 3G - -**VS** если бы были оригиналы: -- 20 × 6.1K = 122 КБ -- Время загрузки: ~1.2сек на медленном 3G - -**Экономия: 93% трафика, 12× быстрее** ⚡ - ---- - -## Адаптивный дизайн - -Система изображений работает на всех устройствах: - -```django - -{{ product.name }} -``` - -- На мобильных: 50px → выглядит хорошо -- На планшетах: 50px → увеличение не требуется -- На десктопе: 50px → оптимально - -Для больших экранов используются большие размеры (800×800) в модальном окне. - ---- - -## Примеры в JavaScript - -Если нужно работать с изображениями через JavaScript: - -```javascript -// Получить URL через атрибут data -const largeImageUrl = document.querySelector('[data-large-url]').dataset.largeUrl; - -// Или из HTML через обычный селектор -const img = document.querySelector('.product-image'); -const src = img.src; // /media/products/medium/image_12345.jpg -``` - ---- - -## Загрузка изображений - -### Через админку: -1. Откройте товар/комплект/категорию в админке -2. В секции "Фото товара" загрузите изображение -3. **Система автоматически создаст все 4 размера!** -4. Фото появится во всех шаблонах с правильным размером - -### Через API (если нужно): -```python -from products.models import ProductPhoto -from products.utils.image_processor import ImageProcessor - -photo = ProductPhoto(product=product) -photo.image = request.FILES['image'] -photo.save() # Все размеры создадутся автоматически -``` - ---- - -## Кэширование и оптимизация - -### Текущая реализация: -- Каждый размер сохраняется на диск (быстро) -- URL вычисляется динамически (не нужна дополнительная БД) - -### Будущие оптимизации (опционально): -- Redis кэширование URL -- WebP формат для современных браузеров -- Ленивая загрузка изображений (lazy loading) -- CDN интеграция - ---- - -## Поиск и устранение проблем - -### Изображение не загружается -1. Проверьте консоль браузера (F12 → Network) -2. Правильный ли URL? (должен быть `/media/products/...`) -3. Загруженное ли изображение в админке? - -### Размер слишком мал/велик -1. Используйте правильный метод: - - `get_thumbnail_url()` → для списков - - `get_medium_url()` → для карточек - - `get_large_url()` → для галерей - - `get_original_url()` → для оригинала - -### Качество плохое -- Используйте `get_large_url()` или `get_original_url()` вместо `get_thumbnail_url()` - ---- - -## Примеры полных шаблонов - -### Пример 1: Сетка товаров (Bootstrap) - -```django -
- {% for product in products %} -
-
- - {% if product.photos.first %} - {{ product.name }} - {% endif %} - -
-
{{ product.name }}
-

{{ product.description|truncatewords:20 }}

- - Открыть → - -
-
-
- {% endfor %} -
-``` - -### Пример 2: Галерея с модальным окном - -```django -
- {% for photo in product.photos.all %} -
- -
- {{ product.name }} -
-
- - - - {% endfor %} -
-``` - ---- - -## Итоги - -✅ **Автоматическое масштабирование** - правильный размер в правильном месте -✅ **Экономия трафика** - 90% для миниатюр -✅ **Быстрая загрузка** - миниатюры 438B -✅ **Высокое качество** - большие размеры 800×800 для просмотра -✅ **Простой API** - всего 4 метода в шаблонах -✅ **Полная автоматизация** - создание размеров при загрузке - -Система готова к использованию! 🎉 diff --git a/IMAGE_CONFIGURATION_REPORT.md b/IMAGE_CONFIGURATION_REPORT.md deleted file mode 100644 index 100e242..0000000 --- a/IMAGE_CONFIGURATION_REPORT.md +++ /dev/null @@ -1,234 +0,0 @@ -# Отчет: Вынос конфигурации размеров и форматов изображений в settings - -## Резюме - -Успешно вынесена конфигурация размеров, форматов и качества изображений из кода в `settings.IMAGE_PROCESSING_CONFIG`. Система теперь поддерживает разные форматы (JPEG для оригинала, WebP для других размеров) с динамическими параметрами качества. - ---- - -## Что было реализовано - -### 1. ✓ Добавлена конфигурация в settings.py - -**Файл:** `myproject/myproject/settings.py:130-173` - -```python -IMAGE_PROCESSING_CONFIG = { - 'formats': { - 'original': { - 'format': 'JPEG', - 'quality': 100, - 'max_width': 2160, - 'max_height': 2160, - 'description': 'Original image (4K max, JPEG format)' - }, - 'large': { - 'format': 'WEBP', - 'quality': 90, - 'width': 1200, - 'height': 1200, - 'description': 'Large image (1200x1200, WebP format)' - }, - 'medium': { - 'format': 'WEBP', - 'quality': 85, - 'width': 600, - 'height': 600, - 'description': 'Medium image (600x600, WebP format)' - }, - 'thumbnail': { - 'format': 'WEBP', - 'quality': 80, - 'width': 200, - 'height': 200, - 'description': 'Thumbnail (200x200, WebP format)' - }, - }, - 'folders': { - 'original': 'originals', - 'large': 'large', - 'medium': 'medium', - 'thumbnail': 'thumbnails', - } -} -``` - -### 2. ✓ Полностью переписан ImageProcessor - -**Файл:** `myproject/products/utils/image_processor.py` - -**Основные изменения:** -- Все SIZES, SIZE_FOLDERS и JPEG_QUALITY теперь берутся из settings -- Добавлены методы для динамического получения конфигурации: - - `_get_config()` - получить конфигурацию из settings - - `_get_size_dimensions(size_key)` - получить размеры для типа изображения - - `_get_format_config(size_key)` - получить конфиг формата - - `_get_folder(size_key)` - получить папку для сохранения - -- Полностью переработан метод `_save_image_version()`: - - Поддерживает разные форматы (JPEG, WebP, PNG) - - Использует качество из конфигурации для каждого типа - - Определяет расширение файла в зависимости от формата - - Масштабирует оригинал если больше 2160×2160 - - **Делает изображение квадратным** (добавляет белый фон если нужно) - -- Обновлена функция `delete_all_versions()`: - - Учитывает разные расширения файлов при удалении - - Работает с новой структурой конфигурации - -### 3. ✓ Обновлен ImageService - -**Файл:** `myproject/products/utils/image_service.py` - -**Основные изменения:** -- Добавлены методы для работы с конфигурацией: - - `_get_config()` - получить конфигурацию - - `_get_size_folders()` - получить папки из конфигурации - - `_get_format_config(size_key)` - получить конфиг формата - - `_get_file_extension(size_key)` - получить расширение для типа - -- Полностью переработан метод `get_url()`: - - Поддерживает разные расширения (.jpg, .webp, .png) - - Корректно парсит имена файлов с разными расширениями - - Генерирует правильный URL для каждого размера с его расширением - ---- - -## Поддерживаемые форматы - -| Формат | Тип | Поддержка | Примечание | -|--------|-----|----------|-----------| -| **JPEG** | Оригинал | ✓ | Качество: 100 | -| **WebP** | Large, Medium, Thumbnail | ✓ | Оптимизация размера | -| **PNG** | Все | ✓ | При необходимости | -| **GIF** | Входной | ✓ | Конвертируется в RGB | -| **TIFF** | Входной | ✓ | Конвертируется в RGB | -| **HEIC** | Входной | ✓ | Конвертируется в RGB | - ---- - -## Размеры и качество - -| Тип | Размер | Формат | Качество | -|-----|--------|--------|----------| -| **Original** | 2160×2160 макс | JPEG | 100 | -| **Large** | 1200×1200 | WebP | 90 | -| **Medium** | 600×600 | WebP | 85 | -| **Thumbnail** | 200×200 | WebP | 80 | - ---- - -## Логика масштабирования оригинала - -**Для оригинала (original):** -1. Если размер больше 2160×2160 → масштабировать с сохранением пропорций -2. Если размер меньше или равен 2160×2160 → оставить как есть -3. **Всегда делать квадратным** → добавить белый фон если нужно - -**Примеры:** -- 3000×2000 → масштабируется до ≈2160×1440, потом до 2160×2160 (с белым фоном) -- 1000×1000 → остается 1000×1000, потом до 1000×1000 квадратное (без изменений) -- 1500×800 → остается 1500×800, потом до 1500×1500 (с белым фоном сверху/снизу) - ---- - -## Примеры создаваемых файлов - -### После загрузки изображения "robot-50cm": - -``` -products/ -├── originals/ -│ └── robot-50cm_1729611234567_original.jpg (JPEG, качество 100) -├── large/ -│ └── robot-50cm_1729611234567_large.webp (WebP, качество 90) -├── medium/ -│ └── robot-50cm_1729611234567_medium.webp (WebP, качество 85) -└── thumbnails/ - └── robot-50cm_1729611234567_thumbnail.webp (WebP, качество 80) -``` - -### Примеры генерируемых URL: - -``` -/media/products/originals/robot-50cm_1729611234567_original.jpg -/media/products/large/robot-50cm_1729611234567_large.webp -/media/products/medium/robot-50cm_1729611234567_medium.webp -/media/products/thumbnails/robot-50cm_1729611234567_thumbnail.webp -``` - ---- - -## Гибкость конфигурации - -Теперь можно легко изменять параметры без изменения кода: - -```python -# Например, если нужно изменить качество large с 90 на 95: -IMAGE_PROCESSING_CONFIG = { - 'formats': { - 'large': { - 'format': 'WEBP', - 'quality': 95, # ← измененное значение - ... - } - } -} - -# Или если нужен PNG для оригинала: -'original': { - 'format': 'PNG', # ← вместо JPEG - 'quality': 100, - ... -} - -# Или для WebP оригинала с качеством 95: -'original': { - 'format': 'WEBP', # ← вместо JPEG - 'quality': 95, - ... -} -``` - ---- - -## Тестирование - -Конфигурация успешно протестирована в Django shell: - -``` -✓ IMAGE_PROCESSING_CONFIG loaded successfully! -✓ All format configurations present -✓ ImageService generates correct URLs with proper extensions -✓ WebP files use correct extensions (.webp) -✓ Original uses JPEG format (.jpg) -``` - ---- - -## Преимущества решения - -1. **Гибкость** - параметры хранятся в settings, легко менять -2. **Масштабируемость** - можно добавлять новые размеры без изменения кода -3. **Производительность** - WebP вместо JPEG для меньших размеров уменьшает размер файлов на 20-30% -4. **Качество** - JPEG качество 100 для оригинала гарантирует максимальное качество -5. **Читаемость** - квадратные изображения более универсальны для использования -6. **Совместимость** - поддержка всех популярных форматов при загрузке - ---- - -## Файлы измененные - -1. **myproject/myproject/settings.py** - добавлена IMAGE_PROCESSING_CONFIG -2. **myproject/products/utils/image_processor.py** - полностью переработан для динамической конфигурации -3. **myproject/products/utils/image_service.py** - обновлен для работы с разными расширениями - ---- - -## Заключение - -Система обработки изображений успешно переведена на конфигурируемую архитектуру. Все параметры (размеры, форматы, качество) теперь находятся в settings.IMAGE_PROCESSING_CONFIG и могут быть легко изменены без дополнительных изменений кода. - -**Статус:** ✓ **ГОТОВО К ИСПОЛЬЗОВАНИЮ** - -Новые загружаемые изображения будут автоматически обрабатываться согласно новой конфигурации. diff --git a/IMAGE_STORAGE_STRATEGY.md b/IMAGE_STORAGE_STRATEGY.md deleted file mode 100644 index 2f50d4e..0000000 --- a/IMAGE_STORAGE_STRATEGY.md +++ /dev/null @@ -1,377 +0,0 @@ -# Стратегия хранения и обработки изображений - -## Обзор системы - -Система автоматически хранит одно большое оригинальное изображение и создает несколько оптимизированных версий для разных сценариев использования в приложении. - -**Преимущества:** -- ✅ Оригинальное изображение сохраняется в полном качестве -- ✅ Автоматическое создание всех размеров при загрузке -- ✅ Оптимизация под разные части приложения (списки, карточки, просмотр) -- ✅ Быстрая загрузка из-за меньшего размера файлов -- ✅ Экономия трафика и дискового пространства - ---- - -## Размеры изображений - -| Размер | Размер (px) | Использование | -|--------|------------|---------------| -| **thumbnail** | 150×150 | Каталоги, списки товаров, сетки | -| **medium** | 400×400 | Карточки товаров, превью в админке | -| **large** | 800×800 | Полноразмерный просмотр на фронте | -| **original** | Без изменений* | Архив, печать, экспорт | - -\* *Сохраняется в JPEG с качеством 90 для оптимизации* - ---- - -## Структура хранения файлов - -``` -media/ -├── products/ -│ ├── originals/ # Оригинальные изображения товаров -│ │ └── product_name_12345.jpg -│ ├── thumbnails/ # Миниатюры (150x150) -│ │ └── product_name_12345.jpg -│ ├── medium/ # Средние (400x400) -│ │ └── product_name_12345.jpg -│ └── large/ # Большие (800x800) -│ └── product_name_12345.jpg -│ -├── kits/ -│ ├── originals/ # Оригинальные изображения комплектов -│ ├── thumbnails/ -│ ├── medium/ -│ └── large/ -│ -└── categories/ - ├── originals/ # Оригинальные изображения категорий - ├── thumbnails/ - ├── medium/ - └── large/ -``` - ---- - -## Использование в шаблонах (templates) - -### Товары - -```django -{% load static %} - - -
- {% for product in products %} -
- {% if product.photos.first %} - {{ product.name }} - {% endif %} -
- {% endfor %} -
- - -
- {% if product.photos.first %} - {{ product.name }} - {% endif %} -
- - - -``` - -### Комплекты (букеты) - -```django - -{{ kit.name }} - - -{{ kit.name }} - - -{{ kit.name }} -``` - -### Категории - -```django - -{{ category.name }} - - -{{ category.name }} -``` - ---- - -## Использование в представлениях (views) - -```python -from products.models import Product, ProductPhoto - -def product_detail(request, pk): - product = Product.objects.get(pk=pk) - - # Получить первое фото - main_photo = product.photos.first() - - if main_photo: - context = { - 'product': product, - 'thumbnail_url': main_photo.get_thumbnail_url(), - 'medium_url': main_photo.get_medium_url(), - 'large_url': main_photo.get_large_url(), - 'original_url': main_photo.get_original_url(), - } - - return render(request, 'product_detail.html', context) -``` - ---- - -## API фото моделей - -### Методы для получения URL - -Каждая фото-модель (`ProductPhoto`, `ProductKitPhoto`, `ProductCategoryPhoto`) имеет методы: - -```python -photo = ProductPhoto.objects.first() - -# Получить URL нужного размера -photo.get_thumbnail_url() # → /media/products/thumbnails/photo_12345.jpg -photo.get_medium_url() # → /media/products/medium/photo_12345.jpg -photo.get_large_url() # → /media/products/large/photo_12345.jpg -photo.get_original_url() # → /media/products/originals/photo_12345.jpg - -# Прямой доступ к изображению -photo.image # → путь к оригиналу в БД -photo.image.url # → URL оригинала -``` - -### Пример полного использования - -```python -from products.models import Product -from products.utils.image_service import ImageService - -product = Product.objects.get(pk=1) -photo = product.photos.first() - -# Способ 1: через методы модели -thumbnail = photo.get_thumbnail_url() -medium = photo.get_medium_url() -large = photo.get_large_url() -original = photo.get_original_url() - -# Способ 2: через ImageService (если нужна большая гибкость) -from products.utils.image_service import ImageService - -all_urls = ImageService.get_all_urls(photo.image.name) -# → { -# 'original': '/media/products/originals/...', -# 'thumbnail': '/media/products/thumbnails/...', -# 'medium': '/media/products/medium/...', -# 'large': '/media/products/large/...' -# } -``` - ---- - -## Загрузка изображений - -### Через админку Django - -1. Откройте админку: `http://localhost:8000/admin/` -2. Перейдите в раздел "Товары", "Комплекты" или "Категории" -3. Нажмите на объект или создайте новый -4. В разделе "Фото" нажмите "Добавить фото" -5. Загрузьте изображение (JPEG или PNG) -6. После сохранения система автоматически создаст все размеры - -### Процесс при загрузке - -1. Система получает загруженное изображение -2. Проверяет его валидность (должно быть JPEG или PNG) -3. Конвертирует в RGB если нужно (для PNG с прозрачностью) -4. Создает 4 версии: - - **original**: сохраняет в JPEG (quality=90) - - **thumbnail**: изменяет размер до 150×150 - - **medium**: изменяет размер до 400×400 - - **large**: изменяет размер до 800×800 -5. Все версии сохраняются в правильные папки -6. В БД хранится путь только к оригиналу - ---- - -## Management команды - -### Обработка существующих изображений - -Если вы загрузили изображения ДО внедрения этой системы, используйте команду: - -```bash -# Обработать все изображения -python manage.py process_images - -# Обработать только товары -python manage.py process_images --model ProductPhoto - -# Обработать только комплекты -python manage.py process_images --model ProductKitPhoto - -# Обработать только категории -python manage.py process_images --model ProductCategoryPhoto -``` - -Команда: -- Найдет все существующие изображения -- Создаст все недостающие размеры -- Покажет прогресс обработки -- Выведет количество успешно обработанных и ошибок - ---- - -## Автоматическое удаление - -При удалении фото все его версии удаляются автоматически: - -```python -photo = ProductPhoto.objects.get(pk=1) -photo.delete() # Удалит оригинал + thumbnail + medium + large -``` - -Это происходит благодаря переопределенному методу `delete()` в моделях. - ---- - -## Обновление изображения - -При загрузке нового изображения для существующего фото: - -1. Система обнаруживает, что `image` поле изменилось -2. Старые версии удаляются (оригинал + все размеры) -3. Создаются новые версии для нового изображения - -```python -photo = ProductPhoto.objects.get(pk=1) -photo.image = request.FILES['new_image'] -photo.save() # Старые версии удалены, созданы новые -``` - ---- - -## Оптимизация и производительность - -### Как работает кэширование путей - -URL изображения рассчитывается динамически на основе пути к оригиналу: - -``` -Оригинал: products/originals/flower_12345.jpg -↓ Динамический расчет пути -Миниатюра: products/thumbnails/flower_12345.jpg -Средний: products/medium/flower_12345.jpg -Большой: products/large/flower_12345.jpg -``` - -Это не требует хранения 4 путей в БД - экономим место и упрощаем код. - -### Параметры сжатия - -- **Алгоритм**: LANCZOS (лучше всего сохраняет качество) -- **Качество JPEG**: 90 (оптимальный баланс между качеством и размером) -- **Оптимизация**: включена (`optimize=True`) - ---- - -## Типичные размеры файлов - -При загрузке изображения 2000×2000 px (∼2-3 МБ): - -| Версия | Размер | Экономия | -|--------|--------|---------| -| original | ∼150-200 КБ | - | -| thumbnail | ∼5-8 КБ | 95% | -| medium | ∼15-25 КБ | 85% | -| large | ∼50-80 КБ | 65% | -| **Итого** | **∼230-310 КБ** | **~90%** | - ---- - -## Возможные доработки в будущем - -1. **Redis кэш URL**: кэшировать сгенерированные URL для еще большей производительности -2. **WebP формат**: сохранять в WebP для еще большей оптимизации -3. **Responsive images**: служить разные размеры в зависимости от устройства -4. **CDN интеграция**: заливать изображения на CDN для быстрой доставки -5. **Водяной знак**: добавлять водяной знак при экспорте -6. **Batch обработка**: обрабатывать несколько изображений параллельно - ---- - -## Решение проблем - -### Изображение не показывается в админке - -Проверьте: -1. Изображение загружено (есть путь в БД) -2. MEDIA_URL и MEDIA_ROOT настроены правильно в settings.py -3. Django runserver запущен (в продакшене нужно настроить serving) - -### Некоторые размеры не созданы - -Запустите management команду: -```bash -python manage.py process_images -``` - -### Слишком долгая загрузка изображения - -- Проверьте размер загружаемого файла (обычно <5 МБ) -- На сервере может быть медленнее - это нормально -- Рассмотрите асинхронную обработку через Celery (будущая доработка) - ---- - -## Версия и требования - -- Django 5.2+ -- Pillow (любая последняя версия) -- Python 3.8+ - ---- - -## История изменений - -### v1.0 (2025-10-22) -- Первая версия системы хранения изображений -- Поддержка товаров, комплектов и категорий -- Автоматическое создание 4 размеров -- Management команда для обработки существующих данных -- Интеграция с админкой Django diff --git a/IMAGE_SYSTEM_EXAMPLES.md b/IMAGE_SYSTEM_EXAMPLES.md deleted file mode 100644 index be8978d..0000000 --- a/IMAGE_SYSTEM_EXAMPLES.md +++ /dev/null @@ -1,547 +0,0 @@ -# Примеры использования системы хранения изображений - -## Содержание - -1. [Примеры в шаблонах](#примеры-в-шаблонах) -2. [Примеры в представлениях](#примеры-в-представлениях) -3. [Примеры в моделях](#примеры-в-моделях) -4. [Примеры в админке](#примеры-в-админке) -5. [Примеры JSON API](#примеры-json-api) -6. [Продвинутые примеры](#продвинутые-примеры) - ---- - -## Примеры в шаблонах - -### Пример 1: Простой список товаров - -```django -{% extends 'base.html' %} - -{% block content %} -
- {% for product in products %} -
- {% if product.photos.first %} - {{ product.name }} - {% else %} -
Нет фото
- {% endif %} - -

{{ product.name }}

-

{{ product.sale_price }} ₽

-
- {% endfor %} -
-{% endblock %} -``` - -### Пример 2: Карточка товара с галереей - -```django -{% extends 'base.html' %} - -{% block content %} -
- - -
-

{{ product.name }}

-

{{ product.description }}

-

{{ product.sale_price }} ₽

-
-
- - -{% endblock %} -``` - -### Пример 3: Каталог комплектов с категориями - -```django -{% extends 'base.html' %} - -{% block content %} -
- {% for category in categories %} -
-

{{ category.name }}

- -
- {% for kit in category.kits.all %} -
- {% if kit.photos.first %} -
- {{ kit.name }} -
- {% endif %} - -

{{ kit.name }}

-

от {{ kit.get_sale_price }} ₽

- - - Подробнее - -
- {% endfor %} -
-
- {% endfor %} -
-{% endblock %} -``` - -### Пример 4: Слайдер (carousel) - -```django -{% extends 'base.html' %} - -{% block content %} -
-
- {% for photo in featured_photos %} -
-
-

{{ photo.product.name }}

-

{{ photo.product.description|truncatewords:20 }}

-
-
- {% endfor %} -
-
- - -{% endblock %} -``` - ---- - -## Примеры в представлениях - -### Пример 1: Список товаров с пагинацией - -```python -from django.shortcuts import render -from django.views.generic import ListView -from products.models import Product - -class ProductListView(ListView): - model = Product - template_name = 'products/list.html' - context_object_name = 'products' - paginate_by = 12 - - def get_queryset(self): - return Product.active.prefetch_related('photos') - -def products_list(request): - products = Product.active.prefetch_related('photos').all() - - context = { - 'products': products, - } - - return render(request, 'products/list.html', context) -``` - -### Пример 2: Детальный вид товара - -```python -from django.shortcuts import render, get_object_or_404 -from products.models import Product - -def product_detail(request, pk): - product = get_object_or_404(Product, pk=pk) - photos = product.photos.all().order_by('order') - - # Получить все URL для шаблона - main_photo = photos.first() - all_photo_urls = [] - - if main_photo: - all_photo_urls = [{ - 'thumbnail': photo.get_thumbnail_url(), - 'medium': photo.get_medium_url(), - 'large': photo.get_large_url(), - 'original': photo.get_original_url(), - } for photo in photos] - - context = { - 'product': product, - 'photos': photos, - 'photo_urls': all_photo_urls, - 'main_photo': main_photo, - } - - return render(request, 'products/detail.html', context) -``` - -### Пример 3: API endpoint с изображениями - -```python -from django.http import JsonResponse -from products.models import Product - -def product_api(request, pk): - product = Product.objects.get(pk=pk) - photo = product.photos.first() - - data = { - 'id': product.id, - 'name': product.name, - 'price': float(product.sale_price), - 'images': { - 'thumbnail': photo.get_thumbnail_url() if photo else None, - 'medium': photo.get_medium_url() if photo else None, - 'large': photo.get_large_url() if photo else None, - 'original': photo.get_original_url() if photo else None, - } - } - - return JsonResponse(data) -``` - -### Пример 4: Экспорт в CSV с ссылками на изображения - -```python -import csv -from django.http import HttpResponse -from products.models import Product - -def export_products_csv(request): - response = HttpResponse(content_type='text/csv') - response['Content-Disposition'] = 'attachment; filename="products.csv"' - - writer = csv.writer(response) - writer.writerow(['ID', 'Название', 'Цена', 'Фото (thumbnail)', 'Фото (original)']) - - for product in Product.active.prefetch_related('photos'): - photo = product.photos.first() - writer.writerow([ - product.id, - product.name, - product.sale_price, - photo.get_thumbnail_url() if photo else '', - photo.get_original_url() if photo else '', - ]) - - return response -``` - ---- - -## Примеры в моделях - -### Пример 1: Добавить метод для получения лучшего фото - -```python -from products.models import Product - -class ProductWithBestPhoto(Product): - """Добавить метод для получения лучшего фото по рейтингу""" - - def get_best_photo(self): - """Получить фото с наименьшим order (основное)""" - return self.photos.first() - - def get_photo_urls(self): - """Получить словарь всех URL основного фото""" - photo = self.get_best_photo() - if not photo: - return { - 'thumbnail': '', - 'medium': '', - 'large': '', - 'original': '', - } - - return { - 'thumbnail': photo.get_thumbnail_url(), - 'medium': photo.get_medium_url(), - 'large': photo.get_large_url(), - 'original': photo.get_original_url(), - } -``` - -### Пример 2: Сигнал для логирования при загрузке фото - -```python -from django.db.models.signals import post_save -from django.dispatch import receiver -from products.models import ProductPhoto -import logging - -logger = logging.getLogger(__name__) - -@receiver(post_save, sender=ProductPhoto) -def log_photo_upload(sender, instance, created, **kwargs): - if created: - logger.info(f"Новое фото для товара '{instance.product.name}': {instance.image.name}") - else: - logger.info(f"Фото для товара '{instance.product.name}' обновлено: {instance.image.name}") -``` - ---- - -## Примеры в админке - -### Пример: Кастомная админка с дополнительными фильтрами - -```python -from django.contrib import admin -from products.models import Product, ProductPhoto - -class ProductPhotoInline(admin.TabularInline): - model = ProductPhoto - extra = 0 - fields = ('image', 'order') - readonly_fields = ('image_preview',) - -class ProductAdminCustom(admin.ModelAdmin): - list_display = ('name', 'photo_count', 'has_photos', 'created_at') - inlines = [ProductPhotoInline] - - def photo_count(self, obj): - """Количество фото""" - return obj.photos.count() - photo_count.short_description = 'Фото' - - def has_photos(self, obj): - """Есть ли фото""" - return obj.photos.exists() - has_photos.boolean = True - has_photos.short_description = 'Есть фото' -``` - ---- - -## Примеры JSON API - -### Пример: REST API с сериализаторами (DRF) - -```python -from rest_framework import serializers -from products.models import Product, ProductPhoto - -class ProductPhotoSerializer(serializers.ModelSerializer): - thumbnail_url = serializers.SerializerMethodField() - medium_url = serializers.SerializerMethodField() - large_url = serializers.SerializerMethodField() - original_url = serializers.SerializerMethodField() - - class Meta: - model = ProductPhoto - fields = ['id', 'thumbnail_url', 'medium_url', 'large_url', 'original_url'] - - def get_thumbnail_url(self, obj): - return obj.get_thumbnail_url() - - def get_medium_url(self, obj): - return obj.get_medium_url() - - def get_large_url(self, obj): - return obj.get_large_url() - - def get_original_url(self, obj): - return obj.get_original_url() - -class ProductSerializer(serializers.ModelSerializer): - photos = ProductPhotoSerializer(many=True) - - class Meta: - model = Product - fields = ['id', 'name', 'price', 'photos'] -``` - -### Пример JSON ответ: - -```json -{ - "id": 1, - "name": "Роза красная Freedom", - "price": "150.00", - "photos": [ - { - "id": 1, - "thumbnail_url": "/media/products/thumbnails/rose_12345.jpg", - "medium_url": "/media/products/medium/rose_12345.jpg", - "large_url": "/media/products/large/rose_12345.jpg", - "original_url": "/media/products/originals/rose_12345.jpg" - } - ] -} -``` - ---- - -## Продвинутые примеры - -### Пример 1: Оптимизированный запрос для перечисления - -```python -from django.db.models import Prefetch -from products.models import Product, ProductPhoto - -def get_optimized_products(queryset=None): - """ - Получить товары с оптимизированными запросами к фото - """ - if queryset is None: - queryset = Product.active - - # Prefetch только первое фото для каждого товара - photo_prefetch = Prefetch( - 'photos', - ProductPhoto.objects.order_by('order')[:1] - ) - - return queryset.prefetch_related(photo_prefetch) - -# Использование -products = get_optimized_products() -for product in products: - photo = product.photos.first() - print(f"{product.name}: {photo.get_medium_url()}") -``` - -### Пример 2: Кэширование URL в Redis - -```python -from django.core.cache import cache -from products.models import ProductPhoto - -def get_photo_urls_cached(photo_id, timeout=3600): - """ - Получить все URL фото из кэша или создать новые - """ - cache_key = f'photo_urls_{photo_id}' - - urls = cache.get(cache_key) - if urls is None: - photo = ProductPhoto.objects.get(id=photo_id) - urls = { - 'thumbnail': photo.get_thumbnail_url(), - 'medium': photo.get_medium_url(), - 'large': photo.get_large_url(), - 'original': photo.get_original_url(), - } - cache.set(cache_key, urls, timeout) - - return urls - -# Использование -urls = get_photo_urls_cached(photo_id=1) -``` - -### Пример 3: Генерация миниатюр для социальных сетей - -```python -from products.models import Product -from products.utils.image_service import ImageService - -def get_social_media_image(product): - """ - Получить оптимальное изображение для социальных сетей - """ - photo = product.photos.first() - if not photo: - return None - - social_images = { - 'og_image': photo.get_large_url(), # Facebook, VK - 'twitter_image': photo.get_medium_url(), - 'pinterest': photo.get_original_url(), - 'instagram_thumbnail': photo.get_thumbnail_url(), - } - - return social_images - -# Использование в шаблоне -{% with social_images=get_social_media_image %} - - -{% endwith %} -``` - -### Пример 4: Batch обработка и переформатирование - -```python -from django.core.management.base import BaseCommand -from products.models import Product - -class Command(BaseCommand): - help = 'Переобработать все изображения' - - def handle(self, *args, **options): - for product in Product.objects.prefetch_related('photos'): - for photo in product.photos.all(): - # Пересохранить - это вызовет переобработку - photo.save() - self.stdout.write(f"✓ {product.name}") -``` - ---- - -## Чек-лист для разработчика - -Когда добавляете изображения в новую модель: - -- [ ] Наследуется ли модель от `Photo*`? -- [ ] Есть ли методы `get_*_url()`? -- [ ] Переопределены ли `save()` и `delete()`? -- [ ] Добавлена ли в админку? -- [ ] Добавлен ли `prefetch_related` в queryset? -- [ ] Проверены ли все размеры в шаблонах? - ---- - -## Производительность - -Типичные цифры на проекте с 1000 товарами: - -| Операция | Время | -|----------|-------| -| Загрузка товара + первое фото | 5мс | -| Получение URL миниатюры | 0.1мс | -| Вся галерея (10 фото) | 50мс | -| Экспорт в CSV (1000 товаров) | 2сек | - ---- - -Больше примеров и кейсов см. в основной документации: `IMAGE_STORAGE_STRATEGY.md` diff --git a/IMPLEMENTATION_SUMMARY.txt b/IMPLEMENTATION_SUMMARY.txt deleted file mode 100644 index 472da2f..0000000 --- a/IMPLEMENTATION_SUMMARY.txt +++ /dev/null @@ -1,138 +0,0 @@ -╔══════════════════════════════════════════════════════════════════════════════╗ -║ СИСТЕМА ХРАНЕНИЯ ИЗОБРАЖЕНИЙ ║ -║ ГОТОВО К ИСПОЛЬЗОВАНИЮ ║ -╚══════════════════════════════════════════════════════════════════════════════╝ - -📋 ЧТО БЫЛО РЕАЛИЗОВАНО -═══════════════════════════════════════════════════════════════════════════════ - -✅ Автоматическая обработка изображений - └─ При загрузке изображения система создает 4 версии: - • thumbnail (150×150) - для каталогов, списков - • medium (400×400) - для карточек товаров - • large (800×800) - для полного просмотра - • original (без изм.) - архив в качестве 90 - -✅ Чистое хранилище файлов - └─ media/products/originals/ (оригинальные) - └─ media/products/thumbnails/ (миниатюры) - └─ media/products/medium/ (средние) - └─ media/products/large/ (большие) - -✅ Простой API в шаблонах - └─ {{ photo.get_thumbnail_url }} # миниатюра - {{ photo.get_medium_url }} # средний размер - {{ photo.get_large_url }} # большой размер - {{ photo.get_original_url }} # оригинал - -✅ Автоматическое управление жизненным циклом - └─ Удаление старых версий при замене фото - └─ Удаление всех версий при удалении фото - └─ Обновление при сохранении - -✅ Админка Django с превью всех размеров - └─ Наглядное отображение 4 версий каждого фото - └─ Быстрая загрузка и управление - -✅ Management команда для batch-обработки - └─ python manage.py process_images - -═══════════════════════════════════════════════════════════════════════════════ -📁 СОЗДАННЫЕ ФАЙЛЫ -═══════════════════════════════════════════════════════════════════════════════ - -1. products/utils/image_processor.py - └─ Класс ImageProcessor для обработки и создания размеров - └─ Методы для сохранения и удаления версий - -2. products/utils/image_service.py - └─ Класс ImageService для получения URL нужного размера - └─ Методы: get_url(), get_thumbnail_url(), get_medium_url() и т.д. - -3. products/models.py (ОБНОВЛЕНО) - └─ ProductPhoto, ProductKitPhoto, ProductCategoryPhoto - └─ Добавлены методы save() и delete() для автоматической обработки - └─ Добавлены методы get_*_url() для получения URL - -4. products/admin.py (ОБНОВЛЕНО) - └─ Админка с красивым превью всех размеров - └─ Grid для наглядного сравнения размеров - -5. products/management/commands/process_images.py - └─ Management команда для обработки существующих изображений - └─ Поддержка фильтра по моделям - -6. Документация: - └─ IMAGE_STORAGE_STRATEGY.md - полная документация системы - └─ QUICK_START_IMAGES.md - быстрый старт - └─ IMAGE_SYSTEM_EXAMPLES.md - примеры кода - -═══════════════════════════════════════════════════════════════════════════════ -🚀 БЫСТРЫЙ СТАРТ -═══════════════════════════════════════════════════════════════════════════════ - -1. В шаблонах (templates): - - - {{ product.name }} - - - {{ product.name }} - - - {{ product.name }} - -2. В представлениях (views): - - from products.models import Product - - product = Product.objects.get(pk=1) - photo = product.photos.first() - - context = { - 'thumbnail': photo.get_thumbnail_url(), - 'medium': photo.get_medium_url(), - 'large': photo.get_large_url(), - 'original': photo.get_original_url(), - } - -3. В админке: - └─ Просто загрузите изображение - система сделает все автоматически! - └─ Сможете видеть все 4 версии при редактировании - -═══════════════════════════════════════════════════════════════════════════════ -📊 ПРОИЗВОДИТЕЛЬНОСТЬ И ЭКОНОМИЯ -═══════════════════════════════════════════════════════════════════════════════ - -При загрузке изображения 2000×2000 px (~2-3 МБ): - -Версия │ Размер │ Экономия -─────────────┼──────────┼───────────── -original │ 6.1K │ - -medium │ 2.9K │ 52% ↓ -large │ 5.6K │ 8% ↓ -thumbnail │ 0.4K │ 93% ↓ -─────────────┼──────────┼───────────── -ИТОГО │ 14.9K │ 90% ↓ - -═══════════════════════════════════════════════════════════════════════════════ -✅ ТЕСТИРОВАНИЕ -═══════════════════════════════════════════════════════════════════════════════ - -✓ Загрузка тестового изображения (1000×1000 px) -✓ Создание всех 4 версий -✓ Правильное сохранение в папки -✓ Получение URL нужного размера -✓ Экономия размера файлов (93% для миниатюр) - -═══════════════════════════════════════════════════════════════════════════════ -🎯 ГОТОВО К ИСПОЛЬЗОВАНИЮ -═══════════════════════════════════════════════════════════════════════════════ - -Система полностью реализована, протестирована и готова! - -Версия: 1.0 -Дата: 2025-10-22 diff --git a/QUICK_START_IMAGES.md b/QUICK_START_IMAGES.md deleted file mode 100644 index 653f898..0000000 --- a/QUICK_START_IMAGES.md +++ /dev/null @@ -1,249 +0,0 @@ -# Быстрый старт: Система хранения изображений - -## Что было реализовано ✅ - -Полнофункциональная система автоматической обработки и хранения изображений для: -- Товаров (ProductPhoto) -- Комплектов/букетов (ProductKitPhoto) -- Категорий (ProductCategoryPhoto) - -## Основные возможности - -1. **Автоматическое создание 4 размеров:** - - `thumbnail` (150×150) - для каталогов - - `medium` (400×400) - для карточек - - `large` (800×800) - для полного просмотра - - `original` - архив в качестве 90 - -2. **Чистое хранилище:** все версии в разных папках - ``` - media/products/originals/... - media/products/thumbnails/... - media/products/medium/... - media/products/large/... - ``` - -3. **Простой API в шаблонах:** - ```django - {{ photo.get_thumbnail_url }} - {{ photo.get_medium_url }} - {{ photo.get_large_url }} - {{ photo.get_original_url }} - ``` - -4. **Автоматическое управление:** - - Удаление старых версий при замене фото - - Удаление всех версий при удалении фото - -## Как использовать - -### В шаблонах (templates) - -```django - -{{ product.name }} - - -{{ product.name }} - - -{{ product.name }} - - - - Скачать в полном размере - -``` - -### В представлениях (views) - -```python -from products.models import Product - -def product_detail(request, pk): - product = Product.objects.get(pk=pk) - photo = product.photos.first() - - context = { - 'thumbnail': photo.get_thumbnail_url(), - 'medium': photo.get_medium_url(), - 'large': photo.get_large_url(), - 'original': photo.get_original_url(), - } - - return render(request, 'product_detail.html', context) -``` - -## Загрузка изображений - -1. Зайти в админку: `http://localhost:8000/admin/` -2. Логин: `admin`, Пароль: `admin123` -3. Открыть Товары, Комплекты или Категории -4. Добавить/редактировать объект -5. Загрузить изображение в секцию "Фото" -6. Сохранить - **система автоматически создаст все размеры!** - -## Management команды - -### Обработать старые изображения - -Если было изображения ДО внедрения этой системы: - -```bash -# Все изображения -python manage.py process_images - -# Только товары -python manage.py process_images --model ProductPhoto - -# Только комплекты -python manage.py process_images --model ProductKitPhoto - -# Только категории -python manage.py process_images --model ProductCategoryPhoto -``` - -## Файлы системы - -| Файл | Назначение | -|------|-----------| -| `products/utils/image_processor.py` | Обработка и создание размеров | -| `products/utils/image_service.py` | Получение URL нужного размера | -| `products/models.py` | Обновленные Photo модели с методами | -| `products/admin.py` | Админка с превью всех размеров | -| `products/management/commands/process_images.py` | Command для batch-обработки | - -## Размеры файлов (пример) - -При загрузке фото 2000×2000 px: - -| Версия | Размер | Экономия | -|--------|--------|---------| -| original | 6.1K | - | -| medium | 2.9K | 52% ↓ | -| large | 5.6K | 8% ↓ | -| thumbnail | 438B | **93% ↓** | - -## API методов моделей - -```python -photo = ProductPhoto.objects.first() - -# URL разных размеров -photo.get_thumbnail_url() # → /media/products/thumbnails/image_123.jpg -photo.get_medium_url() # → /media/products/medium/image_123.jpg -photo.get_large_url() # → /media/products/large/image_123.jpg -photo.get_original_url() # → /media/products/originals/image_123.jpg - -# Удаление (удалит все версии автоматически) -photo.delete() - -# Замена изображения (удалит старые, создаст новые) -photo.image = new_file -photo.save() -``` - -## Для разработчиков - -### Структура обработки изображения - -``` -1. Загрузка файла → ImageProcessor.process_image() -2. Проверка валидности (должен быть JPEG/PNG) -3. Конвертирование в RGB (для PNG с прозрачностью) -4. Создание 4 версий: - - original: JPEG quality=90 - - thumbnail: resize to 150×150 - - medium: resize to 400×400 - - large: resize to 800×800 -5. Сохранение в media/[type]/[size]/filename -6. Хранение только пути к оригиналу в БД -``` - -### Добавление нового размера - -Если нужен новый размер, отредактируйте `products/utils/image_processor.py`: - -```python -class ImageProcessor: - SIZES = { - 'thumbnail': (150, 150), - 'medium': (400, 400), - 'large': (800, 800), - 'xl': (1200, 1200), # ← новый размер - } -``` - -И добавьте метод в модели: - -```python -def get_xl_url(self): - """Получить URL XL размера (1200x1200)""" - from .utils.image_service import ImageService - return ImageService.get_url(self.image.name, 'xl') -``` - -## Производительность - -- **Нет дополнительной БД записи** - все пути рассчитываются на лету -- **Минимальный оверхед** - вычисление пути занимает <1мс -- **Оптимальное сжатие** - LANCZOS для качества, quality=90 для баланса - -Если нужна оптимизация: -- Можно добавить кэш в Redis -- Можно генерировать в фоне через Celery -- Можно загружать на CDN - -## Полная документация - -См. файл `IMAGE_STORAGE_STRATEGY.md` для полной документации. - -## Контрольный список - -- [x] ImageProcessor создан -- [x] ImageService создан -- [x] Модели обновлены -- [x] Методы получения URL добавлены -- [x] Админка обновлена с превью всех размеров -- [x] Management команда создана -- [x] Миграции применены -- [x] Тестирование пройдено ✓ - -## Быстрая проверка - -```bash -# Запустить сервер -python manage.py runserver - -# В другом терминале - протестировать -python manage.py shell -``` - -```python -from django.core.files.uploadedfile import SimpleUploadedFile -from products.models import Product, ProductPhoto -from PIL import Image -from io import BytesIO - -# Создать тестовое изображение -img = Image.new('RGB', (1000, 1000), color='blue') -img_io = BytesIO() -img.save(img_io, format='JPEG') -img_io.seek(0) - -# Получить первый товар -product = Product.objects.first() - -# Создать фото -photo = ProductPhoto(product=product) -photo.image = SimpleUploadedFile('test.jpg', img_io.getvalue()) -photo.save() - -# Проверить что все работает -print(photo.get_thumbnail_url()) -print(photo.get_medium_url()) -print(photo.get_large_url()) -print(photo.get_original_url()) -``` - -Все должно вывести правильные пути к файлам! 🎉