Search code examples
djangoserializationdjango-rest-frameworkencapsulation

django rest framework serializer, create an object to encapsulate some model field


I have a serializer which looks like this:

class ListingSerializer(serializers.ModelSerializer):
    class Meta:
        model = Listing
        fields = '__all__'

My Listing model have some field: name, price, description, etc.., street_address, postal_code, city, etc...

I would like my serializer to return an object like this:

{
    "name": "Prestige",
    "price": 12,
    "description": "lorem ipsum",
    "address": {
        "street": "123 Main St",
        "city": "New York",
        "state": "NY",
        "zip": "10001"
    },
    ...
}

instead of the basic:

{
    "name": "Prestige",
    "price": 12,
    "description": "lorem ipsum",
    "street": "123 Main St",
    "city": "New York",
    "state": "NY",
    "zip": "10001"
    ...
}

What I want is encapsulate all "address" field into an "address" object in my response.


Solution

  • First option is to override .to_representation on your serializer:

    serializers.py

    class ListingSerializer(serializers.ModelSerializer):
        class Meta:
            model = Listing
            fields = '__all__'
    
        def to_representation(self, instance):
            representation = super().to_representation(instance)
            address = {}
            address['street'] = representation.pop('street')
            address['city'] = representation.pop('city')
            address['state'] = representation.pop('state')
            address['zip'] = representation.pop('zip')
    
            representation['address'] = address
            return representation
    

    Alternatively

    If it is possible to change your models, maybe the better option is to add an Address and associate it with Listing:

    models.py

    class Address(models.Model):
        street = models.CharField(max_length=100)
        city =models.CharField(max_length=20)
        state = models.CharField(max_length=5)
        zip = models.CharField(max_length=15)
    
    class Listing(models.Model):
        name = models.CharField(max_length=255)
        price = models.IntegerField()
        description = models.CharField(max_length=255)
        address = models.ForeignKey(Address, on_delete=models.DO_NOTHING, related_name='listings')
    

    serializers.py

    class AddressSerializer(serializers.ModelSerializer):
        class Meta:
            model = Address
            exclude = ['id']
    
    class ListingSerializer(serializers.ModelSerializer):
        address = AddressSerializer()
    
        class Meta:
            model = Listing
            fields = ['name', 'price', 'description', 'address']