附近的商店,不使用 Geo 包,Django Rest

Nearby stores ,without using Geo packages, Django Rest

我需要列出某个地址附近的商店 这是我的代码:

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

    

在我的 apiView 中使用 post 方法接收经纬度地址 我这样做了 :

 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)

我不知道为什么,但我遇到了 return 这个错误:

QuerySet.annotate() 收到非表达式:4783.728105194982

最好在 table 中存储一个地理点,而不是分别存储经度和纬度。这将提高视图的性能并简化代码

您的商店、地址模型应该如下所示

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

然后你可以在你的视图中使用这样的东西:

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))

如果您需要更多说明,请告诉我

首先,当您尝试制作地理定位应用程序时,您应该使用 postgresql 及其 postgis 扩展,并且 django 有一个名为 geodjango 的强大包,您不再需要使用两个不同的字段名称 longlat

from django.contrib.gis.db import models
class Shop(models.Model):
    city= models.CharField(max_length=100)
    long_lat = models.PointField()

然后在你的 views.py

里面
from django.views import generic
from django.contrib.gis.geos import fromstr , Point
from django.contrib.gis.db.models.functions import Distance
from .models import Shop


Latitude = 31.2551 # for example , you can get long and lat from user using js
Longitude = 49.8824 #for example
user_location = Point(Latitude , Longitude , srid=4326)

class NearestShop(generic.ListView):
    model = Shop
    context_object_name = 'shops'
    queryset = Shop.objects.annotate(distance=Distance('long_lat ',user_location)).order_by('distance').distinct('distance')

如果您使用 windows 并且无法配置 postgis 我有勇气在 windows

上使用 ubuntu 命令行

我最终这样做而不是使用注释(距离)。 不使用 Postgis 扩展。

       #### see code in Question
        list = {}
        location = (address.lat, address.long)
        for shop in shops:
            dis = shop.distance_shop(location)
            if dis <= 10.0:
                list['name'] = shop.name
                list['long'] = shop.long
                list['lat'] = shop.lat
            return Response({'message': 'no close shops to your address'}, status=status.HTTP_303_SEE_OTHER)
        return Response({'shops in district': list}, status=status.HTTP_200_OK)