Files
octopus/myproject/products/views/utils.py

101 lines
4.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Утилиты для работы с фотографиями товаров, комплектов и категорий.
"""
import os
from django.db import models
def validate_photo(photo):
"""
Валидация загружаемого фото.
Возвращает (True, None) если валидно, или (False, error_message) если ошибка.
"""
max_size = 20 * 1024 * 1024 # 20MB
allowed_extensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.heic', '.heif']
if photo.size > max_size:
return False, f'Размер файла {photo.name} превышает 20MB.'
ext = os.path.splitext(photo.name)[1].lower()
if ext not in allowed_extensions:
return False, f'Формат файла {ext} не поддерживается. Разрешены: {", ".join(allowed_extensions)}'
return True, None
def handle_photos(request, parent_obj, photo_model, parent_field_name):
"""
Универсальная обработка загружаемых фотографий.
Args:
request: HTTP request с FILES
parent_obj: Родительский объект (Product, ProductKit или ProductCategory)
photo_model: Модель фотографии (ProductPhoto, ProductKitPhoto, ProductCategoryPhoto)
parent_field_name: Имя поля связи в модели фото ('product', 'kit', 'category')
Returns:
Список сообщений об ошибках (пустой список если все ок).
"""
errors = []
warnings = []
photos = request.FILES.getlist('photos')
if not photos:
return errors
# МАКСИМУМ 5 ФОТО на товар/комплект/категорию
MAX_PHOTOS = 5
# Получаем количество уже существующих фото
filter_kwargs = {parent_field_name: parent_obj}
existing_count = photo_model.objects.filter(**filter_kwargs).count()
# Проверяем общий лимит (существующие + новые)
total_after_upload = existing_count + len(photos)
if existing_count >= MAX_PHOTOS:
# Уже достигнут лимит - новые фото не принимаем
errors.append(
f'Достигнут лимит: максимум {MAX_PHOTOS} фото. '
f'Сейчас уже загружено {existing_count} фото. '
f'Удалите лишние фото перед добавлением новых.'
)
return errors
if total_after_upload > MAX_PHOTOS:
# Превышение лимита - сохраняем только те фото, которые влезают
available_slots = MAX_PHOTOS - existing_count
warnings.append(
f'Можно загрузить только {available_slots} фото '
f'(уже есть {existing_count}, лимит {MAX_PHOTOS}). '
f'Обработано первые {available_slots} из {len(photos)} загруженных файлов.'
)
photos = photos[:available_slots]
# Получаем максимальный order для этого родительского объекта
max_order = photo_model.objects.filter(**filter_kwargs).aggregate(
models.Max('order')
)['order__max']
# Если фото нет, начинаем с 0, иначе с max_order + 1
next_order = 0 if max_order is None else max_order + 1
# Валидация и сохранение фото
for photo in photos:
is_valid, error_msg = validate_photo(photo)
if not is_valid:
errors.append(error_msg)
else:
# Создаем фото с правильной связью
create_kwargs = {
parent_field_name: parent_obj,
'image': photo,
'order': next_order
}
photo_model.objects.create(**create_kwargs)
next_order += 1
# Добавляем warnings в errors для отображения пользователю
errors.extend(warnings)
return errors