Serialize model fields into nested object/dict

2019-05-11 19:45发布

问题:

Imagine the following model:

class Person(models.Model):
    name = models.CharField()
    address_streetname = models.CharField()
    address_housenumber = models.CharField()
    address_zip = models.CharField()

I have a django rest framework ModelSerializer that exposes all the fields. But I would like to be able to serialize the address fields into a dict. So when serialized to json output would be:

{
    name: 'Some name',
    address: {
        streetname: 'This is a test',
        housenumber: '23',
        zip: '1337',
    }
}

I tried creating creating a AddressSerializer

class Address(object):
    ...

class AddressSerializer(serializers.Serializer):
    streetname = serializers.CharField()
    housenumber = serializers.CharField()
    zip = serializers.CharField()
    ...

and then set the PersonSerializer.address to use the AddressSerializer

class PersonSerializer(serializers.ModelSerializer):
    ...
    address = AddressSerializer()

This results in my schema being correct. I generate swagger docs by using drf-yasg. It looks at the serializers to generate the correct model definitions. So the serializers needs to represent the schema.

So this is where I am at, at the moment. Obviously now it fails because there is no address property in the Person model. How would you go about solving this?

回答1:

from the DRF-doc for source says,

The value source='*' has a special meaning, and is used to indicate that the entire object should be passed through to the field. This can be useful for creating nested representations, or for fields which require access to the complete object in order to determine the output representation.


So,try this,

class AddressSerializer(serializers.Serializer):
    streetname = serializers.CharField(source='address_streetname')
    housenumber = serializers.CharField(source='address_housenumber')
    zip = serializers.CharField(source='address_zip')


class PersonSerializer(serializers.ModelSerializer):
    # .... your fields
    address = AddressSerializer(source='*')

    class Meta:
        fields = ('address', 'other_fields')
        model = Person


回答2:

you can just define the property:

class Person(models.Model):
    name = models.CharField()
    address_streetname = models.CharField()
    address_housenumber = models.CharField()
    address_zip = models.CharField()

    @property
    def address(self):
    return {'streetname': self.address_streetname,
            'housenumber': self.address_housenumber,
            'zip': self.address_zip}