2

TL;DR: how can I accept floats as query params in a url?

I have written an API view with Django Rest Framework. The API should be able to list houses by their addresses' coordinates using a GET request. Meaning the user should be able to send a query containing the coordinates as a get request and should get back a list of all the houses around these coordinates.

Here is the view (views.py):

class HouseListAPIView(generics.ListAPIView):
    serializer_class = HouseListSerializer
    queryset = House.objects.all()

    def get_queryset(self):
        qs = self.queryset
        longitude = self.request.query_params.get('lon', None)
        latitude = self.request.query_params.get('lat', None)
        radius = self.request.query_params.get('radius', 1)
        if radius < 0 or radius >= 1:
            radius = 1
        if longitude is not None and latitude is not None:
            qs = qs.filter(
                address__coordinates__latitude__lte=get_lat_max(longitude, latitude, radius),
                address__coordinates__latitude__gte=get_lat_min(longitude, latitude, radius),
                address__coordinates__longitude__lte=get_lon_max(longitude, latitude, radius),
                address__coordinates__longitude__gte=get_lon_min(longitude, latitude, radius)
            )
        return qs

Here are the helper functions (utils.py):

import geopy
from geopy.distance import VincentyDistance


def get_lat_lon_values_from_radius(d, lat1, lon1, b):
    """
    given: lat1, lon1, b = bearing in degrees, d = distance in kilometers
    """
    origin = geopy.Point(lat1, lon1)
    destination = VincentyDistance(kilometers=d).destination(origin, b)

    lat2, lon2 = destination.latitude, destination.longitude
    return lat2, lon2


def get_lat_max(lat1, lon1, d=1):
    # latitude = north / south
    return int(get_lat_lon_values_from_radius(d, lat1, lon1, 0)[0])


def get_lat_min(lat1, lon1, d=1):
    return int(get_lat_lon_values_from_radius(d, lat1, lon1, 180)[0])


def get_lon_max(lat1, lon1, d=1):
    # longitude = west / east
    return int(get_lat_lon_values_from_radius(d, lat1, lon1, 90)[1])


def get_lon_min(lat1, lon1, d=1):
    return int(get_lat_lon_values_from_radius(d, lat1, lon1, 270)[1])

My problem is that I currently get an empty query set back, because the number in the url get rounded for some reason:

input_lat: 50.7758666, input_lon: 6.0849401
lat_max: 6
lat_min: 6
lon_max: 50
lon_min: 50

How can I pass the longitude and the latitude as floats in the queryparams to the view?

halfer
  • 19,824
  • 17
  • 99
  • 186
J. Hesters
  • 13,117
  • 31
  • 133
  • 249
  • 1
    query params is string. So `self.request.query_params.get('lon', None)` will give you string. Try to convert it to float: `longitude = float(self.request.query_params.get('lon', None))`. – neverwalkaloner Jun 13 '18 at 11:15
  • 1
    Solved it using this. Thank you :) If you write it as an answer I will accept it. – J. Hesters Jun 13 '18 at 11:19

2 Answers2

3

When you access query_params in your view it give you string objects. You should convert it to float, like this:

longitude = float(self.request.query_params.get('lon', None))
neverwalkaloner
  • 46,181
  • 7
  • 92
  • 100
2

(Posted answer on behalf of the question author).

Using the answer by neverwalkaloner I solved it like this:

    def get_queryset(self):
        qs = self.queryset
        longitude = self.request.query_params.get('lon', None)
        latitude = self.request.query_params.get('lat', None)
        if longitude is not None and latitude is not None:
            longitude = float(longitude.replace(',', '.'))
            latitude = float(latitude.replace(',', '.'))
            radius = self.request.query_params.get('radius', 1)
            if radius < 0 or radius >= 1:
                radius = 1
            qs = qs.filter(
                address__coordinates__latitude__lte=get_lat_max(longitude, latitude, radius),
                address__coordinates__latitude__gte=get_lat_min(longitude, latitude, radius),
                address__coordinates__longitude__lte=get_lon_max(longitude, latitude, radius),
                address__coordinates__longitude__gte=get_lon_min(longitude, latitude, radius)
            )
        return qs
halfer
  • 19,824
  • 17
  • 99
  • 186