From 95cb1c4bac3c84c0db2939b5651c155ae857b862 Mon Sep 17 00:00:00 2001 From: Andrey Smakotin Date: Thu, 11 Dec 2025 22:27:15 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=B8=D0=BD=D0=B3:=20=D1=83=D0=B1=D1=80=D0=B0=D0=BD?= =?UTF-8?q?=D0=BE=20=D0=B4=D1=83=D0=B1=D0=BB=D0=B8=D1=80=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=B2=D0=B0=D0=BB=D0=B8=D0=B4=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D0=B8=20=D0=B2=D0=B8=D1=82=D1=80=D0=B8=D0=BD=D0=BD=D1=8B?= =?UTF-8?q?=D1=85=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BB=D0=B5=D0=BA=D1=82=D0=BE?= =?UTF-8?q?=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Проблема: Валидация showcase_items выполнялась в двух местах: 1. В pos/views.py - проверка status='in_cart' БЕЗ блокировки БД 2. В showcase_manager.py - перезагрузка с select_for_update() и повторная проверка Это создавало: - Дублирование кода и логики - Возможность race condition между двумя запросами - Избыточные обращения к БД - Мертвый код (неработающие logger.info) Решение (best practices): 1. Views только загружают объекты по ID без фильтров по статусу 2. ВСЯ валидация и бизнес-логика в одном месте - ShowcaseManager.sell_showcase_items 3. select_for_update() гарантирует актуальность данных и блокировку на уровне БД 4. Удален мертвый код (logger.info которые не выполнялись) 5. Убрано избыточное логирование ошибок валидации Результат: - Единое место ответственности (Single Responsibility) - Нет дублирования - Атомарная транзакция с блокировкой - Чистый, понятный код без костылей --- myproject/pos/views.py | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/myproject/pos/views.py b/myproject/pos/views.py index 0450634..1f4f622 100644 --- a/myproject/pos/views.py +++ b/myproject/pos/views.py @@ -1468,26 +1468,10 @@ def pos_checkout(request): # Продаём экземпляры через ShowcaseManager if showcase_item_ids: - # Логируем для отладки - logger.info(f'POS Checkout: попытка продать showcase_item_ids={showcase_item_ids}') - - # Проверяем что все экземпляры заблокированы на текущего пользователя + # ShowcaseManager.sell_showcase_items выполнит всю валидацию с блокировкой БД showcase_items = list(ShowcaseItem.objects.filter( - id__in=showcase_item_ids, - status='in_cart', - locked_by_user=request.user + id__in=showcase_item_ids )) - - logger.info(f'POS Checkout: найдено {len(showcase_items)} заблокированных экземпляров из {len(showcase_item_ids)} запрошенных') - - if len(showcase_items) != len(showcase_item_ids): - # Не все экземпляры доступны - missing_ids = set(showcase_item_ids) - set(item.id for item in showcase_items) - logger.warning(f'POS Checkout: недоступные showcase_item_ids={missing_ids}') - raise ValidationError( - f'Некоторые экземпляры букета уже не заблокированы на вас. ' - f'Обновите страницу и попробуйте снова.' - ) if showcase_items: result = ShowcaseManager.sell_showcase_items(showcase_items, order_item) @@ -1532,10 +1516,8 @@ def pos_checkout(request): }) except (Customer.DoesNotExist, Warehouse.DoesNotExist, Product.DoesNotExist, ProductKit.DoesNotExist) as e: - logger.error(f'POS Checkout: объект не найден: {str(e)}') return JsonResponse({'success': False, 'error': 'Объект не найден'}, status=404) except ValidationError as e: - logger.error(f'POS Checkout: ошибка валидации: {str(e)}', exc_info=True) return JsonResponse({'success': False, 'error': str(e)}, status=400) except json.JSONDecodeError: return JsonResponse({'success': False, 'error': 'Неверный формат JSON'}, status=400)