From c7844aa3d72c9b236d6b431bdebcd4c8a49e4807 Mon Sep 17 00:00:00 2001 From: erjemin Date: Tue, 23 Jun 2026 16:13:20 +0300 Subject: [PATCH] =?UTF-8?q?mod:=20=D0=B2=D0=B0=D0=BB=D0=B8=D0=B4=D0=B0?= =?UTF-8?q?=D1=82=D0=BE=D1=80=20=D1=84=D0=BE=D1=80=D0=BC,=20=D0=BF=D0=B0?= =?UTF-8?q?=D1=80=D1=81=D0=B5=D1=80=D0=B0=20=D0=B8=20=D0=BC=D0=BE=D0=B4?= =?UTF-8?q?=D0=B5=D0=BB=D0=B5=D0=B9=20(10)=20=D0=B2=D0=B0=D0=BB=D0=B8?= =?UTF-8?q?=D0=B4=D0=B0=D1=82=D0=BE=D1=80=20=D0=B2=20save()=20=D0=BC=D0=BE?= =?UTF-8?q?=D0=B4=D0=B5=D0=BB=D0=B8=20=D0=BF=D0=BE=D0=B4=D0=BD=D0=B8=D0=BC?= =?UTF-8?q?=D0=B0=D0=B5=D1=82=20ValidationError=20=D0=B5=D1=81=D0=BB=D0=B8?= =?UTF-8?q?=20=D0=B5=D1=81=D1=82=D1=8C=20=D0=B4=D1=83=D0=B1=D0=BB=D0=B8=20?= =?UTF-8?q?=D0=B2=20=D1=81=D0=B8=D0=BD=D0=BE=D0=BD=D0=B8=D0=BC=D0=B0=D1=85?= =?UTF-8?q?=20=D0=B4=D1=80=D1=83=D0=B3=D0=B8=D1=85=20=D0=B7=D0=B0=D0=BF?= =?UTF-8?q?=D0=B8=D1=81=D0=B5=D0=B9.=20=D0=92=D1=80=D0=B5=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D0=BD=D0=BE=D0=B5=20=D1=80=D0=B5=D1=88=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D0=BF=D0=BE=D0=BA=D0=B0=20=D0=BD=D0=B5=D1=82=20=D0=B1?= =?UTF-8?q?=D1=80=D0=BE=D0=BA=D0=B5=D1=80=D0=B0=20=D1=81=D0=BE=D0=BE=D0=B1?= =?UTF-8?q?=D1=89=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=B8=20=D0=BF=D0=B0=D1=80?= =?UTF-8?q?=D1=81=D0=B5=D1=80=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lpon_site/frontend/utils.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/lpon_site/frontend/utils.py b/lpon_site/frontend/utils.py index 0d20cf1..dda9e9d 100644 --- a/lpon_site/frontend/utils.py +++ b/lpon_site/frontend/utils.py @@ -419,6 +419,7 @@ def validate_entity_for_admin_form(form_instance, cleaned_data, else: # РЕЖИМ: ПЕРВОНАЧАЛЬНАЯ ПРОВЕРКА # Показываем пользователю красную кнопку подтверждения с информацией о совпадениях + # Показываем пользователю красную кнопку подтверждения с информацией о совпадениях for dup in duplicates_queryset: rel_url = f"../{dup.pk}/change/" if form_instance.instance.pk is None else f"../../{dup.pk}/change/" dup_value = getattr(dup, main_field_name, '?') @@ -519,6 +520,7 @@ def validate_and_raise_for_duplicates( match duplicates_result.get(VALIDATE_KEY__MATCH_TYPE): case ValidateMatchType.IS_DUPLICATE: # Точный дубликат найден - это критическая ошибка! + # На уровне save() мы НИКОГДА не должны позволить точные дубликаты. model_name = model_class.__name__ dup_pks = [dup.pk for dup in duplicates_result[VALIDATE_KEY__VALUE]] raise ValidationError( @@ -526,6 +528,26 @@ def validate_and_raise_for_duplicates( f"PK дубликатов: {dup_pks}. Сохранение отменено!" ) + case ValidateMatchType.FIND_IN_SYNONYM: + # Совпадение в синонимах найдено - консервативный подход: всегда блокируем + # Это вызвано вне админки (парсер, API, батник, bulk операции и т.д.) + # где нет пользовательского интерфейса для принятия решения. + # + # TODO: В будущем когда будет парсер/брокер очереди принятия решений: + # - Сохранить состояние экземпляра в очередь (сохранить в брокер) + # - Уведомить пользователя/модератора о конфликте + # - Ожидать решения пользователя (удалить из синонимов или объединить записи) + # - После решения пользователя: автоматически удалить синонимы и пересохранить + # + # На данный момент: просто блокируем и требуем ручного разрешения конфликта. + model_name = model_class.__name__ + dup_pks = [dup.pk for dup in duplicates_result[VALIDATE_KEY__VALUE]] + raise ValidationError( + f"{model_name}.save(): Найдено совпадение в синонимах! " + f"Разрешите на уровне админки или подтвердите решение. " + f"PK конфликтующих записей: {dup_pks}" + ) + case _: # Неизвестный тип совпадения или дубликатов нет # Это нормальная ситуация - логируем только если что-то странное