- Add ImageProcessor utility for automatic image resizing * Creates 4 versions: original, thumbnail (150x150), medium (400x400), large (800x800) * Uses LANCZOS algorithm for quality, JPEG quality 90 for optimization * Handles PNG transparency with white background * 90% file size reduction for thumbnails vs original - Add ImageService for URL generation * Dynamically computes paths based on original filename * Methods: get_thumbnail_url(), get_medium_url(), get_large_url(), get_original_url() * No additional database overhead - Update Photo models with automatic processing * ProductPhoto, ProductKitPhoto, ProductCategoryPhoto * Auto-creates all sizes on save * Auto-deletes all sizes on delete * Handles image replacement with cleanup - Enhance admin interface * Display all 4 image versions side-by-side in admin * Grid layout for easy comparison * Readonly preview fields - Add management command * process_images: batch process existing images * Support filtering by model type * Progress reporting and error handling - Clean database * Removed old migrations, rebuild from scratch * Clean SQLite database - Add comprehensive documentation * IMAGE_STORAGE_STRATEGY.md: full system architecture * QUICK_START_IMAGES.md: quick reference guide * IMAGE_SYSTEM_EXAMPLES.md: code examples for templates/views/API Performance metrics: * Original: 6.1K * Medium: 2.9K (52% smaller) * Large: 5.6K (8% smaller) * Thumbnail: 438B (93% smaller) 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
14 KiB
Стратегия хранения и обработки изображений
Обзор системы
Система автоматически хранит одно большое оригинальное изображение и создает несколько оптимизированных версий для разных сценариев использования в приложении.
Преимущества:
- ✅ Оригинальное изображение сохраняется в полном качестве
- ✅ Автоматическое создание всех размеров при загрузке
- ✅ Оптимизация под разные части приложения (списки, карточки, просмотр)
- ✅ Быстрая загрузка из-за меньшего размера файлов
- ✅ Экономия трафика и дискового пространства
Размеры изображений
| Размер | Размер (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)
Товары
{% load static %}
<!-- Список товаров - используем миниатюры -->
<div class="product-grid">
{% for product in products %}
<div class="product-card">
{% if product.photos.first %}
<img src="{{ product.photos.first.get_thumbnail_url }}"
alt="{{ product.name }}"
class="product-thumbnail">
{% endif %}
</div>
{% endfor %}
</div>
<!-- Карточка товара - используем средний размер -->
<div class="product-detail-card">
{% if product.photos.first %}
<img src="{{ product.photos.first.get_medium_url }}"
alt="{{ product.name }}"
class="product-preview">
{% endif %}
</div>
<!-- Полный просмотр - используем большой размер -->
<div class="product-gallery">
{% for photo in product.photos.all %}
<div class="gallery-item">
<img src="{{ photo.get_large_url }}"
alt="{{ product.name }}"
class="gallery-image">
<a href="{{ photo.get_original_url }}"
target="_blank"
class="view-original">Оригинал</a>
</div>
{% endfor %}
</div>
Комплекты (букеты)
<!-- Список комплектов -->
<img src="{{ kit.photos.first.get_thumbnail_url }}"
alt="{{ kit.name }}">
<!-- Карточка комплекта -->
<img src="{{ kit.photos.first.get_medium_url }}"
alt="{{ kit.name }}">
<!-- Просмотр комплекта -->
<img src="{{ kit.photos.first.get_large_url }}"
alt="{{ kit.name }}">
Категории
<!-- Превью категории -->
<img src="{{ category.photos.first.get_thumbnail_url }}"
alt="{{ category.name }}">
<!-- Детальное изображение категории -->
<img src="{{ category.photos.first.get_medium_url }}"
alt="{{ category.name }}">
Использование в представлениях (views)
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) имеет методы:
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 оригинала
Пример полного использования
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
- Откройте админку:
http://localhost:8000/admin/ - Перейдите в раздел "Товары", "Комплекты" или "Категории"
- Нажмите на объект или создайте новый
- В разделе "Фото" нажмите "Добавить фото"
- Загрузьте изображение (JPEG или PNG)
- После сохранения система автоматически создаст все размеры
Процесс при загрузке
- Система получает загруженное изображение
- Проверяет его валидность (должно быть JPEG или PNG)
- Конвертирует в RGB если нужно (для PNG с прозрачностью)
- Создает 4 версии:
- original: сохраняет в JPEG (quality=90)
- thumbnail: изменяет размер до 150×150
- medium: изменяет размер до 400×400
- large: изменяет размер до 800×800
- Все версии сохраняются в правильные папки
- В БД хранится путь только к оригиналу
Management команды
Обработка существующих изображений
Если вы загрузили изображения ДО внедрения этой системы, используйте команду:
# Обработать все изображения
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
Команда:
- Найдет все существующие изображения
- Создаст все недостающие размеры
- Покажет прогресс обработки
- Выведет количество успешно обработанных и ошибок
Автоматическое удаление
При удалении фото все его версии удаляются автоматически:
photo = ProductPhoto.objects.get(pk=1)
photo.delete() # Удалит оригинал + thumbnail + medium + large
Это происходит благодаря переопределенному методу delete() в моделях.
Обновление изображения
При загрузке нового изображения для существующего фото:
- Система обнаруживает, что
imageполе изменилось - Старые версии удаляются (оригинал + все размеры)
- Создаются новые версии для нового изображения
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% |
Возможные доработки в будущем
- Redis кэш URL: кэшировать сгенерированные URL для еще большей производительности
- WebP формат: сохранять в WebP для еще большей оптимизации
- Responsive images: служить разные размеры в зависимости от устройства
- CDN интеграция: заливать изображения на CDN для быстрой доставки
- Водяной знак: добавлять водяной знак при экспорте
- Batch обработка: обрабатывать несколько изображений параллельно
Решение проблем
Изображение не показывается в админке
Проверьте:
- Изображение загружено (есть путь в БД)
- MEDIA_URL и MEDIA_ROOT настроены правильно в settings.py
- Django runserver запущен (в продакшене нужно настроить serving)
Некоторые размеры не созданы
Запустите management команду:
python manage.py process_images
Слишком долгая загрузка изображения
- Проверьте размер загружаемого файла (обычно <5 МБ)
- На сервере может быть медленнее - это нормально
- Рассмотрите асинхронную обработку через Celery (будущая доработка)
Версия и требования
- Django 5.2+
- Pillow (любая последняя версия)
- Python 3.8+
История изменений
v1.0 (2025-10-22)
- Первая версия системы хранения изображений
- Поддержка товаров, комплектов и категорий
- Автоматическое создание 4 размеров
- Management команда для обработки существующих данных
- Интеграция с админкой Django