Files
octopus/myproject/products/views/utils.py
Andrey Smakotin a073b1aa77 Добавлена проверка общего лимита фото при редактировании
- Проверяется количество уже существующих фото перед загрузкой новых
- Блокируется загрузка если уже есть 10 фото (максимум)
- При превышении загружается только доступное количество слотов
- Информативные сообщения об ошибках и предупреждения для пользователя
- Исправлена проблема с накоплением фото при многократном редактировании
2025-11-16 02:08:57 +03:00

101 lines
4.1 KiB
Python
Raw 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 = 5 * 1024 * 1024 # 5MB
allowed_extensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp']
if photo.size > max_size:
return False, f'Размер файла {photo.name} превышает 5MB.'
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
# МАКСИМУМ 10 ФОТО на товар/комплект/категорию
MAX_PHOTOS = 10
# Получаем количество уже существующих фото
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