Search code examples
pythondjangoannotate

Nearby stores ,without using Geo packages, Django Rest


I need to list stores nearby an address Here is my code :

class Shop(TimeStamp):
    city = models.CharField(max_length=15, choices=CITIES, blank=True)
    lat = models.DecimalField(decimal_places=6, max_digits=10, verbose_name='latitude', default=None)
    long = models.DecimalField(decimal_places=6, max_digits=10, verbose_name='longitude', default=None)
   
    def distance_shop(self, location):
        return distance((self.lat, self.long), location)

#####################

    #this is the function I used for calculating distance I used haversine distance(origin, destination)
    def distance(origin, destination):
        lat1, lon1 = origin
        lat2, lon2 = destination
        radius = 6371  # km
        dlat = math.radians(lat2 - lat1)
        dlon = math.radians(lon2 - lon1)
        a = math.sin(dlat / 2) * math.sin(dlat / 2) +      math.cos(math.radians(lat1))* math.cos(math.radians(lat2)) * math.sin(dlon/2)*math.sin(dlon / 2)
        c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
        d = radius * c
        return d

    

in my apiView using post method receiving an address lat and long I did this :

 class NearbyShops(APIView):
    permission_classes = [IsAuthenticated]
    serializer_class = NearbyShopsSerializer

    def post(self, request):
       data = request.data
       serializer = NearbyShopsSerializer(data=data)
       if serializer.is_valid(raise_exception=True):
          try:
            address = DeliveryAddress.objects.get(client=request.user, lat=serializer.data.get('address_lat'),
                                                  long=serializer.data.get('address_long'))
          except DeliveryAddress().DoesNotExist:
            return Response({"error": "This address doesn't exist"}, status=status.HTTP_404_NOT_FOUND)       

          try:
            shops = Shop.objects.filter(city=address.city)
          except Shop().DoesNotExist:
            return Response({"error": "No shops in this city address"},
                            status=status.HTTP_417_EXPECTATION_FAILED)
          list = {}
          for shop in shops:
             location = (address.lat, address.long)
             dis = shop.distance_shop(location)
             shops = shops.annotate(distance=dis).order_by('distance')
          closest = Shop.objects.filter(distance__lt=10.0)
          for close in closest:
            list['name'] = close.name
            list['long'] = close.long
            list['lat'] = close.lat
          return Response({'shops': list}, status=status.HTTP_200_OK)

I don't know why , but I get in return this error :

QuerySet.annotate() received non-expression(s): 4783.728105194982


Solution

  • It's better to store a geopoint in your table rather than storing longitude and latitude separately. This will improve the performance of your view and simplify your code

    your Shop, Address model should look like this

    class MyModel(models.Model:
        geopoint = GeometryField(null=True, blank=True)
    

    Then you can use something like this in your view:

    geopoint = Point(address_long, address_lat)
    closest_shops = Shop.objects.filter(geopoint__distance_lte=(place.geometry, D(m=10.0)).annotate(distance=Distance('geometry', geopoint))
    

    Please let me know if you need more clarification