Milano
Milano

Reputation: 18735

Django Rest Framework: How to modify output structure?

Is there a way to group fields in Serializer/ModelSerializer or to modify JSON structure?

There is a Location model:

class Location(Model):
    name_en = ...
    name_fr = ...
    ...

If I use ModelSerializer I get plain representation of the object fields like:

{'name_en':'England','name_fr':'Angleterre'}

I want to group some fields under "names" key so I get

{'names':{'name_en':'England','name_fr':'Angleterre'}}

I know I can create custom fields but I want to know if there is a more straightforward way. I tried

Meta.fields = {'names':['name_en','name_fr']...}

which doesn't work

Upvotes: 1

Views: 971

Answers (3)

edilio
edilio

Reputation: 1868

You could also not use a property on the model and but use a SerializerMethodField on your serializer like in this implementation. We used here a _meta.fields, like in the other implementation, to get all the fields that starts with name_ so we can dynamically get the output you desired

class LocationSerializer(serializers.ModelSerializer):
    names = serializers.SerializerMethodField()

    def get_names(self, obj):
        lst = {field.name: getattr(obj, field.name)
               for field in obj.__class__._meta.fields
               if field.name.startswith('name_')}
        return lst

    class Meta:
        model = Location
        fields = ('id', 'names')

Upvotes: 0

edilio
edilio

Reputation: 1868

I think it is better using a property. Here is the whole example.

class Location(models.Model):
    name_en = models.CharField(max_length=50)
    name_fr = models.CharField(max_length=50)

    @property
    def names(self):
        lst = {field.name: getattr(self, field.name)
              for field in self.__class__._meta.fields
              if field.name.startswith('name_')}
        return lst

In views:

class LocationViewSet(viewsets.ModelViewSet):
    model = models.Location
    serializer_class = serializers.LocationSerializer
    queryset = models.Location.objects.all()

And in serializers:

class LocationSerializer(serializers.ModelSerializer):
    class Meta:
        model = Location
        fields = ('id', 'names')

My result for my fake data:

[{
  "id": 1,
  "names": {
      "name_en": "England",
      "name_fr": "Angleterre"}
}]

Upvotes: 4

Haidar Zeineddine
Haidar Zeineddine

Reputation: 998

Try to create a wrapper serialzer and place the LocationSerializer inside it

class LocationSerialzer(serializers.ModelSerialzer):
   name_en = ...
   name_fr = ...
   ...


class MySerializer(serializers.ModelSerializer):
   name = LocationSerialzer()
   ...

Using the above method , you can apply your own customization without being limited to drf custom fields.

Upvotes: 1

Related Questions