GeoDjango: объект расстояния не сериализуем

Я только изучаю гео-джанго. Я могу узнать расстояние до всех мест из точки. Но когда я использую метод .values для аннотированного поля distance, я получаю

TypeError: Object of type 'Distance' is not JSON serializable

Вот мои фрагменты кода

#models.py
import uuid
from django.contrib.gis.db import models
from django.contrib.gis.db.models.functions import Distance
from django.contrib.gis.geos import Point
class PlaceManager(models.GeoManager):
    def get_queryset(self):
        qs = super(PlaceManager, self).get_queryset()
        qs = qs.annotate(
        latitude=models.ExpressionWrapper(models.Func('position', function='ST_X'), output_field=models.FloatField()),
        longitude=models.ExpressionWrapper(models.Func('position', function='ST_Y'), output_field=models.FloatField()),
    )
    return qs.distinct()

    def nearby_places(self, lat, lng):
        p = Point(lat, lng, srid=4326)
        qs = self.get_queryset()
        qs = qs.annotate(
            distance=Distance('position', p)
        )
        return qs.order_by('distance').distinct()


class Place(models.Model):
    id = models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, db_index=True)
    position = models.PointField()
    address = models.TextField(default=None, null=True, blank=True)
    objects = PlaceManager()

    def __str__(self):
        return '{},{}'.format(self.position.x, self.position.y)

Теперь фрагменты кода, которые у меня есть, такие

from rest_framework.views import APIView
from rest_framework import status
from rest_framework.response import Response

class NearbyPlaces(APIView):
    def get(self, request):
        p = Place.objects.nearby_places(30.45, -90.43)
        p = p.values('distance', 'address', 'latitude', 'longitude')
        return Response(p, status=status.HTTP_200_OK)

Значение p здесь такое

<GeoQuerySet [{'distance': Distance(m=7596021.71574835), 'address': 'New York City, New York','latitude': 13.4586, 'longitude': 45.6789}]>

Так что все, что мне нужно здесь, это 'distance': 7596021.71574835 вместо 'distance': Distance(m=7596021.71574835)

Любая помощь в этом? Заранее спасибо.


person Anonymous Coder    schedule 28.07.2017    source источник
comment
Я бы предложил не использовать .values(), а вместо этого использовать сериализатор. Таким образом, когда сериализатор получает набор запросов, вы можете выбрать значение расстояния и установить его в качестве значения. django-rest-framework.org/tutorial /1-сериализация/   -  person Erion S    schedule 28.07.2017
comment
Хорошо нашел другой способ сделать это. Только что сделал рендерер и использовал его как класс рендерера по умолчанию в django rest framework.   -  person Anonymous Coder    schedule 28.07.2017
comment
В файле #models.py просто измените distance=Distance('position', p) на distance=Distance('position', p).m< /b> и это сработает. Таким образом, вы можете сохранить весь остальной код в том виде, в котором он был первоначально, без необходимости в вашем классе Renderer.   -  person Erion S    schedule 28.07.2017
comment
@ErionV здесь значение p может быть динамическим.   -  person Anonymous Coder    schedule 04.08.2017


Ответы (2)


есть более простой способ, нужно только изменить содержимое в serializer.py:

class CitySerializer(serializers.ModelSerializer):

    distance = serializers.SerializerMethodField()

    def get_distance(self, obj):

        return obj.distance.m

    class Meta:
        model = City
        fields = ('distance')
person Yuchen Huang    schedule 14.11.2018
comment
Это гораздо лучшее решение, первое решение - излишество. - person jnowak; 31.05.2019

Просто нашел способ сделать это. Пришлось создать класс Renderer в остальной структуре и обработать там Distance объекты. Фрагменты кода такие.

encoders.py

from rest_framework.utils.encoders import JSONEncoder
from django.contrib.gis.measure import Distance


class CustomJsonEncoderWithDistance(JSONEncoder):
    def default(self, obj):
        if isinstance(obj, Distance):
            print(obj)
            return obj.m
        return super(CustomJsonEncoderWithDistance, self).default(obj)

рендеры.py

from rest_framework.renderers import JSONRenderer
from .encoders import CustomJsonEncoderWithDistance


class CustomJsonRenderer(JSONRenderer):
     encoder_class = CustomJsonEncoderWithDistance

settings.py

...
REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': (
        'app.renderers.CustomJsonRenderer',
    )
}
person Anonymous Coder    schedule 28.07.2017