简体   繁体   中英

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

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"}
}]

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.

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')

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM