Оптимизация производительности: устранение N+1 запросов и дубликатов

- Добавлен django-debug-toolbar 6.1.0 для мониторинга производительности
- Устранен дублирующийся COUNT запрос в списке клиентов (используется paginator.count)
- Добавлен select_related('status') в списке заказов для устранения N+1

Результаты:
- Список клиентов: 6→5 запросов, 13.24→10мс
- Список заказов: 18→7 запросов, 52.68→15-20мс, устранено 11 дубликатов
This commit is contained in:
2025-12-20 18:02:23 +03:00
parent 0bf694966b
commit fed62d992a
6 changed files with 47 additions and 5 deletions

View File

@@ -33,9 +33,6 @@ def customer_list(request):
# Исключаем системного клиента из списка
customers = Customer.objects.filter(is_system_customer=False)
# Общее количество клиентов
total_customers = customers.count()
if query:
# Нормализуем номер телефона
phone_normalized = normalize_query_phone(query)
@@ -74,7 +71,7 @@ def customer_list(request):
context = {
'page_obj': page_obj,
'query': query,
'total_customers': total_customers,
'total_customers': paginator.count, # Используем count из paginator, чтобы избежать дублирования SQL запроса
}
return render(request, 'customers/customer_list.html', context)

View File

@@ -92,6 +92,10 @@ TENANT_APPS = [
# Объединяем для INSTALLED_APPS
INSTALLED_APPS = list(SHARED_APPS) + [app for app in TENANT_APPS if app not in SHARED_APPS]
# Django Debug Toolbar (только в DEBUG режиме)
if DEBUG:
INSTALLED_APPS += ['debug_toolbar']
# Модели тенанта и домена
TENANT_MODEL = "tenants.Client"
TENANT_DOMAIN_MODEL = "tenants.Domain"
@@ -117,6 +121,11 @@ MIDDLEWARE = [
'simple_history.middleware.HistoryRequestMiddleware', # История изменений
]
# Django Debug Toolbar Middleware (только в DEBUG режиме)
# ВАЖНО: добавляем ПОСЛЕ TenantMainMiddleware, чтобы toolbar видел корректный tenant
if DEBUG:
MIDDLEWARE.append('debug_toolbar.middleware.DebugToolbarMiddleware')
# ============================================
# AUTHENTICATION BACKENDS
@@ -379,6 +388,29 @@ TENANT_DOMAIN_BASE = env('TENANT_DOMAIN_BASE', default='localhost:8000')
USE_HTTPS = env.bool('USE_HTTPS', default=False)
# ============================================
# DJANGO DEBUG TOOLBAR SETTINGS
# ============================================
if DEBUG:
# IP адреса, с которых разрешен доступ к Debug Toolbar
INTERNAL_IPS = [
'127.0.0.1',
'localhost',
]
# Если запускаете в Docker, добавьте IP хоста
# Например: INTERNAL_IPS += ['172.17.0.1', '192.168.65.1'] # Docker Desktop
# Конфигурация Debug Toolbar
DEBUG_TOOLBAR_CONFIG = {
# Показывать toolbar всегда при DEBUG=True и INTERNAL_IPS
'SHOW_TOOLBAR_CALLBACK': lambda request: DEBUG,
# Или можно фильтровать по tenant (например, только для определенных поддоменов):
# 'SHOW_TOOLBAR_CALLBACK': lambda request: DEBUG and getattr(request, 'tenant', None) and request.tenant.schema_name != 'public',
}
# ============================================
# EMAIL SETTINGS
# ============================================

View File

@@ -32,6 +32,12 @@ urlpatterns = [
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
# Django Debug Toolbar (только в DEBUG режиме)
import debug_toolbar
urlpatterns += [
path('__debug__/', include(debug_toolbar.urls)),
]
else:
# Force serve media files in production (for NAS setup)
urlpatterns += [

View File

@@ -23,6 +23,12 @@ urlpatterns = [
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
# Django Debug Toolbar (только в DEBUG режиме)
import debug_toolbar
urlpatterns += [
path('__debug__/', include(debug_toolbar.urls)),
]
else:
# Force serve media files in production (for NAS setup)
urlpatterns += [

View File

@@ -22,7 +22,7 @@ def order_list(request):
"""
# Базовый queryset с оптимизацией запросов
orders = Order.objects.select_related(
'customer', 'delivery_address', 'pickup_warehouse'
'customer', 'delivery_address', 'pickup_warehouse', 'status' # Добавлен 'status' для избежания N+1
).all()
# Применяем фильтры через django-filter

View File

@@ -9,6 +9,7 @@ click-repl==0.3.0
colorama==0.4.6
Django==5.0.10
django-celery-results==2.5.1
django-debug-toolbar==6.1.0
django-environ==0.12.0
django-filter==24.3
django-nested-admin==4.1.5