Search code examples
pythondjangodjango-rest-frameworkdjango-serializermanytomanyfield

Don't use field name in DRF serialization (there is only one field)


I want the serialization of a nested model not to include the field name for every instance, since there is only one field in the serialization.

I have the following models in models.py:

class Language(models.Model):
    name = models.CharField(max_length=32)
    code = models.CharField(max_length=32, unique=True)


class Person(models.Model):
    name = models.CharField(max_lenght=128)
    languages = models.ManyToManyField(Language, blank=True) # the languages this person speaks

languages is a ManyToManyField since one person can speak many languages and one language can be spoken by many people.

I have for them the following serializers in serializers.py:

class LanguageSerializer(serializers.ModelSerializer):
    class Meta:
        model = Language
        fields = ['code']


class PersonSerializer(serializers.ModelSerializer):
    languages = LanguageSerializer(many=True, required=False)


    class Meta:
        model = Person
        fields = ['name', 'languages']

Currently the serialization into a JSON looks like something like this:

{"name": "Elizabeth II", "languages": [{"code":"en"}, {"code":"fr"}]}

but I would like it to look like this:

{"name": "Elizabeth II", "languages": ["en", "fr"]}

And this will should not cause a problem, because there is only one field in the serialization of language and there will never be another field, so the "code" field name is redundant.

How can this be achieved?

Update: How can this be made to also work when creating a new person from a JSON over DRF?

I have the following view in views.py:

class CreatePersonView(generics.CreateAPIView):
    queryset = Person.objects.all()
    serializer_class = PersonSerializer

Solution

  • This is how I achieved the wanted behaviour:

    serializers.py:

    class LanguageRelatedField(serializers.StringRelatedField):
        def get_queryset(self):
            return Language.objects.all()
    
        def to_representation(self, instance):
            return instance.code
    
        def to_internal_value(self, data):
            try:
                return Language.objects.get(code=data)
            except Language.DoesNotExist:
                raise serializers.ValidationError('Language {} could not be recognized'.format(data))
    
    
    class PersonSerializer(serializers.ModelSerializer):
        languages = LanguageRelatedField(many=True, required=False)
    
        class Meta:
            model = Person
            fields = ['name', 'languages']