Add bulk photo deletion feature for products.

Allows selecting and deleting multiple photos at once via checkboxes
and an AJAX endpoint. Key features:
- Checkboxes next to each photo in edit form
- Delete button that shows only when photos are selected
- AJAX request with JSON payload and success confirmation
- DOM removal and counter update after deletion
- Uses existing ImageProcessor cleanup logic

Files changed:
- product_form.html: Added checkboxes and delete button with JS handler
- photo_management.py: Added product_photos_delete_bulk AJAX view
- urls.py: Added /product/photos/delete-bulk/ endpoint
- views/__init__.py: Exported new function

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-23 21:49:50 +03:00
parent 26146ac639
commit 493b6c212d
4 changed files with 185 additions and 4 deletions

View File

@@ -2,8 +2,13 @@
Универсальные функции для управления фотографиями товаров, комплектов и категорий.
Устраняет дублирование кода для операций: delete, set_main, move_up, move_down.
"""
import json
from django.shortcuts import get_object_or_404, redirect
from django.contrib import messages
from django.http import JsonResponse
from django.views.decorators.http import require_http_methods
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import PermissionRequiredMixin
from ..models import ProductPhoto, ProductKitPhoto, ProductCategoryPhoto
@@ -308,3 +313,71 @@ def category_photo_move_down(request, pk):
parent_attr='category',
permission='products.change_productcategory'
)
# ====================================
# AJAX Endpoints для массового удаления
# ====================================
@require_http_methods(["POST"])
@login_required
def product_photos_delete_bulk(request):
"""
AJAX endpoint для массового удаления фотографий товара.
Ожидает JSON: {photo_ids: [1, 2, 3]}
Возвращает JSON: {success: true, deleted: 3} или {success: false, error: "..."}
"""
# Проверка прав доступа
if not request.user.has_perm('products.change_product'):
return JsonResponse({
'success': False,
'error': 'У вас нет прав для удаления фотографий'
}, status=403)
try:
# Получаем список photo_ids из JSON тела запроса
data = json.loads(request.body)
photo_ids = data.get('photo_ids', [])
if not photo_ids or not isinstance(photo_ids, list):
return JsonResponse({
'success': False,
'error': 'Неверный формат: требуется список photo_ids'
}, status=400)
# Удаляем фотографии
deleted_count = 0
for photo_id in photo_ids:
try:
photo = ProductPhoto.objects.get(pk=photo_id)
photo.delete() # Это вызовет ImageProcessor.delete_all_versions()
deleted_count += 1
except ProductPhoto.DoesNotExist:
# Если фото не найдена, просто пропускаем
continue
except Exception as e:
# Логируем ошибку но продолжаем удаление остальных
import logging
logger = logging.getLogger(__name__)
logger.error(f"Error deleting photo {photo_id}: {str(e)}", exc_info=True)
continue
return JsonResponse({
'success': True,
'deleted': deleted_count
})
except json.JSONDecodeError:
return JsonResponse({
'success': False,
'error': 'Неверный JSON формат'
}, status=400)
except Exception as e:
import logging
logger = logging.getLogger(__name__)
logger.error(f"Bulk photo deletion error: {str(e)}", exc_info=True)
return JsonResponse({
'success': False,
'error': f'Ошибка сервера: {str(e)}'
}, status=500)