Khan Asfi Reza
Khan Asfi Reza

Reputation: 691

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

Upvotes: 0

Views: 314

Answers (4)

Khan Asfi Reza
Khan Asfi Reza

Reputation: 691

I found out the problem and solved it in this matter Serializer and Models are same but I have changed the view.


class PropertyView(viewsets.ModelViewSet):
    # Adding Authentication classes Token Authentication
    # Permission class > Will allow only post request if user is not authenticated
    authentication_classes = [TokenAuthentication]
    # Permissions only allow renters and authenticated users
    permission_classes = [IsAuthenticated, RenterPermission]
    # Gives JSON Rendered Class
    renderer_classes = [JSONRenderer]
    # Serializer Class for this Class View
    serializer_class = PropertySerializer
    # Query set of all renter objects
    queryset = Renter_Property_Pref.objects.all()

   
    @action(methods=['get'], detail=True)
    def retrieve(self, request, pk=None):
        
        renter = get_object_or_404(self.queryset, user=request.user)
        serializer = self.get_serializer(renter, many=False)
        return Response(serializer.data, status=status.HTTP_200_OK)

    @action(methods=['post'], detail=True)
    def create(self, request):
        # giving serializer the data from request data
        serializer = self.get_serializer(data=request.data)
        # checks if serializer is valid
        if serializer.is_valid():
            # saves user information and saves it
            serializer.save(user=self.request.user)
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        # Otherwise it will return errors and a bad request
        return Response(serializer.error_messages, status=status.HTTP_400_BAD_REQUEST)

    
    @action(methods=['put'], detail=True)
    def update(self, request):
        # Propery Serializer and instance object
        instance = Property.objects.get(owner=request.user)
        serializer = self.get_serializer(instance=instance, data=request.data)
        # If serializer is valid it will save
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        # Otherwise it will show error
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)**

Upvotes: 0

Ken4scholars
Ken4scholars

Reputation: 6296

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

Upvotes: 1

Jun Zhou
Jun Zhou

Reputation: 1117

In your PUT method(put(self, request)), you are using request.user as the PropertySerializer's object rather than a property object. You can check the serializer documentation for saving an object.

You can following this wonderful DRF tutorial to set up your APIViews and urls.py for get/put/post methods.

Then you can create, update and get an object without issues.

Upvotes: 0

Michael Hawkins
Michael Hawkins

Reputation: 2873

In your PropertySerializer you need to either add a PrimaryKeyRelatedField or LocationSerializer to the "location" attribute:

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

or:

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

I believe there is also a way to do it in the RenterCreate view, but let me know if either of those work first.

Upvotes: 1

Related Questions