Goutam B Seervi
Goutam B Seervi

Reputation: 1226

DRF: custom update method not being executed in ModelSerializer class

I am writing a REST API using Django Rest Framework. I'm encoutering a wierd problem here. I have an app called user.

Models.py:

@python_2_unicode_compatible
class User(AbstractUser):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    profile_picture = models.ForeignKey(File, on_delete=models.DO_NOTHING, default=None, null=True, blank=True)
    email = models.EmailField('Email address', unique=True)
    name = models.CharField('Name', default='', max_length=255)
    phone_no = models.CharField('Phone Number', max_length=255, unique=True)
    company_name = models.CharField('Company Name', default='', max_length=255)
    verification_token = models.CharField('Verification Token', default='', max_length=255)
    address = models.CharField('Address', default='', max_length=255)
    address_coordinates = models.CharField('Address Coordinates', default='', max_length=255)
    country = models.CharField('Country', default='', max_length=255)
    pincode = models.CharField('Pincode', default='', max_length=255)
    pan_number = models.CharField('Pan Number', default='', max_length=255, blank=True)
    gst_number = models.CharField('GST Number', default='', max_length=255, blank=True)
    is_advertisor = models.BooleanField(default=False)
    is_signage_owner = models.BooleanField(default=False)
    phone_verified = models.BooleanField(default=False)
    email_verified = models.BooleanField(default=False)
    updated_at = models.DateTimeField(auto_now_add=True)
    from_time = models.TimeField(blank=True, default='00:00')
    to_time = models.TimeField(blank=True, default='00:00')
    category = models.CharField('Category', default='', max_length=255, blank=True)
    reset_token = models.CharField(max_length=255, default='')
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['username']

    def __str__(self):
        return self.email

Views.py:

class UserViewSet(mixins.RetrieveModelMixin,
                  mixins.ListModelMixin,
                  mixins.UpdateModelMixin,
                  mixins.CreateModelMixin,
                  viewsets.GenericViewSet):
    queryset = User.objects.all()
    permission_classes = (AllowAny,)
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['id', 'email', 'phone_no']

    def get_serializer_class(self):
        if self.action == 'create':
            return CreateUserSerializer
        elif self.action == 'update':
            return UpdateUserSerializer
        else:
            return UserSerializer

Serializers.py:

class CreateUserSerializer(serializers.ModelSerializer):
    def create(self, validated_data):
        token = account_activation_token.make_token(validated_data['email'])
        validated_data['verification_token'] = token
        user = User.objects.create_user(**validated_data)
        subject = 'Account Verification'
        message = 'Your Verification token is http://167.71.234.51/verify?token=%s' % token
        send_mail_from = '[email protected]'
        recipients = [user.email]
        send_mail(subject, message, send_mail_from, recipients)
        response = send_otp(validated_data['phone_no'])
        if "success" not in response:
            return __badrequest__("Error while generating OTP")
        return user

    class Meta:
        unique_together = ('email',)
        model = User
        fields = (
            'id', 'password', 'email', 'username', 'phone_no'
        )
        extra_kwargs = {'password': {'write_only': True}}


class UpdateUserSerializer(serializers.ModelSerializer):
    def update(self, instance, validated_data):
        instance.email_verified = validated_data.get('email_verified', instance.email_verified)
        instance.phone_verified = validated_data.get('phone_verified', instance.phone_verified)
        instance.name = validated_data.get('name', instance.name)
        instance.email = validated_data.get('email', instance.email)
        instance.phone_no = validated_data.get('phone_no', instance.phone_no)
        instance.company_name = validated_data.get('company_name', instance.company_name)
        instance.address = validated_data.get('address', instance.address)
        instance.country = validated_data.get('country', instance.country)
        instance.pincode = validated_data.get('pincode', instance.pincode)
        instance.pan_number = validated_data.get('pan_number', instance.pan_number)
        instance.gst_number = validated_data.get('gst_number', instance.gst_number)
        instance.is_advertisor = validated_data.get('is_advertisor', instance.is_advertisor)
        instance.is_signage_owner = validated_data.get('is_signage_owner', instance.is_signage_owner)
        instance.from_time = validated_data.get('from_time', instance.from_time)
        instance.to_time = validated_data.get('to_time', instance.to_time)
        instance.category = validated_data.get('category', instance.category)
        instance.profile_picture = validated_data.get('profile_picture', instance.profile_picture)
        instance.reset_token = validated_data.get('reset_token', instance.reset_token)
        instance.save()
        return instance

    class Meta:
        model = User
        fields = (
            'id', 'name', 'email_verified', 'phone_verified', 'email', 'phone_no', 'auth_token',
            'company_name', 'address', 'country', 'pincode', 'pan_number', 'gst_number',
            'is_advertisor', 'is_signage_owner', 'from_time', 'to_time', 'category', 'profile_picture',
            'reset_token'
        )
        read_only_fields = ('auth_token',)


class UserSerializer(serializers.ModelSerializer):
    profile_picture = FileSerializer()

    class Meta:
        unique_together = ('email',)
        model = User
        fields = (
            'id', 'password', 'email', 'name', 'auth_token', 'email_verified', 'phone_verified',
            'phone_no', 'company_name', 'address', 'country', 'pincode', 'pan_number', 'gst_number',
            'is_advertisor', 'is_signage_owner', 'from_time', 'to_time', 'category', 'profile_picture',
            'reset_token'
        )
        read_only_fields = ('auth_token',)

Here is the interesting part. When I make a PUT request at the route which points to UserViewSet, the UpdateUserSerializer is returned in the get_serializer_class method but the case happens to be that the update method is not invoked which is in the UpdateUserSerializer class.

Here's the response I get:

{"email":["This field is required."],"phone_no":["This field is required."]}

Upvotes: 1

Views: 717

Answers (1)

Aashay Amballi
Aashay Amballi

Reputation: 1431

This is a validation error for the API. By default, blank will be false for every model field. I could see in your user model blank is not set to True for email and phone_no fields. So what happens here is that when you send a request without email and phone_no, you'll get such validation errors. A quick solution is to add blank=True to both email and phone_no fields in your user model.

Change the below code in your user model and migrate it.

email = models.EmailField('Email  address', unique=True, blank=True)
phone_no = models.CharField('Phone Number', max_length=255, unique=True, blank=True)

and

In your user serializer add this code

email = serializers.EmailField(required=False)
phone_no = serializers.CharField(required=False)

Here is a link for Django documentation for the blank field. https://docs.djangoproject.com/en/2.2/ref/models/fields/#blank

Upvotes: 2

Related Questions