101 lines
4.2 KiB
Python
101 lines
4.2 KiB
Python
"""
|
||
Утилиты для работы с фотографиями товаров, комплектов и категорий.
|
||
"""
|
||
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
|