Реализовано управление активностью тегов товаров и комплектов
## Что сделано: ### 1. Фильтрация тегов по активности - ProductForm и ProductKitForm показывают только активные теги в селектах - ProductListView, ProductKitListView передают только активные теги в контекст фильтров ### 2. Отображение тегов на страницах товаров/комплектов - product_detail.html отображает только активные теги - productkit_detail.html отображает только активные теги ### 3. Логика отображения в деталях тега - Для активного тега: показываются только активные товары/комплекты - Для неактивного тега: показываются ВСЕ товары/комплекты (для возможности ручной очистки) ### 4. API endpoint для переключения статуса - Новый endpoint toggle_tag_status_api в api_views.py - POST /products/api/tags/<id>/toggle/ - Переключает is_active и возвращает новый статус ### 5. AJAX toggle switch в таблице тегов - Заменены бейджи на toggle switch в tag_list.html - Переключатель в 1.3x больше (масштабирование через CSS) - Мгновенное обновление без перезагрузки страницы - Показывает сообщение об успехе/ошибке ### 6. Связь в БД остаётся неизменной - При деактивации тег остаётся привязан к товарам в БД - Просто скрывается в интерфейсе - При реактивации вновь становится видимым 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -98,7 +98,27 @@ def search_products_and_variants(request):
|
||||
}],
|
||||
'pagination': {'more': False}
|
||||
})
|
||||
except (Product.DoesNotExist, ProductKit.DoesNotExist, ValueError):
|
||||
elif item_type == 'variant':
|
||||
# Для групп вариантов: получаем по ID с prefetch приоритетов
|
||||
variant = ProductVariantGroup.objects.prefetch_related(
|
||||
'items__product'
|
||||
).get(id=numeric_id)
|
||||
|
||||
variant_price = variant.price or 0
|
||||
count = variant.items.count()
|
||||
|
||||
return JsonResponse({
|
||||
'results': [{
|
||||
'id': variant.id,
|
||||
'text': f"{variant.name} ({count} вариантов)",
|
||||
'price': str(variant_price),
|
||||
'actual_price': str(variant_price),
|
||||
'type': 'variant',
|
||||
'count': count
|
||||
}],
|
||||
'pagination': {'more': False}
|
||||
})
|
||||
except (Product.DoesNotExist, ProductKit.DoesNotExist, ProductVariantGroup.DoesNotExist, ValueError):
|
||||
return JsonResponse({'results': [], 'pagination': {'more': False}})
|
||||
|
||||
query = request.GET.get('q', '').strip()
|
||||
@@ -337,15 +357,18 @@ def search_products_and_variants(request):
|
||||
variants = ProductVariantGroup.objects.filter(
|
||||
models.Q(name__icontains=query) |
|
||||
models.Q(description__icontains=query)
|
||||
).prefetch_related('products')[:page_size]
|
||||
).prefetch_related('items__product')[:page_size]
|
||||
|
||||
for variant in variants:
|
||||
count = variant.products.filter(is_active=True).count()
|
||||
count = variant.items.count()
|
||||
variant_price = variant.price or 0
|
||||
variant_results.append({
|
||||
'id': variant.id,
|
||||
'text': f"{variant.name} ({count} вариантов)",
|
||||
'type': 'variant',
|
||||
'count': count
|
||||
'count': count,
|
||||
'price': str(variant_price),
|
||||
'actual_price': str(variant_price)
|
||||
})
|
||||
|
||||
# Формируем финальный результат с группировкой или без
|
||||
@@ -711,3 +734,59 @@ def create_tag_api(request):
|
||||
'success': False,
|
||||
'error': f'Ошибка при создании тега: {str(e)}'
|
||||
}, status=500)
|
||||
|
||||
|
||||
def toggle_tag_status_api(request, pk):
|
||||
"""
|
||||
AJAX endpoint для переключения статуса активности тега.
|
||||
|
||||
Принимает POST запрос и переключает is_active на противоположное значение.
|
||||
|
||||
Возвращает JSON:
|
||||
{
|
||||
"success": true,
|
||||
"is_active": true/false,
|
||||
"message": "Тег активирован" / "Тег деактивирован"
|
||||
}
|
||||
|
||||
Или при ошибке:
|
||||
{
|
||||
"success": false,
|
||||
"error": "Описание ошибки"
|
||||
}
|
||||
"""
|
||||
if request.method != 'POST':
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': 'Метод не поддерживается'
|
||||
}, status=405)
|
||||
|
||||
try:
|
||||
from ..models import ProductTag
|
||||
|
||||
# Получаем тег
|
||||
tag = ProductTag.objects.get(pk=pk)
|
||||
|
||||
# Переключаем статус
|
||||
tag.is_active = not tag.is_active
|
||||
tag.save()
|
||||
|
||||
# Определяем сообщение
|
||||
message = "Тег активирован" if tag.is_active else "Тег деактивирован"
|
||||
|
||||
return JsonResponse({
|
||||
'success': True,
|
||||
'is_active': tag.is_active,
|
||||
'message': message
|
||||
})
|
||||
|
||||
except ProductTag.DoesNotExist:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': 'Тег не найден'
|
||||
}, status=404)
|
||||
except Exception as e:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'error': f'Ошибка при обновлении тега: {str(e)}'
|
||||
}, status=500)
|
||||
|
||||
Reference in New Issue
Block a user