From 3a7d8031081b4092adea63cb23f203b0a297b9b8 Mon Sep 17 00:00:00 2001 From: Toksi86 Date: Wed, 27 Aug 2025 14:01:06 +0500 Subject: [PATCH 1/2] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D0=B2=D0=B5=D0=B4?= =?UTF-8?q?=D0=BD=D1=8B=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B8=20=D0=B2?= =?UTF-8?q?=D0=B0=D0=BB=D0=B8=D0=B4=D0=B0=D1=86=D0=B8=D0=B8=20=D0=B4=D0=B0?= =?UTF-8?q?=D0=BD=D0=BD=D1=8B=D1=85=20=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...54_alter_customuser_first_name_and_more.py | 57 +++++++++++++++++++ users/models.py | 29 ++++++++-- users/serializers.py | 14 +++++ users/validators.py | 27 ++++----- 4 files changed, 109 insertions(+), 18 deletions(-) create mode 100644 users/migrations/0054_alter_customuser_first_name_and_more.py diff --git a/users/migrations/0054_alter_customuser_first_name_and_more.py b/users/migrations/0054_alter_customuser_first_name_and_more.py new file mode 100644 index 00000000..12b193ec --- /dev/null +++ b/users/migrations/0054_alter_customuser_first_name_and_more.py @@ -0,0 +1,57 @@ +# Generated by Django 4.2.11 on 2025-08-20 07:55 + +from django.db import migrations, models +import functools +import users.validators + + +class Migration(migrations.Migration): + + dependencies = [ + ("users", "0053_customuser_is_mospolytech_student_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="customuser", + name="first_name", + field=models.CharField( + max_length=255, + validators=[ + functools.partial( + users.validators.user_name_validator, *(), **{"field_name": "Имя"} + ) + ], + ), + ), + migrations.AlterField( + model_name="customuser", + name="last_name", + field=models.CharField( + max_length=255, + validators=[ + functools.partial( + users.validators.user_name_validator, + *(), + **{"field_name": "Фамилия"} + ) + ], + ), + ), + migrations.AlterField( + model_name="customuser", + name="patronymic", + field=models.CharField( + blank=True, + max_length=255, + null=True, + validators=[ + functools.partial( + users.validators.user_name_validator, + *(), + **{"field_name": "Отчество"} + ) + ], + ), + ), + ] diff --git a/users/models.py b/users/models.py index 3f231842..d3e3a91f 100644 --- a/users/models.py +++ b/users/models.py @@ -1,6 +1,9 @@ +from functools import partial + from django.contrib.auth.models import AbstractUser from django.contrib.contenttypes.fields import GenericRelation from django.core.exceptions import ValidationError +from django.core.validators import URLValidator from django.db import models from django.db.models import QuerySet from django.utils import timezone @@ -60,9 +63,18 @@ class CustomUser(AbstractUser): INVESTOR = constants.INVESTOR username = None - email = models.EmailField(unique=True) - first_name = models.CharField(max_length=255, validators=[user_name_validator]) - last_name = models.CharField(max_length=255, validators=[user_name_validator]) + email = models.EmailField( + unique=True, + error_messages={ + "unique": "Пользователь с таким email уже существует", + }, + ) + first_name = models.CharField( + max_length=255, validators=[partial(user_name_validator, field_name="Имя")] + ) + last_name = models.CharField( + max_length=255, validators=[partial(user_name_validator, field_name="Фамилия")] + ) password = models.CharField(max_length=255) is_active = models.BooleanField(default=False, editable=False) user_type = models.PositiveSmallIntegerField( @@ -74,7 +86,10 @@ class CustomUser(AbstractUser): editable=False, ) patronymic = models.CharField( - max_length=255, validators=[user_name_validator], null=True, blank=True + max_length=255, + validators=[partial(user_name_validator, field_name="Отчество")], + null=True, + blank=True, ) # TODO need to be removed in future `key_skills` -> `skills`. key_skills = models.CharField( @@ -87,7 +102,11 @@ class CustomUser(AbstractUser): "core.SkillToObject", related_query_name="users", ) - avatar = models.URLField(null=True, blank=True) + avatar = models.URLField( + null=True, + blank=True, + validators=[URLValidator(message="Введите корректный URL")], + ) birthday = models.DateField( validators=[user_birthday_validator], ) diff --git a/users/serializers.py b/users/serializers.py index 0e074f9e..879ce134 100644 --- a/users/serializers.py +++ b/users/serializers.py @@ -7,6 +7,7 @@ from django.forms.models import model_to_dict from rest_framework import serializers from rest_framework.exceptions import ValidationError +from rest_framework.validators import UniqueValidator from rest_framework_simplejwt.serializers import TokenObtainPairSerializer from core.models import Skill, SkillToObject, Specialization, SpecializationCategory @@ -692,6 +693,19 @@ class Meta: class UserListSerializer( serializers.ModelSerializer[CustomUser], SkillsWriteSerializerMixin ): + email = serializers.EmailField( + validators=[ + UniqueValidator( + queryset=CustomUser.objects.all(), + message="Пользователь с таким email уже существует", + ) + ], + error_messages={"invalid": "Введите корректный email адрес"}, + ) + avatar = serializers.URLField( + error_messages={"invalid": "Введите корректный url адрес"} + ) + member = MemberSerializer(required=False) is_online = serializers.SerializerMethodField() diff --git a/users/validators.py b/users/validators.py index 41929397..61e9e527 100644 --- a/users/validators.py +++ b/users/validators.py @@ -1,9 +1,8 @@ +import phonenumbers +from django.core.exceptions import ValidationError as DjangoValidationError from django.utils import timezone from rest_framework import serializers from rest_framework.exceptions import ValidationError -from django.core.exceptions import ValidationError as DjangoValidationError - -import phonenumbers from users.constants import NOT_VALID_NUMBER_MESSAGE @@ -18,19 +17,19 @@ def user_birthday_validator(birthday): raise ValidationError("Человек младше 12 лет") -def user_name_validator(name): - """returns true if name is valid""" - # TODO: add check for vulgar words +def user_name_validator(value, field_name="Поле"): + """Валидатор для имени, фамилии и отчества""" + if not value: + return valid_name_chars = "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ" - for letter in name: + for letter in value: if letter.upper() not in valid_name_chars: - raise ValidationError( - "Имя содержит недопустимые символы. Могут быть только символы кириллического алфавита." + raise DjangoValidationError( + f"{field_name} содержит недопустимые символы. Могут быть только символы кириллического алфавита." ) - if len(name) < 2: - raise ValidationError("Имя слишком короткое") - return True + if len(value) < 2: + raise DjangoValidationError(f"Поле '{field_name}' слишком короткое") def specialization_exists_validator(pk: int): @@ -49,7 +48,9 @@ def user_experience_years_range_validator(value: int): (2000 - `now.year`) """ if value not in range(2000, timezone.now().year + 1): - raise DjangoValidationError(f"Год должен быть в диапазоне 2000 - {timezone.now().year}") + raise DjangoValidationError( + f"Год должен быть в диапазоне 2000 - {timezone.now().year}" + ) def user_phone_number_validation(value: str): From 8c1799ce7d719a2de376c6293670aacedf48d95d Mon Sep 17 00:00:00 2001 From: Toksi86 Date: Wed, 27 Aug 2025 14:25:16 +0500 Subject: [PATCH 2/2] =?UTF-8?q?=D0=9F=D0=BE=D0=BB=D0=B5=20=D0=B0=D0=B2?= =?UTF-8?q?=D0=B0=D1=82=D0=B0=D1=80=20=D0=B1=D0=BE=D0=BB=D1=8C=D1=88=D0=B5?= =?UTF-8?q?=20=D0=BD=D0=B5=20=D0=BE=D0=B1=D1=8F=D0=B7=D0=B0=D1=82=D0=B5?= =?UTF-8?q?=D0=BB=D1=8C=D0=BD=D0=BE=D0=B5=20=D0=BF=D1=80=D0=B8=20=D1=81?= =?UTF-8?q?=D0=B5=D1=80=D0=B8=D0=B0=D0=BB=D0=B8=D0=B7=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D0=B8=20=D0=B4=D0=B0=D0=BD=D1=8B=D1=85,=20=D0=BA=D0=B0=D0=BA?= =?UTF-8?q?=20=D0=B1=D1=8B=D0=BB=D0=BE=20=D0=B7=D0=B0=D0=BF=D0=BB=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=BE=20=D0=B8=D0=B7?= =?UTF-8?q?=D0=BD=D0=B0=D1=87=D0=B0=D0=BB=D1=8C=D0=BD=D0=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- users/serializers.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/users/serializers.py b/users/serializers.py index 879ce134..c43bc1d9 100644 --- a/users/serializers.py +++ b/users/serializers.py @@ -703,7 +703,9 @@ class UserListSerializer( error_messages={"invalid": "Введите корректный email адрес"}, ) avatar = serializers.URLField( - error_messages={"invalid": "Введите корректный url адрес"} + required=False, + allow_null=True, + error_messages={"invalid": "Введите корректный url адрес"}, ) member = MemberSerializer(required=False)