diff --git a/myproject/products/templates/products/catalog.html b/myproject/products/templates/products/catalog.html
index 8bf471b..3caee28 100644
--- a/myproject/products/templates/products/catalog.html
+++ b/myproject/products/templates/products/catalog.html
@@ -190,6 +190,20 @@
.price-edit-container:hover .add-sale-price {
opacity: 1;
}
+ /* Стили для информации об остатках */
+ .stock-info {
+ font-size: 0.85rem;
+ }
+ .stock-info i {
+ font-size: 0.9rem;
+ }
+ /* Для режима списка - добавляем правильное выравнивание */
+ .catalog-list .catalog-item .stock-info {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.25rem;
+ white-space: nowrap;
+ }
{% endblock %}
@@ -266,6 +280,18 @@
{{ item.name }}
{% endif %}
+
+ {% if item.item_type == 'product' %}
+ {# Информация об остатках для товаров #}
+
{% if item.item_type == 'product' %}
diff --git a/myproject/products/views/catalog_views.py b/myproject/products/views/catalog_views.py
index 0eba3c9..3a34d7a 100644
--- a/myproject/products/views/catalog_views.py
+++ b/myproject/products/views/catalog_views.py
@@ -3,9 +3,10 @@
"""
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import TemplateView
-from django.db.models import Prefetch
+from django.db.models import Prefetch, Sum, Value, DecimalField
+from django.db.models.functions import Coalesce
-from ..models import Product, ProductKit, ProductCategory
+from ..models import Product, ProductKit, ProductCategory, ProductPhoto, ProductKitPhoto
class CatalogView(LoginRequiredMixin, TemplateView):
@@ -27,17 +28,30 @@ class CatalogView(LoginRequiredMixin, TemplateView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
- # Оптимизированный prefetch только для активных товаров и комплектов
+ # Аннотации для остатков
+ total_available = Coalesce(Sum('stocks__quantity_available'), Value(0), output_field=DecimalField())
+ total_reserved = Coalesce(Sum('stocks__quantity_reserved'), Value(0), output_field=DecimalField())
+
+ # Оптимизированный prefetch с аннотациями для активных товаров
active_products_prefetch = Prefetch(
'products',
- queryset=Product.objects.filter(status='active').order_by('name')
+ queryset=Product.objects.filter(status='active').prefetch_related(
+ Prefetch('photos', queryset=ProductPhoto.objects.order_by('order'))
+ ).annotate(
+ total_available=total_available,
+ total_reserved=total_reserved,
+ ).order_by('name')
)
+
+ # Оптимизированный prefetch для комплектов
active_kits_prefetch = Prefetch(
'kits',
- queryset=ProductKit.objects.filter(status='active', is_temporary=False).order_by('name')
+ queryset=ProductKit.objects.filter(status='active', is_temporary=False).prefetch_related(
+ Prefetch('photos', queryset=ProductKitPhoto.objects.order_by('order'))
+ ).order_by('name')
)
- # Все активные категории с prefetch только активных товаров
+ # Все активные категории с оптимизированным prefetch
categories = list(ProductCategory.objects.filter(
is_active=True, is_deleted=False
).prefetch_related(active_products_prefetch, active_kits_prefetch).order_by('name'))
@@ -45,18 +59,31 @@ class CatalogView(LoginRequiredMixin, TemplateView):
# Строим дерево
category_tree = self.build_category_tree(categories, parent=None)
- # Товары и комплекты для правой панели
- products = Product.objects.filter(status='active').prefetch_related('photos').order_by('name')
- for p in products:
- p.item_type = 'product'
- p.main_photo = p.photos.order_by('order').first()
+ # Извлекаем товары и комплекты из уже загруженных категорий
+ # Это избегает дополнительных запросов к БД
+ products_dict = {}
+ kits_dict = {}
+
+ for cat in categories:
+ # Извлекаем из prefetch_related кеша
+ for p in cat.products.all():
+ if p.id not in products_dict:
+ p.item_type = 'product'
+ # main_photo уже загружено через prefetch
+ p.main_photo = p.photos.all()[0] if p.photos.all() else None
+ # Вычисляем свободное количество
+ p.total_free = p.total_available - p.total_reserved
+ products_dict[p.id] = p
+
+ for k in cat.kits.all():
+ if k.id not in kits_dict:
+ k.item_type = 'kit'
+ # main_photo уже загружено через prefetch
+ k.main_photo = k.photos.all()[0] if k.photos.all() else None
+ kits_dict[k.id] = k
- kits = ProductKit.objects.filter(status='active', is_temporary=False).prefetch_related('photos').order_by('name')
- for k in kits:
- k.item_type = 'kit'
- k.main_photo = k.photos.order_by('order').first()
-
- items = sorted(list(products) + list(kits), key=lambda x: x.name)
+ # Объединяем и сортируем
+ items = sorted(list(products_dict.values()) + list(kits_dict.values()), key=lambda x: x.name)
context['category_tree'] = category_tree
context['items'] = items