Добавление упорядочивания в сериализаторе (Django)

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

Я добавил в свой сериализатор дополнительное поле, называемое distance (расстояние равно расстоянию в милях между двумя разными местами). Я хочу вернуть порядок бизнес-объекта в это новое поле, возможно ли это? Или я иду неверным путем для решения этой проблемы?

Внизу у вас есть мой Serializer и ModelViewSet

Serializer

class BusinessesSerializer(serializers.ModelSerializer):
    distance = serializers.SerializerMethodField('get_location')

    class Meta:
        model= Businesses
        fields = ('id', 'address_first_line', 'address_second_line',
                  'city', 'region', 'post_code', 'phone_number', 'logo', 'join_date', 'distance')

    def get_location(self, business):
        ip_info = requests.get('https://api64.ipify.org?format=json').json()
        ip_address = ip_info["ip"]
        response = requests.get(f'http://api.ipstack.com/{ip_address}?access_key=8eba29fcae0bbc63c1e93b8c370e4bcf').json() 
        latitude = response.get("latitude")
        longitude = response.get("longitude")
        first = (float(latitude), float(longitude))
        second = (business.lat, business.long)
        distance = great_circle(first, second).miles


    return distance

ModelViewSet

class BusinessesViewSet(ModelViewSet):
    serializer_class = BusinessesSerializer
    queryset = Businesses.objects.all()

Вы не можете фильтровать в представлении по полю SerializerMethodField (или даже свойству объекта), так как фильтры Django работают на уровне базы данных.

Кроме того, вы не можете использовать annotate() в этом случае, так как он не принимает функцию python. Самым простым решением, вероятно, будет просто добавить новое поле в вашу модель, где вы будете хранить это значение, и вы сможете легко фильтровать / заказывать по нему, когда захотите.

Вероятно, вам следует перенести вызовы внешних API для получения поля distance за пределы вашего сериализатора, поскольку вызов API является побочным эффектом и не должен выполняться в сериализаторе. Его цель - просто сериализовать данные, а не получать их из Интернета. Если вы выполняете эти вызовы на большом наборе данных, ваш код будет выполняться медленно и, возможно, будет заблокирован ipify.

Возможным решением может быть переход от ModelViewSet к обычному APIView и выполнение пользовательского запроса.

class BusinessesAPIView(APIView):

    def get(self, request):
        # Get IP info once
        ip_info = requests.get('https://api64.ipify.org?format=json').json()
        ip_address = ip_info["ip"]
        response = requests.get(
            f'http://api.ipstack.com/{ip_address}?access_key=8eba29fcae0bbc63c1e93b8c370e4bcf').json()
        latitude = response.get("latitude")
        longitude = response.get("longitude")
        first = (float(latitude), float(longitude))

        # Calculate distances for all businesses and pass them as a context to our serializer
        businesses = Businesses.objects.all()
        distances = {}
        for business in businesses:
            second = (business.lat, business.long)
            distance = great_circle(first, second).miles
            distances[business.id] = distance

        # Sort by distance
        businesses_processed = BusinessesSerializer(businesses, many=True, context={'distances': distances}).data
        businesses_processed.sort(key=lambda x: x['distance'])

        return Response({'businesses': businesses_processed})


class BusinessesSerializer(serializers.ModelSerializer):
    distance = serializers.SerializerMethodField()

    class Meta:
        model= Businesses
        fields = ('id', 'address_first_line', 'address_second_line',
                  'city', 'region', 'post_code', 'phone_number', 'logo', 'join_date', 'distance')

    # Get distance by business id from context we passed from our APIView
    def get_distance(self, business):
        return self.context['distances'][business.id]
Вернуться на верх