Search code examples
djangodjango-rest-frameworkdjango-serializer

How are all serializer errors returned in DRF at once?


I'm testing for multiple validation errors to be raised in(UserRegistrationSerializer). Yet DRF only returns the first error that is raised: {'username': [ErrorDetail(string='Choose a different username', code='invalid')]}

I'm expecting: {'username': [ErrorDetail(string='Choose a different username', code='invalid')], 'password2': [ErrorDetail(string='Password confirmation failed', code='invalid')]

How can multiple errors be accounted for once a serializer is validated as in the documentation example?

https://www.django-rest-framework.org/api-guide/serializers/#validation

class TestRegisterationSerializer__002(TestCase):
    '''Verify that the registeration process fails with respect
    to selecting an unavailable username and password confirmation'''

    @classmethod
    def setUpTestData(cls):
        User.objects.create_user(username="Python")
        cls.data = {
            'username': "Python",
            'password': "#secret#",
            'password2': "Secret"
        }
        cls.error_messages = [
            "Choose a different username", "Password confirmation failed"
        ]
        cls.serializer = UserRegisterationSerializer(data=cls.data)

    def test_user_registeration_invalid_confirmation(self):
        self.serializer.is_valid()
        print(self.serializer.errors)
import re

from django.contrib.auth.models import User

from rest_framework import serializers


class UsernameSerializer(serializers.ModelSerializer):

    username = serializers.SlugField(min_length=4, max_length=12)

    def validate_username(self, value):
        try:
            self.Meta.model.objects.get(username__iexact=value)
        except self.Meta.model.DoesNotExist:
            return value
        raise serializers.ValidationError("Choose a different username")

    class Meta:
        fields = ['username', ]
        model = User


class LoginSerializer(UsernameSerializer):

    password = serializers.RegexField(
        r"[0-9A-Za-z]+", min_length=5, max_length=8
    )

    def validate(self, data):
        username, password = [
            input.lower() for input in [data['username'], data['password']]
        ]
        if all(password[i] == password[i + 1] for i in range(len(password) - 1)) or username == password:
            raise serializers.ValidationError({
                'password': "Invalid password"
            })
        return data

    class Meta:
        fields = ['username', 'password', ]
        model = User


class UserRegistrationSerializer(LoginSerializer):

    password2 = serializers.RegexField(
        r"[0-9A-Za-z]+", min_length=5, max_length=8, write_only=True
    )

    def validate(self, data):
        data = super().validate(data)
        if data['password'] != data['password2']:
            raise serializers.ValidationError({"password2": "Password confirmation failed"})
        return data

    class Meta:
        model = User
        fields = ['username', 'password', 'password2']



Solution

  • Please remove the customizations of validate method in UsernameSerializer and LoginSerializer and place this method inside UserRegistrationSerializer

    from rest_framework.exceptions import ValidationError
    
    def validate(self, data):
        errors = []
        data = super().validate(data)
        username = data.get("username")
        if self.Meta.model.objects.filter(username__iexact=username).exists():
           errors.append({"username":"Choose a different username"})
    
        username, password = [
                input.lower() for input in [data['username'], data['password']]
            ]
        if all(password[i] == password[i + 1] for i in range(len(password) - 1)) or username == password:
            errors.append({
                'password': "Invalid password"
            })
        
        if data['password'] != data['password2']:
            errors.append({"password2": "Password confirmation failed"})
    
        if errors:
            raise serializers.ValidationError(errors)
        return data