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