Расширен API поиска товаров фильтрацией и фото

- Добавлены параметры фильтрации: category, tag, in_stock
- Функция _apply_product_filters() для применения фильтров к queryset
- Функция _get_product_photo_url() для получения главного фото товара
- В ответ API добавлено поле photo_url с URL фото товара
- Отключено кэширование при использовании фильтров
- Улучшена производительность запросов с использованием order_by и values
This commit is contained in:
2025-12-10 23:36:22 +03:00
parent ccab09fb40
commit f8808c6ba0

View File

@@ -7,11 +7,30 @@ from django.core.cache import cache
from django.core.exceptions import ValidationError
import logging
from ..models import Product, ProductVariantGroup, ProductKit, ProductCategory
from ..models import Product, ProductVariantGroup, ProductKit, ProductCategory, ProductPhoto
logger = logging.getLogger(__name__)
def _get_product_photo_url(product_id):
"""Получает URL главного фото товара (первого по порядку)."""
photo = ProductPhoto.objects.filter(product_id=product_id).order_by('order').first()
if photo and photo.image:
return photo.image.url
return None
def _apply_product_filters(queryset, category_id=None, tag_id=None, in_stock_only=False):
"""Применяет фильтры к queryset товаров."""
if category_id:
queryset = queryset.filter(categories__id=category_id)
if tag_id:
queryset = queryset.filter(tags__id=tag_id)
if in_stock_only:
queryset = queryset.filter(in_stock=True)
return queryset.distinct()
def search_products_and_variants(request):
"""
API endpoint для поиска товаров, групп вариантов и комплектов (совместимость с Select2).
@@ -22,6 +41,9 @@ def search_products_and_variants(request):
- id: ID товара/комплекта для получения его данных (формат: "product_123" или "kit_456")
- type: 'product', 'variant', 'kit' или 'all' (опционально, по умолчанию 'all')
- page: номер страницы для пагинации (по умолчанию 1)
- category: ID категории для фильтрации (опционально)
- tag: ID тега для фильтрации (опционально)
- in_stock: 'true' для фильтрации только товаров в наличии (опционально)
Возвращает JSON в формате Select2 с группировкой:
{
@@ -130,14 +152,22 @@ def search_products_and_variants(request):
page = int(request.GET.get('page', 1))
page_size = 30
# Дополнительные фильтры
category_id = request.GET.get('category', '').strip()
tag_id = request.GET.get('tag', '').strip()
in_stock_only = request.GET.get('in_stock', '').lower() == 'true'
results = []
# Проверяем, есть ли дополнительные фильтры
has_filters = category_id or tag_id or in_stock_only
# Если поиска нет - показываем популярные товары и комплекты
if not query or len(query) < 2:
# Кэшируем популярные товары на 1 час
# Кэшируем только если нет фильтров
if not has_filters:
cache_key = f'popular_items_{search_type}'
cached_results = cache.get(cache_key)
if cached_results:
return JsonResponse(cached_results)
@@ -146,8 +176,10 @@ def search_products_and_variants(request):
if search_type in ['all', 'product']:
# Показываем последние добавленные активные товары
products = Product.objects.filter(status='active')\
.order_by('-created_at')[:page_size]\
products_qs = Product.objects.filter(status='active')
# Применяем фильтры
products_qs = _apply_product_filters(products_qs, category_id, tag_id, in_stock_only)
products = products_qs.order_by('-created_at')[:page_size]\
.values('id', 'name', 'sku', 'price', 'sale_price', 'in_stock')
for product in products:
@@ -165,7 +197,8 @@ def search_products_and_variants(request):
'price': str(product['price']) if product['price'] else None,
'actual_price': str(actual_price) if actual_price else '0',
'in_stock': product['in_stock'],
'type': 'product'
'type': 'product',
'photo_url': _get_product_photo_url(product['id'])
})
if search_type in ['all', 'kit']:
@@ -214,6 +247,8 @@ def search_products_and_variants(request):
'results': results,
'pagination': {'more': False}
}
# Кэшируем только если нет фильтров
if not has_filters:
cache.set(cache_key, response_data, 3600)
return JsonResponse(response_data)
@@ -269,6 +304,9 @@ def search_products_and_variants(request):
)
).order_by('-relevance', 'name')
# Применяем дополнительные фильтры
products_query = _apply_product_filters(products_query, category_id, tag_id, in_stock_only)
total_products = products_query.count()
start = (page - 1) * page_size
end = start + page_size
@@ -290,7 +328,8 @@ def search_products_and_variants(request):
'price': str(product['price']) if product['price'] else None,
'actual_price': str(actual_price) if actual_price else '0',
'in_stock': product['in_stock'],
'type': 'product'
'type': 'product',
'photo_url': _get_product_photo_url(product['id'])
})
has_more = total_products > end