diff --git a/myproject/customers/forms.py b/myproject/customers/forms.py index 0171986..f05f4a4 100644 --- a/myproject/customers/forms.py +++ b/myproject/customers/forms.py @@ -1,4 +1,5 @@ from django import forms +from django.core.exceptions import ValidationError from phonenumber_field.formfields import PhoneNumberField from phonenumber_field.widgets import PhoneNumberPrefixWidget from .models import Customer @@ -6,6 +7,7 @@ from .models import Customer class CustomerForm(forms.ModelForm): phone = PhoneNumberField( region='BY', + required=False, help_text='Формат: +375XXXXXXXXX или 80XXXXXXXXX', widget=forms.TextInput(attrs={'placeholder': '+375XXXXXXXXX'}) ) @@ -35,4 +37,44 @@ class CustomerForm(forms.ModelForm): field.widget.attrs.update({'class': 'form-control'}) else: # Regular input fields get form-control class - field.widget.attrs.update({'class': 'form-control'}) \ No newline at end of file + field.widget.attrs.update({'class': 'form-control'}) + + def clean_email(self): + """Проверяет уникальность email при создании/редактировании""" + email = self.cleaned_data.get('email') + + # Если email пустой, это нормально (blank=True) + if not email: + return email + + # Проверяем уникальность + queryset = Customer.objects.filter(email=email) + + # При редактировании исключаем текущий экземпляр + if self.instance and self.instance.pk: + queryset = queryset.exclude(pk=self.instance.pk) + + if queryset.exists(): + raise ValidationError('Клиент с таким email уже существует.') + + return email + + def clean_phone(self): + """Проверяет уникальность телефона при создании/редактировании""" + phone = self.cleaned_data.get('phone') + + # Если телефон пустой, это нормально (blank=True) + if not phone: + return phone + + # Проверяем уникальность + queryset = Customer.objects.filter(phone=phone) + + # При редактировании исключаем текущий экземпляр + if self.instance and self.instance.pk: + queryset = queryset.exclude(pk=self.instance.pk) + + if queryset.exists(): + raise ValidationError('Клиент с таким номером телефона уже существует.') + + return phone \ No newline at end of file diff --git a/myproject/customers/migrations/0003_alter_customer_email.py b/myproject/customers/migrations/0003_alter_customer_email.py new file mode 100644 index 0000000..a91fb02 --- /dev/null +++ b/myproject/customers/migrations/0003_alter_customer_email.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.10 on 2025-11-11 14:34 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('customers', '0002_remove_address_model'), + ] + + operations = [ + migrations.AlterField( + model_name='customer', + name='email', + field=models.EmailField(blank=True, max_length=254, null=True, verbose_name='Email'), + ), + ] diff --git a/myproject/customers/migrations/0004_alter_customer_email.py b/myproject/customers/migrations/0004_alter_customer_email.py new file mode 100644 index 0000000..0fc8602 --- /dev/null +++ b/myproject/customers/migrations/0004_alter_customer_email.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.10 on 2025-11-11 14:35 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('customers', '0003_alter_customer_email'), + ] + + operations = [ + migrations.AlterField( + model_name='customer', + name='email', + field=models.EmailField(blank=True, max_length=254, null=True, unique=True, verbose_name='Email'), + ), + ] diff --git a/myproject/customers/models.py b/myproject/customers/models.py index ce8487a..9ea375b 100644 --- a/myproject/customers/models.py +++ b/myproject/customers/models.py @@ -11,8 +11,8 @@ class Customer(models.Model): """ # Name field that is not required to be unique name = models.CharField(max_length=200, blank=True, verbose_name="Имя") - - email = models.EmailField(blank=True, verbose_name="Email") + + email = models.EmailField(blank=True, null=True, unique=True, verbose_name="Email") # Phone with validation using django-phonenumber-field phone = PhoneNumberField( diff --git a/myproject/customers/views.py b/myproject/customers/views.py index b2f4d31..500a78e 100644 --- a/myproject/customers/views.py +++ b/myproject/customers/views.py @@ -380,67 +380,54 @@ def api_create_customer(request): try: data = json.loads(request.body) - name = data.get('name', '') - phone = data.get('phone') - email = data.get('email', '') + # Нормализуем данные + name = data.get('name', '').strip() if data.get('name') else '' + phone = data.get('phone', '').strip() if data.get('phone') else '' + email = data.get('email', '').strip() if data.get('email') else '' - # Нормализуем строки (может быть None из JavaScript) - name = name.strip() if name else '' - phone = phone.strip() if phone else '' - email = email.strip() if email else '' + # Подготавливаем данные для формы + form_data = { + 'name': name, + 'phone': phone if phone else None, + 'email': email if email else None, + } + + # Используем форму для валидации и создания + form = CustomerForm(data=form_data) + + if form.is_valid(): + # Сохраняем клиента через форму (автоматически вызывает все валидации) + customer = form.save() + + phone_display = str(customer.phone) if customer.phone else '' + + return JsonResponse({ + 'success': True, + 'id': customer.pk, + 'name': customer.name, + 'phone': phone_display, + 'email': customer.email if customer.email else '', + }, status=201) + else: + # Собираем ошибки валидации + errors = [] + for field, field_errors in form.errors.items(): + for error in field_errors: + errors.append(error) + + # Возвращаем первую ошибку + error_message = errors[0] if errors else 'Ошибка валидации данных' - # Валидация: имя обязательно - if not name: return JsonResponse({ 'success': False, - 'error': 'Имя клиента обязательно' + 'error': error_message }, status=400) - # Нормализуем телефон если он указан - if phone: - phone = normalize_query_phone(phone) - - # Проверяем, не существует ли уже клиент с таким телефоном - if phone and Customer.objects.filter(phone=phone).exists(): - return JsonResponse({ - 'success': False, - 'error': 'Клиент с таким номером телефона уже существует' - }, status=400) - - # Проверяем, не существует ли уже клиент с таким email - if email and Customer.objects.filter(email=email).exists(): - return JsonResponse({ - 'success': False, - 'error': 'Клиент с таким email уже существует' - }, status=400) - - # Создаем нового клиента - customer = Customer.objects.create( - name=name, - phone=phone if phone else None, - email=email if email else None - ) - - phone_display = str(customer.phone) if customer.phone else '' - - return JsonResponse({ - 'success': True, - 'id': customer.pk, - 'name': customer.name, - 'phone': phone_display, - 'email': customer.email, - }, status=201) - except json.JSONDecodeError: return JsonResponse({ 'success': False, 'error': 'Некорректный JSON' }, status=400) - except ValidationError as e: - return JsonResponse({ - 'success': False, - 'error': str(e) - }, status=400) except Exception as e: return JsonResponse({ 'success': False,