Reputation: 2342
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
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
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
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