如何有效地过滤数据库中的位置,而无需手动循环?

问题描述 投票:0回答:1

在我的 DRF 项目中,我有一个这样结构的模型

class ServiceLocation(models.Model):
    '''
    Represents a location where an internet service is offered
    '''
    SERVICE_TYPES = [
        ("wifi", 'wifi'),
        ("fibre", "fibre"),
        ("p2p/ptmp", "p2p/ptmp")
    ]

    id = models.UUIDField(primary_key=True, default=uuid.uuid4,
                          editable=False, null=False, blank=False)
    description = models.TextField()


    # Location
    address = models.CharField(max_length=150, null=False, blank=False)
    latitude = models.DecimalField(max_digits=18, decimal_places=15)
    longitude = models.DecimalField(max_digits=18, decimal_places=15)

    # Service
    service = models.CharField(
        max_length=10, choices=SERVICE_TYPES, null=False, blank=False)
    speed = models.IntegerField()
    
    def __str__(self):
        return f"{self.service} by {self.operator}"

并且我正在尝试根据其与给定坐标的相对接近度来过滤该模型的这些实例。

我的观点是这样的


class CloseServiceLocations(View):
    def get(self, request):
        lat = request.GET.get('lat', 6.748134)
        lng = request.GET.get('lng', 3.633301)
        distance = request.GET.get('distance', 10)  # Default distance to 10 if not provided

        # if lat is None or lng is None:
        #     # return JsonResponse({'error': 'Latitude and Longitude are required parameters.'}, status=400)


        try:
            lat = float(lat)
            lng = float(lng)
            distance = float(distance)
        except ValueError:
            return JsonResponse({'error': 'Invalid latitude, longitude, or distance provided.'}, status=400)

        # Create a Point object representing the provided latitude and longitude
        user_location = Point(lng, lat, srid=4326)

        

        # Calculate the distance in meters (Django's Distance function uses meters)
        distance_in_meters = distance * 1000  
        close_service_locations = ServiceLocation.objects.annotate(
            # Convert longitude and latitude fields to floats
            longitude_float=Cast('longitude', FloatField()),
            latitude_float=Cast('latitude', FloatField())
        ).annotate(
            # Create Point object using converted longitude and latitude
            location=Point(F('longitude_float'), F('latitude_float'), srid=4326)
        ).annotate(
            # Calculate distance
            distance=Distance('location', user_location)
        ).filter(distance__lte=distance_in_meters)
    # Serialize the queryset to JSON
        serialized_data = [{'id': location.id,
                            'description': location.description,
                            'operator': location.operator.name,
                            'address': location.address,
                            'latitude': location.latitude,
                            'longitude': location.longitude,
                            'service': location.service,
                            'speed': location.speed} for location in close_service_locations]

        return JsonResponse(serialized_data, safe=False)

    def post(self, request):
        return JsonResponse({'error': 'Method not allowed'}, status=405)

我尝试注释一个新属性“位置”,这样我就可以利用

from django.contrib.gis.db.models.functions
的距离方法来计算距离,而不是手动循环并计算半正弦距离,这是我最初的方法。

当我运行此程序时,我收到服务器错误消息,我几乎可以肯定这不是我的观点。

为了解决这个问题,我将视图分成几个部分,并添加打印语句以查看哪个部分导致它损坏

        print("Annotating QS with lon/lat float... ")
        close_service_locations = ServiceLocation.objects.annotate(
            # Convert longitude and latitude fields to floats
            longitude_float=Cast('longitude', FloatField()),
            latitude_float=Cast('latitude', FloatField())
        )
        print("LON/LAT float annotation complete")
       
        print("Annotating QS with location point... ")
        print("Size: ", len(close_service_locations))
       

        close_service_locations = close_service_locations.annotate(
            # Create Point object using converted longitude and latitude
            location=Point('longitude_float', 'latitude_float', srid=4326)
        )
        print("Location point annotation complete")

        print("Annotating QS with relative distance... ")
        close_service_locations = close_service_locations.annotate(
            # Calculate distance
            distance=Distance('location', user_location)
        )
        print("Distance annotation complete")

我注意到

print("Annotating QS with lon/lat float... ")
块很快就成功运行,但它在
print("Annotating QS with location point... ")
块中中断,我尝试使用 location 属性来注释 QS

在某些时候,我收到一条错误消息“为点初始化给出的参数无效”。这让我添加了

print("Annotating QS with lon/lat float... ")
块来强制所有 Decimalfiled 对象转换为浮点数。

我还尝试手动循环 close_service_locations 以查看是否有一个服务位置的纬度或经度无效

for i in range(len(close_service_locations)):
            print(i)
            location=Point(close_service_locations[i].longitude_float, close_service_locations[i].latitude_float, srid=4326)

令人惊讶的是,这次运行成功了。

但我仍然不知道如何让我的观点在这一点之后成功发挥作用。

这是我不断收到的错误

Annotating QS with lon/lat float...
LON/LAT float annotation complete
Annotating QS with location point...

[13/Apr/2024 04:49:30] "GET /directory/close-service-locations/ HTTP/1.1" 500 145 

浏览器上没有详细说明原因的 Django 错误页面,只是一个大服务器错误

我还尝试手动循环服务位置并添加位置属性并打印索引,也许我可以得到一些提示,了解是什么破坏了我的代码,或者是否有服务位置的经度和海拔对无法转换为点对象但我仍然遇到同样的错误

django database postgresql geodjango
1个回答
0
投票

在以下代码中,您将向

longitude_float
类提供字符串(
latitude_float
latitude_float 和
Point
)。

close_service_locations = close_service_locations.annotate(
    # Create Point object using converted longitude and latitude
     location=Point('longitude_float', 'latitude_float', srid=4326)
   )

您可能认为您正在创建上面的这些值,但这在

longitude_float=Cast('longitude', FloatField()),
行中,但在下面的注释中,它被视为只是字符串。

您可以尝试以下操作来使其发挥作用。

close_service_locations = ServiceLocation.objects.annotate(
            # Convert longitude and latitude fields to floats
            longitude_float=Cast('longitude', FloatField()),
            latitude_float=Cast('latitude', FloatField()),
            location=Point('longitude_float', 'latitude_float', srid=4326)
            # in the same code.
        )
© www.soinside.com 2019 - 2024. All rights reserved.