Search code examples
pythondjangodjango-rest-frameworkgeojsondjango-rest-framework-gis

How do I properly nest serializers in Django REST Framework?


I need to start saying that none of the solutions provided in similar question seem to work for me.

I've got two models

class Building(models.Model):
    (...)
    address =  models.ForeignKey('common.Address', null=True)

class Address (models.Model):
    (...)
    latlng = models.PointField(null=True)

I am using Django REST Framework (with addidtional GIS extension) serializers to serialize these models:

class BuildingSerializer(serializers.ModelSerializer):
    class Meta:
        model = Building

class AddressSerializer(serializers.GeoModelSerializer):
    class Meta:
        model = Address

With default serializers I get JSON looking like this:

results": [
        {
            (...)
            "address": 1
        }
    ] 

And desired JSON would look like this:

results": [
            {
                (...)
                "address": 1, 
                "latlng": {
                    "type": "Point", 
                    "coordinates": [
                        11.0, 
                        11.0
                    ]
                }, 
            }, 
        ]

Where latlng is field from address, which building can have only one.

Using this http://www.django-rest-framework.org/api-guide/serializers/#dealing-with-nested-objects throws and error:

Got AttributeError when attempting to get a value for field `latlng` on serializer `BuildingSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `Building` instance.
Original exception text was: 'Building' object has no attribute 'latlng'.

Solution

  • The simplest approach would be to add a latlng field to the Building serializer and implement a method to retrieve it:

    class BuildingSerializer(serializers.ModelSerializer):
    
        class Meta:
            model = Building
    
        latlng = serializers.SerializerMethodField()
    
        def get_latlng(self, obj):
            if obj.address and obj.address.latlng:
                return {
                    "type": obj.address.latlng.geom_type,
                    # any other fields in latlng
                }