1

The code:

class OTP(AppModel):
 phone_regex = RegexValidator(regex=r'^[6789]\d{9}$', message="phone no. is invalid.")
 phone_number = models.CharField(validators=[phone_regex], max_length=10, unique=True)
 code = models.CharField(max_length=255)
 def __str__(self):
 return str(self.phone_number) + ": "+str(self.code)
class OTPSerializer(serializers.ModelSerializer):
 code = serializers.CharField(max_length=None, required=False)
 class Meta:
 model = OTP
 fields = ('id', 'code', 'phone_number')
 read_only_fields=('id', 'code')
 @transaction.atomic
 def create(self, validated_data):
 phone_number = validated_data.pop("phone_number")
 otp, created = OTP.objects.update_or_create(
 phone_number=phone_number, defaults={"code": generate_otp()})
 return otp

I am trying to do update_or_create inside the create method of the django-rest-framework's ModelSerializer.

But, the field phone_number inside the model OTP must be unique. Hence the unique=True.

I was able to post a phone_number and create the object. But, posting the same phone_number again throws error otp with this phone number already exists, instead of updating it if it already exists as I have overridden the create method. Please help!

asked Jan 12, 2019 at 18:49

3 Answers 3

0

You could make phone_number NOT required and then manually do the check. You get the error, because DRF validated phone_number before you do. So, basically, the solution could be the following (serialiser code only):

class OTPSerializer(serializers.ModelSerializer):
 code = serializers.CharField(max_length=None, required=False)
 class Meta:
 model = OTP
 fields = ('id', 'code', 'phone_number')
 read_only_fields=('id', 'code')
 extra_kwargs = {'phone_number': {'required': False}}
 @transaction.atomic
 def create(self, validated_data):
 phone_number = validated_data.pop("phone_number")
 otp, created = OTP.objects.update_or_create(
 phone_number=phone_number, defaults={"code": generate_otp()})
 return otp
answered Jan 12, 2019 at 20:40
Sign up to request clarification or add additional context in comments.

1 Comment

No. the phone_number is required. And the problem is: UniqueValidator validates before reaching create method.
0

Try setting the validator inside your serializer class instead of inside the model class. So have your serializer class look something like this:

class OTPSerializer(serializers.ModelSerializer):
 code = serializers.CharField(max_length=None, required=False)
 phone_regex = RegexValidator(regex=r'^[6789]\d{9}$', message="phone no. is invalid.") # add this line
 phone_number = serializers.CharField(validators=[phone_regex]) # and this line
 
 class Meta:
 model = OTP
 fields = ('id', 'code', 'phone_number')
 read_only_fields=('id', 'code')
 @transaction.atomic
 def create(self, validated_data):
 phone_number = validated_data.pop("phone_number")
 otp, created = OTP.objects.update_or_create(
 phone_number=phone_number, defaults={"code": generate_otp()})
 return otp
answered Sep 7, 2020 at 18:27

Comments

0

You can use Signals to do this in a clean way. Simply, you can send a created variable to a receiver which you define, and deal with it based on either your object was created or not. in the case of the REST response, just override the create method in the Serializer to return data based on the state you're in, or the get/post/patch method you're using in the APIView to not return serializer.data, and instead return whatever you want it to. Here is an example for a signal receiver:

@receiver(post_save, sender=settings.OTP_MODEL)
def update(sender, instance=None, created=False, **kwargs):
 if created:
 # Do Something
 else:
 # Do Some Other thing
answered Sep 18, 2020 at 17:37

1 Comment

Just be careful with Signals as they aren't atomic. Your serializer can commit data and anything the signal does to the database is in its own transaction. UNLESS you decorate the view with @atomic from django.db.transaction

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.