Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I flatten a foreignkey object with django-rest-framework-(gis)

I have searched long and far for a solution that is up to date and specific to my problem but have yet not found a solution or a clear documentation on what I really need to do in order to flatten a relationship to become geojson compliant.

This question is almost identical to mine, however the solutions or answers does not solve the problem and still produces invalid GeoJSON.

  • django-rest-framework-gis related field

Related

  • Set serializer geo_field as PointField from another model - Django
  • Django REST Framework: define fields in nested object?

Problem

I have a Location model that holds a pointfield with SRID=4326. I also have a TrainStation model that has location field as foreign key to Location. When I serialize the TrainStation via the GeoFeatureModelSerializer it produces invalid GeoJSON (see example below "invalid geojson").

(Valid GeoJSON can, of course, be obtained if I where to store the PointField in the TrainStation model, but in my case, I cannot do that so I need to flatten it somehow.)

Question

  • How do I achieve output like the "Valid GeoJSON" example below?

Research

I am a newcomer to both Python and Django thus I am yet not very good at reading other people's source code, however I think I can conclude that I need to somehow override the to_representation() method in order to get what I want, but my searches are so far fruitless so I am stuck.

models.py

class Location(models.Model):

    point = models.PointField()

class TrainStation(models.Model):

    location_signature = models.CharField(primary_key=True, max_length=32)
    advertised_location_name = models.CharField(max_length=32)
    country_code = models.ForeignKey(Country)
    county_no = models.ForeignKey(County)
    location = models.ForeignKey(Location, null=True)

serializers.py

class LocationSerializer(ModelSerializer):

    class Meta:
        model = Location
        geo_field = 'point'
        fields = [
            'point',
        ]


class TrainStationSerializer(GeoFeatureModelSerializer):

    location_signature = PrimaryKeyRelatedField(read_only=True)
    location = LocationSerializer(read_only=True)
    country_code = StringRelatedField(read_only=True)
    county_no = StringRelatedField(read_only=True)

    class Meta:
        model = TrainStation
        geo_field = 'location'
        fields = [
            'location_signature',
            'advertised_location_name',
            'country_code',
            'county_no',
        ]

GeoJSON Output examples:

I have verified the output on http://geojson.io to determine if its valid or not.

Invalid GeoJSON

{
    "type": "FeatureCollection",
    "features": [
        {
            "id": "Ak",
            "type": "Feature",
            "geometry": {
                "point": {           <------+------ offending lines
                    "type": "Point",        |
                    "coordinates": [        |
                        18.8303462142963,   |
                        68.3486410812835    |
                    ]                       |
                }                    <------+
            },
            "properties": {
                "advertised_location_name": "Abisko Östra",
                "country_code": "Sverige",
                "county_no": "Norrbottens län"
            }
        }
    ]
}

Valid GeoJSON

This is the output I am looking for.

{
    "type": "FeatureCollection",
    "features": [
        {
            "id": "Ak",
            "type": "Feature",
            "geometry": {
                "type": "Point",
                "coordinates": [
                    18.8303462142963,
                    68.3486410812835
                ]
            },
            "properties": {
                "advertised_location_name": "Abisko Östra",
                "country_code": "Sverige",
                "county_no": "Norrbottens län"
            }
        }
    ]
}
like image 257
sphrak Avatar asked Jul 01 '17 04:07

sphrak


1 Answers

I have now solved this issue with the following code:

class LocationSerializer(ModelSerializer):

    def to_representation(self, obj):

        representation = super().to_representation(obj)
        point_representation = representation.pop('point')
        for key in point_representation:
            representation[key] = point_representation[key]

        return representation

    class Meta:
        model = Location
        geo_field = 'point'
        fields = [
            'point',
        ]

And this does indeed produce valid GeoJSON:

{
    "type": "FeatureCollection",
    "features": [
        {
            "id": "Ak",
            "type": "Feature",
            "geometry": {
                "type": "Point",
                "coordinates": [
                    18.8303462142963,
                    68.3486410812835
                ]
            },
            "properties": {
                "advertised_location_name": "Abisko Östra",
                "country_code": "Sverige",
                "county_no": "Norrbottens län"
            }
        }
    ]
}

If someone has any inputs on this feel free to contribute and add an answer :-)

like image 108
sphrak Avatar answered Oct 22 '22 23:10

sphrak