1

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

3 Answers 3

4

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"}
}]
Sign up to request clarification or add additional context in comments.

Comments

1

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.

Comments

0

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

Comments

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.