Thinker
Thinker

Reputation: 5366

Django rest Bad Request

I have extended user model to store user profile related information:

class Profile(models.Model):
    user = models.OneToOneField(User, blank=True, on_delete=models.CASCADE, )
    name = models.CharField(max_length=50, blank=True, null = True, )
    age = models.IntegerField(blank=True, null = True, )
    EDUCATION_CHOICES = (
    ('VOC', 'Vocational'),  
    ('DPL', 'Diploma'),
    ('BCL', 'Bachelor'),
    ('MST', 'Master'),
    ('DOC', 'Doctor'),
    )
    education = models.CharField(blank=True, null = True, choices=EDUCATION_CHOICES, max_length=3, )
    short_story = models.TextField(blank=True, null = True, max_length=500)
    ROLE_CHOICES = (
    ('INT', 'Intern'),  
    ('WOR', 'Worker'),
    ('SUP', 'Supervisor'),  
    ('MAN', 'Manager'),
    )
    role = models.CharField(blank=True, null = True, choices=ROLE_CHOICES, max_length=3, )
    hobbies = models.TextField(blank=True, null = True, max_length=500)
    rational_thinker = models.NullBooleanField(blank=True, null = True, default=False)
    welcome_new_changes = models.NullBooleanField(blank=True, null = True, default=False)
    embrace_science = models.NullBooleanField(blank=True, null = True, default=False)
    if_social = models.NullBooleanField(blank=True, null = True, default=False)
    seek_latest_info = models.NullBooleanField(blank=True, null = True, default=False)
    exciting_about_job = models.TextField(blank=True, null = True, max_length=500)
    not_exciting_about_job = models.TextField(blank=True, null = True, max_length=500)
    aspiration = models.TextField(blank=True, null = True, max_length=500)

    @receiver(post_save, sender=User)
    def create_user_profile(sender, instance, created, **kwargs):
        if created:
            Profile.objects.create(user=instance)

    @receiver(post_save, sender=User)
    def save_user_profile(sender, instance, **kwargs):
        instance.profile.save()

Its serializer:

class ProfileSerializer(serializers.ModelSerializer):
    class Meta:
        model = Profile
        fields = '__all__'

And view:

@api_view(['GET', 'PUT', 'DELETE'])
def profile(request):
    """
    Get, udpate, or delete a specific profile
    """
    try:
        profile = Profile.objects.get(user=request.user.id)
        print(profile)
        print(request.data)
    except Profile.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)

    if request.method == 'GET':
        serializer = ProfileSerializer(profile)
        return Response(serializer.data)

    elif request.method == 'PUT':
        print('PUT called')
        serializer = ProfileSerializer(profile, data=request.data)
        if serializer.is_valid():
            print('Valid')
            serializer.save(user=request.user)
            return Response(serializer.data)
        else:
            print('Else')
            return Response(
                serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    elif request.method == 'DELETE':
        profile.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

Profile is created automatically when the user is created.

While updating a Profile when I send partially filled data i.e. some fields are sent blank, I get Bad Request error. User should be able to fill only fields he wishes to and must not all. If I make a PUT request with all the data, then it is successful.

Why is it happening?

Upvotes: 1

Views: 2763

Answers (2)

Aneesh R S
Aneesh R S

Reputation: 3827

Usually PUT method used to update all fields. PATCH method is used for partial update. But you can override it by adding partial=True in the serializer. If you still want to use PUT for partial update, then set serializer as

serializer = ProfileSerializer(profile, data=request.data, partial=True)

Applying this on your view will looks like:

@api_view(['GET', 'PUT', 'DELETE'])
def profile(request):
    """
    Get, udpate, or delete a specific profile
    """
    try:
        profile = Profile.objects.get(user=request.user.id)
    except Profile.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)

    if request.method == 'GET':
        serializer = ProfileSerializer(profile)
        return Response(serializer.data)

    elif request.method == 'PUT':
        serializer = ProfileSerializer(profile, data=request.data, partial=True)
        # setting raise_exception=True in the serializer's is_valid method will raise exception on error. So you don't have implement extra logics
        serializer.is_valid(raise_exception=True)
        serializer.save(user=request.user)
        return Response(serializer.data)

    elif request.method == 'DELETE':
        profile.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

Upvotes: 2

Ganesh Negi
Ganesh Negi

Reputation: 2029

PUT method needs all the fields. If you want to pass only few fields, use PATCH method.

Upvotes: 0

Related Questions