0

Iam using Django with Restframework. Iam trying to get a json output from serializers in a nested manner with key as one of the fields from model. I acheived the nested JSON but the json key for them is troubling me.

Here is my code and expected results out of it:

Models.py

class Tags(models.Model):
    tagId = models.CharField(primary_key=True, max_length=100, default=1)
    section = models.CharField(max_length=100,default=1)
    def __str__(self):
        return self.section

class TagItem(models.Model):
    section= models.ForeignKey(Tags, on_delete=models.CASCADE,default=1,related_name="items")
    select = models.BooleanField(default=False)
    name = models.CharField(max_length=50)
    def __str__(self):
        return self.name

serializers.py

class TagItemModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = TagItem
        fields = '__all__'


class TagModelSerializer(serializers.ModelSerializer):
    items = TagItemModelSerializer(many=True)

    class Meta:
        model = Tags
        fields = ['pk','section', 'items']

Expected output:

 {
    "crafts" : {                //craft is comming from Tag model's "section"
        "id": 1,
        "section": "crafts",
        "items": [
          {
            "id": "10",
            "select": false,
            "name": "Wood",   
            "category": "crafts"   
          },

        ]
      },
    "states" : {
        "id": 2,
        "section": "states",
        "items": [
          {
            "id": "20",
            "select": false,
            "name": "Andhra Pradesh",
            "category": "states" 
          }
        ]
      },
      "others" : {
      "id": 3,
      "section": "others",
      "items": [
        {
          "id": "30",
          "select": false,
          "name": "Volunteer",
          "category": "others" 
        }
      ]
    }
}

Current output:

[                                           //cant get the key of Tag model's "section"
    {
        "pk": "1",
        "section": "states",
        "items": [
            {
                "id": 6,
                "select": false,
                "name": "Assam",
                "section": "1"
            }
        ]
    },
    {
        "pk": "2",
        "section": "others",
        "items": [
            {
                "id": 12,
                "select": false,
                "name": "Artisan",
                "section": "2"
            }
        ]
    },
    {
        "pk": "3",
        "section": "crafts",
        "items": [
            {
                "id": 9,
                "select": false,
                "name": "Metal",
                "section": "3"
            }
        ]
    }
]

1 Answer 1

1

At your case, you would like to have a custom representation I think?

You can adjust it with overriding to_representation()

I provide two approaches:

serializer -> the better way but not quite the output you prefer

view

class TagModelView(views.APIView):

    def get(self, request):
        qs = Tags.objects.all()
        serializer = TagModelSerializer(qs, many=True)
        return Response(data=serializer.data)

serializer

class TagItemModelSerializer(serializers.ModelSerializer):
    category = serializers.SerializerMethodField()

    class Meta:
        model = TagItem
        fields = ['id', 'select', 'name', 'category']

    def get_category(self, instance):
        return instance.section.section


class TagModelSerializer(serializers.ModelSerializer):
    items = TagItemModelSerializer(many=True)

    class Meta:
        model = Tags
        fields = ['pk', 'section', 'items']

    def to_representation(self, instance):
        container = super().to_representation(instance)
        return {container.get('section'): container}

Making representation in your view -> output you would like to get

view

    def get(self, request):
        qs = Tags.objects.all()
        serializer = TagModelSerializer(qs, many=True)

        container = dict()
        for element in serializer.data:
            container.update(**element)

        return Response(data=container)

serializer

No change to my proposal obove.

update

I saw that your primary key of Tags is tagId and you are using fields = ['pk', 'section', 'items'] but you would like to have id as a key-name.

Relabeling can help you.

class TagModelSerializer(serializers.ModelSerializer):
    items = TagItemModelSerializer(many=True)
    id = serializers.CharField(source='tagId')

    class Meta:
        model = Tags
        fields = ['id', 'section', 'items']
Sign up to request clarification or add additional context in comments.

4 Comments

Hi @Klim, Sorry for the late reply. I tried the above solution, and it worked quite well! Thanks a lot!
But on a curious case, how does this return instance.section.section worked? I mean section comes twice, what kind of sorcery was that? And I cant find this get_category, is this part of DRF?
@Ankzious. 1. instance is one of many TagItem-objects because we wrote TagModelSerializer(qs, many=True) and qs is a QuerySet and the serializer iterates over it. get_<custom_name>(...) is a method that can do anything what you would like to display. At instance.section.section, instance is your current TagItem-object. .section is the fieldname of your ForeignKey in TagItem-model and the second .section is the fieldname of your CharField in Tags-model
@Ankzious If you use get_<display_name>, DRF takes <display_name> as a key and uses it for displaying what your method is returning. It is a ´read_only´-field and cannot be used for saving/updating.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.