Search code examples
pythondjangopostdjango-rest-frameworkdjango-serializer

POST with an ID and GET the nested object does not work in django at the same time in ModelSerializer


I have spent already a couple of days researching about this issue in similar questions and I am not able to get a solution. This should be something simple, I have a model:

model.py

class Item(models.Model):
    """Class to represent an item..."""

    label = models.TextField(null=True)
    name = models.TextField()

    category = models.ForeignKey(   "Category", on_delete=models.SET_NULL,
                                    null=True, default=DEFAULT_CATEGORY_ID)

class Category(models.Model):
    """Class to represent the category of an Item. Like plants, bikes..."""
    name = models.TextField()

    description = models.TextField(null=True)

view.py

class ItemViewset(viewsets.ModelViewSet): # pylint: disable=too-many-ancestors
    """API Endpoint to return the list of items"""
    queryset = Item.objects.all()
    serializer_class = ItemSerializer

serializer.py

class ItemSerializer(serializers.ModelSerializer):
    """Serializer for Item."""

    category = CategorySerializer(read_only=True)

    class Meta: # pylint: disable=too-few-public-methods
        """Class to represent metadata of the object."""
        model = Item
        fields = [  'id', 'label', 'name', 'category']   
        read_only_fields = ['id']
        
        # def to_representation(self, instance):
        #     ret = super().to_representation(instance)
        #     ret['category'] = CategorySerializer(instance.category).data
        #     return ret
        def create(self, request):
            # Look up objects by arbitrary attributes.
            # You can check here if your students are participating
            # the classes and have taken the subjects they sign up for.
            category = get_object_or_404(Category(), id=request.data.get('category'))

            serializer = self.get_serializer(data=request.data)
            serializer.is_valid(raise_exception=True)
            serializer.save(category=category)
            headers = self.get_success_headers(serializer.data)

            return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

If I have the line category = CategorySerializer(read_only=True) commented the nested get won't work, for example for a curl to an object. I will get just de id of the structure. If I uncomment it like it is in the example I get the right response:

{
  "id": 60,
  "label": null,
  "name": "Delete me",
  "category": {
    "id": 1,
    "name": "IT"
  }
}

But then the post with id will not work, the category will be set to null.

How can I get both working at the same time?

Update: This is the post I want to do:

{
    "label": "00000003",
    "name": "Delete me",
    "category": 1,
}

Solution

  • You can override the to_represention method of your serializer

    class ItemSerializer(serializers.ModelSerializer):
        """Serializer for Item."""
    
        class Meta: # pylint: disable=too-few-public-methods
            """Class to represent metadata of the object."""
            model = Item
            fields = [  'id', 'label', 'name', 'category']   
            read_only_fields = ['id']
            
        def to_representation(self, instance):
                data = super().to_representation(instance)
                data["category"] = CategorySerializer(instance.category).data
                return data