Alvaro Bataller
Alvaro Bataller

Reputation: 483

Django Rest Framework: Modify Serializer to return dictionary using a field as the key instead of an array of objects

Currently I have an API View that returns a dictionary that looks like this:

[
    {
        "id": 1,
        "name": "Fly in the sky",
        "thumbnail": "games/fly_in_the_sky",
        "maps": [
            {
                "id": 1,
                "name": "Blue Sea",
                "thumbnail": "maps/blue_sea.jpg"
            },
            {
                "id": 2,
                "name": "Red Mountain",
                "thumbnail": "maps/red_mountain.jpg"
            }
        ],
        "characters": [
            {
                "id": 1,
                "name": "Steve",
                "thumbnail": "characters/steve.jpg"
            },
            {
                "id": 2,
                "name": "Peter",
                "thumbnail": "characters/peter.jpg"
            }
        ]
    }
]

I would like it to look like this instead, moving the names of all items as keys. (I would make sure ahead of time that all the names are unique so there is no key conflict)

{
    "Fly in the sky": {
        "id": 1,
        "thumbnail": "games/fly_in_the_sky",
        "maps": {
            "Blue Sea": {
                "id": 1,
                "thumbnail": "maps/blue_sea.jpg"
                },
            "Red Mountain": {
                "id": 2,
                "thumbnail": "maps/red_mountain.jpg"
            }
        },
        "characters": {
            "Steve": {
                "id": 1,
                "thumbnail": "characters/steve.jpg"
            },
            "Peter",{
                "id": 2,
                "thumbnail": "characters/peter.jpg"
            }
        }
    }
}

Is there any way of telling Django to return elements as a dictionary using a field as keys instead of an array?

My views and serializer currently look like this:

class GamesView(APIView):
    def get(self, request):
        game_objects = Game.objects.all()
        game_serializer = GameListSerializer(game_objects, many=True)
        return Response(game_serializer.data)


class MapListSerializer(ModelSerializer):
    class Meta:
        model = Map
        fields = ('id', 'name', 'thumbnail')


class CharacterListSerializer(ModelSerializer):
    class Meta:
        model = Character
        fields = ('id', 'name', 'thumbnail')


class GameListSerializer(ModelSerializer):
    maps = MapListSerializer(many=True)
    characters = CharacterListSerializer(many=True)

    class Meta:
        model = Game
        fields = ('id', 'name', 'thumbnail', 'maps', 'characters')

Upvotes: 0

Views: 4233

Answers (2)

jeevu94
jeevu94

Reputation: 718

Maybe this should help. But i'm not sure why you would do that. xD

class GamesView(APIView):
    def get(self, request):
        game_objects = Game.objects.all()
        game_serializer = GameListSerializer(game_objects, many=True)
        modified_response = {}
        if game_serializer.data:
            for game in game_serializer:
                name = game.pop("name")
                modified_response[name] = game
        return Response(modified_response)

Upvotes: 0

Ogulcan Olguner
Ogulcan Olguner

Reputation: 581

When you give the parameter many=True to serializer, it returns a list and you cannot change it. This is better because more than one object can return. I wouldn't suggest it, but if you want to return the dictionary when there is only one element, you can write a condition like below. The reason I don't recommend it that it can be difficult to parse JSON for the client.

class GamesView(APIView):
    def get(self, request):
        game_objects = Game.objects.all()
        game_serializer = GameListSerializer(game_objects, many=True)
        serializer_data = game_serializer.data
        if len(serializer_data) == 1:
            serializer_data = serializer_data[0]
        return Response(serializer_data)

Upvotes: 1

Related Questions