Nalin Dobhal
Nalin Dobhal

Reputation: 2342

Serializer field validation and how to check if value exist?

I have 2 models, User and UserProfile. UserProfile model has OneToOneField with User model. Here I am trying to update both the models in a single request.

Request Payload:

{'email': ['[email protected]'], 'first_name': ['Nalin'], 'last_name': ['Dobhal'],}

I have created serializer for both models.

serializers.py

class UserAccountSerializer(serializers.ModelSerializer):

    id = serializers.IntegerField(required=False, read_only=True)
    mobile = serializers.IntegerField(read_only=True)
    email = serializers.EmailField(required=False, read_only=False)
    username = serializers.CharField(read_only=True)

    class Meta:
        model = User
        fields = ("id", "mobile", 'email', "username",)

class UserProfileSerializer(serializers.ModelSerializer):

    user = UserAccountSerializer(required=False, read_only=False)
    # other fields
    class Meta:
        model = UserProfile
        fields = ("user", # other fields)

    def update(self, instance, validated_data):
        # validated data doesn't have email here, that's why getting value from self.initial_data
        if self.initial_data.get("email"):

            instance.user.email = self.initial_data.get("email")
            instance.user.save()

        instance.save()
        return instance

views.py

class UserAccountSettingsAPI(generics.RetrieveUpdateAPIView):
    http_method_names = ["options", "get", "put", "patch"]
    permission_classes = (IsAuthenticated,)
    authentication_classes = (TokenAuthentication,)
    serializer_class = UserProfileSerializer

    def get(self, request, *args, **kwargs):
    # some processing

    def update(self, request, *args, **kwargs):
        profile = UserProfile.objects.select_related("user").get(user_id=request.user.id)
        serializer = self.get_serializer(profile, data=request.data)
        if serializer.is_valid(raise_exception=False):
            serializer.save()
        # some other processing only to set key value for context.
        return Response(context)

I would like to perform some validation before updating user's email. So my question is where to perform that validation? And is there any better way of doing this? I tried to add def validate_email(self, email): in UserAccountSerializer but it is not getting executed. So I want to make sure that email does not belong to another user and if email exists, I would like to send some custom error message to client. I have removed unnecessary code.

Upvotes: 3

Views: 7686

Answers (3)

Marios Yiannakou
Marios Yiannakou

Reputation: 21

You could try raising a ValidationError which is part of the serializers library:

from rest_framework import serializers

def update(self, instance, validated_data):
  # If `self.request.data["email"]` works
  if User.objects.filter(email=self.request.data["email"]).exists():
    raise serializers.ValidationError("This email already exists.")

  # If `self.request.data["email"]` doesn't work
  new_email = self.initial_data.get("email"):
  if new_email and User.objects.filter(email=new_email).exists():
    raise serializers.ValidationError("This email already exists.")

  # Save and return the modified instanced

or if you unique=True to the email field, it will raise an IntegrityError so you don't need to do any further checks. You simply catch the error and handle it yourself.

Upvotes: 2

engin_ipek
engin_ipek

Reputation: 917

I assume you want email to be unique. Then you should add unique=True to your user model's email field.

class YourUserModel(AbstractUser):
    email = models.EmailField(unique=True)

After you make email a unique field, your database will not allow to add another entry with the same email and it will raise IntegrityError. You can catch this error and return a better error message to your user. Like this:

try:
    if serializer.is_valid(raise_exception=False):
        serializer.save()
except IntegrityError:
    return Response(data={'message':'that email is in use'}, status=HTTP_400_BAD_REQUEST)

Upvotes: 1

Gorkhali Khadka
Gorkhali Khadka

Reputation: 835

Try adding this code

create or update in views

if User.objects.filter(email=self.request.data['email']).exists():
    return Response({"error": "This email id already exists."})

Upvotes: 2

Related Questions