问题
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"
}
}
]
}
回答1:
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 :-)
回答2:
I think the problem might be that you're declaring a whole ModelSerializer as your geo_field and its just sticking the result of that serializer, which is itself an entire geojson object, inside the geometry part of your main geojson object and the Serializers that come with django-restframework-gis just don't know what to do about that.
What the GeoFeatureModelSerializer
class would like to see as a geo_field is a GeometryField
which has been serialized by its own rest_framework_gis.fields.GeometryField
. I imagine any of the following would get you the behavior you want.
just add location.point to your TrainStationSerializer as its geofield using the double underscore format. Disclaimer: I'm not actually sure off the top of my head if drf-gis implements the double underscores properly for the geo_field property, but I think this should work.
from rest_framework_gis.fields import GeometryField from rest_framework_gis.serializers import GeoFeatureModelSerializer class TrainStationSerializer(GeoFeatureModelSerializer): location_signature = PrimaryKeyRelatedField(read_only=True) country_code = StringRelatedField(read_only=True) county_no = StringRelatedField(read_only=True) class Meta: model = TrainStation geo_field = 'location__point' fields = [ 'location_signature', 'advertised_location_name', 'country_code', 'county_no', ]
Use the
fields.GeometryField
class that comes with drf-gis and specify your location.point field as its source.from rest_framework_gis.fields import GeometryField from rest_framework_gis.serializers import GeoFeatureModelSerializer class TrainStationSerializer(GeoFeatureModelSerializer): location_signature = PrimaryKeyRelatedField(read_only=True) location = GeometryField(source='location.point') 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', ]
Use the
GeometrySerializerMethodField
as shown in the example on drf-gis's readmefrom rest_framework_gis.fields import GeometrySerializerMethodField from rest_framework_gis.serializers import GeoFeatureModelSerializer class TrainStationSerializer(GeoFeatureModelSerializer): location_signature = PrimaryKeyRelatedField(read_only=True) location = GeometrySerializerMethodField() country_code = StringRelatedField(read_only=True) county_no = StringRelatedField(read_only=True) def get_location(self, obj): return obj.location.point class Meta: model = TrainStation geo_field = 'location' fields = [ 'location_signature', 'advertised_location_name', 'country_code', 'county_no', ]
来源:https://stackoverflow.com/questions/44857249/how-can-i-flatten-a-foreignkey-object-with-django-rest-framework-gis