首页 > 解决方案 > Django过滤位置距离与动态半径

问题描述

我在 django 有 2 个模型,一个区域和一个商店,模型是这样的:

from django.contrib.gis.db import models
from django.contrib.gis.geos import Point
from django.contrib.gis.measure import D
from location_field.models.spatial import LocationField


class Zone(models.Model):
    name = models.CharField(max_length=200)
    location_point = LocationField(based_fields=['city'], zoom=7, default=Point(51.67, 32.65))
    radius = models.IntegerField(default=1000)  # radius in meters

class Shop(models.Model):
    name = models.CharField(max_length=200)
    location_point = LocationField(based_fields=['city'], zoom=7, default=Point(51.67, 32.65), null=True, blank=True)
    zone = models.ForeignKey(Zone, on_delete=models.CASCADE, null=True)

LocationField是一个 PointField,在 django admin 中有更好的地图。

我希望每个商店都根据商店位置、区域位置和半径自动保存选择区域。如果没有半径区域支持商店,它将是无。我试过这个查询:

zone_list = Zone.objects.filter(
    location_point__distance_lte=(
        shop.location_point, D(m=models.F('radius'))
    )
)

但我得到这个错误:

TypeError: float() 参数必须是字符串或数字,而不是“F”

我怎样才能解决这个问题?

标签: pythondjangoormgeodjango

解决方案


这似乎发生在(/MeasureBase继承自)的类中,更具体地说,发生在它尝试将或数字输入值转换为但接收表达式的方法中。django.contrib.gis.measureDistanceDdefault_unitsstrfloatF

作为一种解决方法,我们可以做的是annotateDistance小心使用此Distance方法,因为它来自 GeoDjango 地理数据库函数shop.location_point和当前之间location_point,然后我们可以通过该距离进行过滤,而<=不是实例radius

from django.contrib.gis.db.models.functions import Distance

zone_list = Zone.objects.annotate(
    distance=Distance('location_point', shop.location_point)
).filter(distance__lte=F('radius'))

对@e4c5 的出色回答表示敬意:GeoDjango filter by distance from a model field

另一种方法是annotation完全消除该部分并直接通过以下方式进行过滤Distance

from django.contrib.gis.db.models.functions import Distance

zone_list = Zone.objects.filter(
    radius_gte=Distance('location_point', shop.location_point)
)


我将其留在这里以保持评论的连续性:

您可以尝试使用将 Integer 转换为 Float 的方法将F('radius')结果转换为a。FloatField()Cast()

zone_list = Zone.objects.filter(
    location_point__distance_lte=(
        shop.location_point, 
        D(m=Cast('radius', output_field=models.FloatField()))
    )
)


推荐阅读