Search code examples
djangodjango-rest-frameworkdjango-serializer

How do I set custom error response upon serializer validation in a Django project?


I am working on a Django project. I have defined custom User model using AbstractBaseModel class. Below is my model representation for clarity.

# models.py

class User(AbstractBaseUser):
    id = models.AutoField(primary_key=True)
    firstname = models.CharField(max_length=50)
    lastname = models.CharField(max_length=50)
    email = models.EmailField(unique=True)
    updated_at = models.DateTimeField(auto_now=True)
    created_at = models.DateTimeField(auto_now_add=True)
    is_staff = models.BooleanField(default=True)
    is_admin = models.BooleanField(default=False)

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['firstname', 'lastname']

    objects = UserManager()

    def save(self, *args, **kwargs):
        if not self.firstname or not self.lastname or not self.email or not self.password:
            raise ValueError('All required fields must be provided')
        super().save(*args, **kwargs)

    def __str__(self):
        return str(self.id) + '. ' + self.firstname + ' ' + self.lastname + ' (' + self.email + ')'

    def has_perm(self, perm, obj=None):
        return self.is_admin

    def has_module_perms(self, app_label):
        return True
    
    class Meta:
        db_table = 'users'
        verbose_name = 'User'
        verbose_name_plural = 'Users'

Below is my serializer class that serves purpose for User object validation upon its creation.

# serializers.py

from rest_framework import serializers
from .models import *
from django.contrib.auth.hashers import make_password, check_password

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['firstname', 'lastname', 'email', 'password']

    def create(self, validated_data):
        user = User.objects.create_user(
            email = validated_data['email'],
            firstname = validated_data['firstname'],
            lastname = validated_data['lastname'],
            password = make_password(validated_data['password'])
        )
        return user

Also, refer to my view that creates a user object for clarity.

# views.py

from .models import *
from .serializers import *
from rest_framework.response import Response
from rest_framework.views import APIView

class UserSignup(APIView):
    def post(self, request):
        serializer = UserSerializer(data=request.data)
        if not serializer.is_valid():
            return Response(serializer.errors, status=400)
        serializer.save()
        return Response(serializer.data, status=201)

Here, as you may noted, my email field is unique and upon creating a user with duplicate email, my serializer gives below validated response.

{
    "email": [
        "User with this email already exists."
    ]
}

My problem statement is that I wish to have a custom response from serializer's validation. There's a way to add a validate method in the serializer class to add my custom validation, but here the problem would be to add validation for all my fields if I were to just add custom validation for email field.

What's the right way to only change custom output for serializer error message that triggers for duplicate email field?

For e.g., I may want below output for duplicate email insertion.

{
    "email": [
        "Email already exists."
    ]
}

How do I achieve above?


Solution

  •     email = serializers.EmailField(
        validators=[UniqueValidator(queryset=User.objects.all(), message={"email" : ["custom msg"]})]
    )
    

    i think you maybe looking for something like this UniqueValidator and UniqueTogetherValidator takes a query to check and second argument would be your missing piece