feat: Добавить трёхуровневую защиту от дубликатов имён товаров, категорий, тегов и комплектов
Реализована полная система обеспечения уникальности названий: 1. **Уровень БД (Model Constraints)** - добавлены UniqueConstraint для: - Product: уникальность имени среди активных товаров - ProductCategory: уникальность имени среди активных категорий - ProductTag: уникальность имени только для активных тегов (неактивные могут повторяться) - ProductKit: уникальность имени среди активных, непроизвременных комплектов 2. **Уровень формы (Form Validation)** - добавлены clean() методы для: - ProductForm, ProductKitForm, ProductCategoryForm, ProductTagForm - Валидация до попытки сохранения в БД - Сохранение введённых данных при ошибке валидации 3. **Уровень представления (IntegrityError Handling)** - добавлена обработка в views: - ProductCategoryCreateView, ProductCategoryUpdateView - ProductTagCreateView, ProductTagUpdateView - ProductKitCreateView, ProductKitUpdateView - create_tag_api: защита от race conditions с fallback поиском Три уровня защиты гарантируют: - Профилактика ошибок на уровне формы - Обработка исключительных ситуаций в views - Защита БД от одновременных запросов (race conditions) - Пользователь видит понятное сообщение об ошибке вместо 500 ошибки 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMix
|
||||
from django.views.generic import ListView, CreateView, DetailView, UpdateView, DeleteView
|
||||
from django.urls import reverse_lazy
|
||||
from django.shortcuts import redirect
|
||||
from django.db import transaction
|
||||
from django.db import transaction, IntegrityError
|
||||
|
||||
from ..models import ProductKit, ProductCategory, ProductTag, ProductKitPhoto, Product, ProductVariantGroup
|
||||
from ..forms import ProductKitForm, KitItemFormSetCreate, KitItemFormSetUpdate
|
||||
@@ -210,6 +210,27 @@ class ProductKitCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateVi
|
||||
)
|
||||
|
||||
return redirect('products:productkit-list')
|
||||
except IntegrityError as e:
|
||||
# Обработка нарушения уникальности в БД
|
||||
error_msg = str(e).lower()
|
||||
if 'unique_active_kit_name' in error_msg or ('name' in error_msg and 'duplicate' in error_msg):
|
||||
messages.error(
|
||||
self.request,
|
||||
f'Ошибка: комплект с названием "{form.instance.name}" уже существует. '
|
||||
'Пожалуйста, используйте другое название.'
|
||||
)
|
||||
elif 'slug' in error_msg or 'unique' in error_msg:
|
||||
messages.error(
|
||||
self.request,
|
||||
'Ошибка: комплект с таким названием уже существует. '
|
||||
'Пожалуйста, используйте другое название.'
|
||||
)
|
||||
else:
|
||||
messages.error(
|
||||
self.request,
|
||||
'Ошибка при сохранении комплекта. Пожалуйста, проверьте введённые данные.'
|
||||
)
|
||||
return self.form_invalid(form)
|
||||
except Exception as e:
|
||||
messages.error(self.request, f'Ошибка при сохранении: {str(e)}')
|
||||
import traceback
|
||||
@@ -391,6 +412,27 @@ class ProductKitUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateVi
|
||||
return redirect('products:productkit-update', pk=self.object.pk)
|
||||
else:
|
||||
return redirect('products:productkit-list')
|
||||
except IntegrityError as e:
|
||||
# Обработка нарушения уникальности в БД
|
||||
error_msg = str(e).lower()
|
||||
if 'unique_active_kit_name' in error_msg or ('name' in error_msg and 'duplicate' in error_msg):
|
||||
messages.error(
|
||||
self.request,
|
||||
f'Ошибка: комплект с названием "{form.instance.name}" уже существует. '
|
||||
'Пожалуйста, используйте другое название.'
|
||||
)
|
||||
elif 'slug' in error_msg or 'unique' in error_msg:
|
||||
messages.error(
|
||||
self.request,
|
||||
'Ошибка: комплект с таким названием уже существует. '
|
||||
'Пожалуйста, используйте другое название.'
|
||||
)
|
||||
else:
|
||||
messages.error(
|
||||
self.request,
|
||||
'Ошибка при сохранении комплекта. Пожалуйста, проверьте введённые данные.'
|
||||
)
|
||||
return self.form_invalid(form)
|
||||
except Exception as e:
|
||||
messages.error(self.request, f'Ошибка при сохранении: {str(e)}')
|
||||
import traceback
|
||||
|
||||
Reference in New Issue
Block a user