Home > other >  Adding ordering on serializer (Django)
Adding ordering on serializer (Django)

Time:11-22

I have an issue for which I'm not sure if I am getting the right path to the soluction, hope you guys could help me out.

I have added an extra field to my serializer, called distance (distance is equal the distance in miles between 2 different locations) I am looking to return the Business Object order my this new field, would it be possible? or am I taking the wrong path for this soluction?

Down here you have my Serializer and 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()

CodePudding user response:

You cannot filter in your view against a SerializerMethodField (or even object property) as Django filters operate at the database level.

Also, you cannot use annotate() in this case as it does not accept python function. The easiest solution is probably to just add a new field in your model where you store this value, and you can easily filter / order against it whenever you want.

CodePudding user response:

You should probably move your external API calls to fetch the distance field outside of your serializer as calling APIs is a side effect and should not be made in a serializer. Its purpose is to simply serialize data, not to fetch it from the web. If you're performing these calls on a big data set, your code is going to execute slow and possibly get throttled by the ipify.

A possible solution would be to move away from ModelViewSet to a regular APIView and perform a custom request.

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]
  • Related