Как мне сгладить объект постороннего ключа с помощью django-rest-framework-(gis)

Я долго и долго искал решение, актуальное и специфичное для моей проблемы, но пока не нашел решения или четкой документации о том, что мне действительно нужно сделать, чтобы выровнять отношения, чтобы стать геоджон-совместимым.

Этот вопрос почти идентичен моему, однако решения или ответы не решают проблему и по-прежнему выдают недействительный GeoJSON.

связанные с

проблема

у меня есть 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, Я полагаю, что любое из следующего даст вам поведение, которое вы хотите.

  1. просто добавьте 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',
            ]
    
  2. Использовать 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',
            ]
    
  3. Использовать GeometrySerializerMethodField как показано в примере в readme drf-gis

    from 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',
            ]
    
Другие вопросы по тегам