Django rest_framework с пользовательской моделью пользователя — повторяющееся значение ключа нарушает уникальное ограничение

#python #django #django-rest-framework

#python #django #django-rest-framework

Вопрос:

Я начинаю изучать django (rest_framework) и создаю приложение. Я создал пользовательскую модель пользователя и менеджера, ниже кода:

 class CustomUserManager(BaseUserManager):
    """Define a model manager for User model with no username field."""

    def _create_user(self, email, password=None, **extra_fields):
        """Create and save a User with the given email and password."""
        if not email:
            raise ValueError('The given email must be set')
        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_user(self, email, password=None, **extra_fields):
        extra_fields.setdefault('is_staff', False)
        extra_fields.setdefault('is_superuser', False)
        return self._create_user(email, password, **extra_fields)

    def create_superuser(self, email, password=None, **extra_fields):
        """Create and save a SuperUser with the given email and password."""
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError('Superuser must have is_staff=True.')
        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True.')

        return self._create_user(email, password, **extra_fields)


class CustomUser(AbstractUser):
    email = models.EmailField(_('email address'), unique=True)
    username = models.CharField(_('username'), max_length=255, unique=False, blank=True)
    mobile = models.CharField(_('mobile'),max_length=255, blank=True)
    picture = models.ImageField(upload_to='images/thumbnail/%Y/%m/%d/', null=True, blank=True)

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    @property
    def picture_preview(self):
        if self.picture:
            return mark_safe('<img src="{}" width="300" height="300" />'.format(self.picture.url))
        return ""

    objects = CustomUserManager()
 

Я также настроил представление для администратора .. и все работает нормально.

Теперь, когда я регистрирую нового пользователя от администратора и, например, дважды отправляю одно и то же электронное письмо, я получаю это сообщение об ошибке: пользователь с этим адресом электронной почты уже существует.

Но если я зарегистрирую нового пользователя с помощью postman для вызова API, я получу сообщение об ошибке ниже:

 django.db.utils.IntegrityError: duplicate key value violates unique constraint "accounts_customuser_email_key" 
DETAIL:  Key (email)=(mario@mario.it) already exists.
 

Ниже класса сериализатора

 class RegisterSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True)
    email = serializers.EmailField()

    class Meta:
        model = CustomUser
        fields = ['email', 'password']

    def validate(self, attrs):
        # Check if user exist
        email = attrs.get('email', '')
        # if CustomUser.objects.filter(email=email).exists():
        #     raise serializers.ValidationError(
        #         'User with this Email address already exists.')

        # Validate password
        errors = dict()
        user = CustomUser(**attrs)
        password = attrs.get('password', '')
        try:
            # validate the password and catch the exception
            validators.validate_password(password=password, user=user)

        # the exception raised here is different than serializers.ValidationError
        except ValidationError as e:
            errors['password'] = list(e.messages)

        if errors:
            raise serializers.ValidationError(errors)

        return attrs

    def create(self, validated_data):
        return CustomUser.objects.create_user(**validated_data)
 

В валидаторе я прокомментировал код, который проверяет, зарегистрировал ли пользователь это, потому что я ожидаю, что django должен знать, зарегистрирован ли email уже без дополнительного кода, поскольку в admin он работает без проблем.

и класс представления:

 class RegisterView(generics.GenericAPIView):

    serializers_class = RegisterSerializer

    def post(self, request):
        user = request.data
        serializer = self.serializers_class(data=user)
        serializer.is_valid(raise_exception=True)
        serializer.save()

        user_data = serializer.data
        return Response(user_data, status.HTTP_201_CREATED)
 

Мой вопрос в том, делаю ли я что-то не так? Я что-то пропустил?

Спасибо всем … любая помощь будет оценена.

Ответ №1:

Если вы создаете пользователя с тем же адресом электронной почты, django проверяет и выдает IntegrityError. Вы должны перехватить это исключение и вернуть ответ. Также лучше, чтобы вы делали это в части представления, а не в методе проверки сериализатора, поскольку задача метода проверки сериализатора заключается только в проверке правильности заданных входных данных. итак, ваше представление должно выглядеть примерно так.

 class RegisterView(generics.GenericAPIView):

    serializers_class = RegisterSerializer

    def post(self, request):
        user = request.data
        serializer = self.serializers_class(data=user)
        serializer.is_valid(raise_exception=True)
        try:
            user = User.objects.create_user(
                email=serializer.data.get("email"),
                password=request.data.get("password"),
            )
        except IntegrityError:
            return Response("Email already exists.",
                status=status.HTTP_406_NOT_ACCEPTABLE,
            )
        response = jwt_response_payload_handler(user.get_jwt_token(), user, request) #  some payload handler, for user information
        return Response(response, status.HTTP_201_CREATED)