Как мне сгладить объект постороннего ключа с помощью django-rest-framework-(gis)
Я долго и долго искал решение, актуальное и специфичное для моей проблемы, но пока не нашел решения или четкой документации о том, что мне действительно нужно сделать, чтобы выровнять отношения, чтобы стать геоджон-совместимым.
Этот вопрос почти идентичен моему, однако решения или ответы не решают проблему и по-прежнему выдают недействительный GeoJSON.
связанные с
- Установить сериализатор geo_field как PointField от другой модели - Django
- Django REST Framework: определить поля во вложенном объекте?
проблема
у меня есть Location
модель, которая содержит pointfield
с SRID=4326. у меня тоже есть TrainStation
модель, которая имеет location
поле как внешний ключ к Location
, Когда я сериализую TrainStation
через GeoFeatureModelSerializer
он генерирует неверный GeoJSON (см. пример ниже "неверный геойсон").
(Действительный GeoJSON, конечно, можно получить, если я буду хранить PointField
в TrainStation
модель, но в моем случае я не могу этого сделать, поэтому мне нужно как-то сгладить ее.)
Вопрос
- Как мне получить вывод, как в примере "Valid GeoJSON" ниже?
Исследование
Я новичок в Python и Django, поэтому я еще не очень хорошо читаю исходный код других людей, однако думаю, что могу сделать вывод, что мне нужно как-то переопределить to_representation()
метод, чтобы получить то, что я хочу, но мои поиски пока безрезультатны, поэтому я застрял.
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:
Я проверил вывод на http://geojson.io/ чтобы определить, действителен ли он или нет.
Неверный 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"
}
}
]
}
Действительный 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"
}
}
]
}
2 ответа
Теперь я решил эту проблему с помощью следующего кода:
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',
]
И это действительно дает действительный 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"
}
}
]
}
Если у кого-то есть какие-либо материалы по этому вопросу, не стесняйтесь внести свой вклад и добавить ответ:-)
Я думаю, что проблема может заключаться в том, что вы объявляете весь ModelSerializer своим гео-полем, а он просто прикрепляет результат этого сериализатора, который сам является целым объектом геоджон, внутри части геометрии вашего основного объекта геоджон и сериализаторов, которые поставляются с django-restframework-gis просто не знает, что с этим делать.
Что за GeoFeatureModelSerializer
класс хотел бы видеть как geo_field является GeometryField
который был сериализован своим собственным rest_framework_gis.fields.GeometryField
, Я полагаю, что любое из следующего даст вам поведение, которое вы хотите.
просто добавьте location.point к вашему TrainStationSerializer в качестве его геополя, используя формат двойного подчеркивания. Отказ от ответственности: я не совсем уверен, что drf-gis правильно применяет двойное подчеркивание для свойства geo_field, но я думаю, что это должно сработать.
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', ]
Использовать
fields.GeometryField
класс, поставляемый с drf-gis, и укажите поле location.point в качестве его источника.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', ]
Использовать
GeometrySerializerMethodField
как показано в примере в readme drf-gisfrom 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', ]