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:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user