Search code examples
pythondjangodjango-rest-frameworkdjango-serializerserialization

Type error while Put Request Django Rest Framework


I am building a Django application where there is a Model called Location and another model called Property. Whenever I try to give put request it shows me type error TypeError: Object of type Location is not JSON serializable

My Location Model

class Location(models.Model):
     lat = models.DecimalField(max_digits=10,decimal_places=8)
     long = models.DecimalField(max_digits=10,decimal_places=8)
     address = models.CharField(max_length=256)
class Property(models.Model):
     owner = models.OneToOneField(to=User,on_delete=models.CASCADE)
     name = models.CharField(max_length=256)
     bedrooms = models.SmallPositiveIntigerField()
     bathrooms = models.SmallPositiveIntigerField()
     living_rooms = models.SmallPositiveIntigerField()
     location = models.ForeignKey(to=Location,null=True,on_delete=models.SETNULL)

Serializer

class PropertySerializer(serializers.ModelSerializer):
     class Meta:
          model = Property
          fields = ['id','name','bedrooms','bathrooms','living_rooms','location']
          read_only_fields = ['id']

class LocationSerializer(serializers.ModelSerializer):
     class Meta:
          model = Location
          fields = ['id','long','lat','address']
          read_only_fields = ['id']

views

class RenterCreate(APIView):
    
    authentication_classes = [TokenAuthentication]
    permission_classes = [IsAuthenticated, RenterPermission]
    renderer_classes = [JSONRenderer]

    
    def get(self, request):
        
        property = Property.objects.all()
        serializer = RenterSerializer(property , many=True)
        return Response(serializer.data, status=status.HTTP_200_OK)

    
    def post(self, request):
        serializer = PropertySerializer(data=request.data)
        if serializer.is_valid():
            serializer.save(user=request.user)
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.error_messages, status=status.HTTP_400_BAD_REQUEST)

    def put(self, request):
        serializer = PropertySerializer(request.user, data=request.data)
        if serializer.is_valid():
            serializer.save(user=request.user)
            return Response(serializer.data)
        
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

After saving a Property when I use PUT request to update the information it shows me TypeError: Object of type Location is not JSON serializable I have saved a couple of locations in the database. First I made a post request with it, it gives me STATUS 201 CREATED

{
'name': 'foo',
'bedrooms':4,
'bathrooms':3,
'living_rooms':5,
'location':4,
}

Then I do a put request on it

{
'name': 'foo bar',
'bedrooms':1,
'bathrooms':2,
'living_rooms':2,
'location':1,
}

It gives me TypeError: Object of type Location is not JSON serializable


Solution

  • There are quite a few issues with your code. The GET and POST requests are correctly working on the list endpoint but the PUT request should work on the detail endpoint. This means that the endpoint should enable you to specify the particular Property object you want to edit, but currently, it is not. Coupled with this, instead of passing the Property instance you want to edit to the serializer, you're passing the request.user.

    Usually, if you are using the low-level APIView, then you need to make separate views for the list and detail endpoints. If you want to use one view, then you should exploit the higher level viewsets which combine list and detail views. And they are pretty easy to implement as most of the boilerplate code is already implemented for you. For your case, a simple viewset like this can suffice.

    from rest_framework import viewsets
    
    class PropertyViewSet(viewsets.ModelViewSet):
        authentication_classes = [TokenAuthentication]
        permission_classes = [IsAuthenticated, RenterPermission]
        renderer_classes = [JSONRenderer]
        serilaizer_class = PropertySerializer
        queryset = Property.objects.all()
    

    Then register it in the urls.py like this:

    from rest_framework.routers import SimpleRouter
    
    router = SimpleRouter()
    router.register(r'properties', PropertyViewSet, basename='properties')
    urlpatterns = router.get_urls()
    

    It automatically already has the get, post, put and delete implemented and embodies two endpoints: api/properties called the list endpoint and api/properties/<id>/ - called the detail endpoint.

    So to edit the property of say, the property with id 2, you should send the PUT request to api/properties/2/.

    You can also read more about DRF viewsets