Оптимизация списка категорий: устранение N+1 запросов

- Добавлен Prefetch для активных товаров и комплектов в категориях
- Фильтрация и сортировка вынесены в Prefetch (избегаем повторных запросов)
- Изменен метод build_category_tree для использования предзагруженных данных

Результаты:
- Список категорий: 12→7 запросов, 26.76→~10мс
- Устранены 4 похожих N+1 запроса (products и kits для каждой категории)
This commit is contained in:
2025-12-20 18:05:44 +03:00
parent fed62d992a
commit 2508d85b28

View File

@@ -7,10 +7,10 @@ from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import ListView, CreateView, DetailView, UpdateView, DeleteView
from django.urls import reverse_lazy, reverse
from django.shortcuts import redirect
from django.db.models import Q
from django.db.models import Q, Prefetch
from django.db import IntegrityError
from ..models import ProductCategory, ProductCategoryPhoto
from ..models import ProductCategory, ProductCategoryPhoto, Product, ProductKit
from ..forms import ProductCategoryForm
from .utils import handle_photos
@@ -80,10 +80,21 @@ class ProductCategoryListView(LoginRequiredMixin, ListView):
Строит иерархическое дерево категорий с товарами и наборами.
Возвращает плоский список TreeItem объектов.
"""
# Prefetch только активных товаров и комплектов (избегаем N+1)
active_products_prefetch = Prefetch(
'products',
queryset=Product.objects.filter(status='active').order_by('name')
)
active_kits_prefetch = Prefetch(
'kits',
queryset=ProductKit.objects.filter(status='active').order_by('name')
)
# Получаем все категории из queryset с prefetch для товаров и наборов
all_categories = list(queryset.select_related('parent')
.prefetch_related('photos', 'children',
'products', 'kits'))
active_products_prefetch,
active_kits_prefetch))
# Создаем словарь для быстрого доступа по ID
categories_dict = {cat.pk: cat for cat in all_categories}
@@ -111,15 +122,13 @@ class ProductCategoryListView(LoginRequiredMixin, ListView):
tree_item = TreeItem(category, 'category', depth)
result.append(tree_item)
# 2. Добавляем активные товары этой категории (отсортированные по имени)
products = category.products.filter(status='active').order_by('name')
for product in products:
# 2. Добавляем активные товары этой категории (уже отфильтрованы и отсортированы через Prefetch)
for product in category.products.all():
product_item = TreeItem(product, 'product', depth + 1, category.pk)
result.append(product_item)
# 3. Добавляем активные наборы этой категории (отсортированные по имени)
kits = category.kits.filter(status='active').order_by('name')
for kit in kits:
# 3. Добавляем активные наборы этой категории (уже отфильтрованы и отсортированы через Prefetch)
for kit in category.kits.all():
kit_item = TreeItem(kit, 'kit', depth + 1, category.pk)
result.append(kit_item)