2

使用此 SO post中的距离逻辑,我将使用以下代码返回一组经过适当过滤的对象:

class LocationManager(models.Manager):
    def nearby_locations(self, latitude, longitude, radius, max_results=100, use_miles=True):
        if use_miles:
            distance_unit = 3959
        else:
            distance_unit = 6371

        from django.db import connection, transaction
        cursor = connection.cursor()

        sql = """SELECT id, (%f * acos( cos( radians(%f) ) * cos( radians( latitude ) ) *
        cos( radians( longitude ) - radians(%f) ) + sin( radians(%f) ) * sin( radians( latitude ) ) ) )
        AS distance FROM locations_location HAVING distance < %d
        ORDER BY distance LIMIT 0 , %d;""" % (distance_unit, latitude, longitude, latitude, int(radius), max_results)
        cursor.execute(sql)
        ids = [row[0] for row in cursor.fetchall()]

        return self.filter(id__in=ids)

问题是我不知道如何保持列表/查询集按距离值排序。出于性能原因,我不想将其作为 extra() 方法调用(对我的数据库中每个潜在位置的一个查询与一个查询)。几个问题:

  1. 如何按距离对列表进行排序?即使取消我在模型中定义的本机排序并使用“order_by()”,它仍然按其他方式排序(id,我相信)。
  2. 我对性能问题有误吗,Django 会优化查询,所以我应该使用 extra() 代替吗?
  3. 这是完全错误的方法吗?我应该使用地理库而不是像putz一样手动滚动它?
4

1 回答 1

1

以相反的顺序回答您的问题:

Re 3) 是的,如果您使用地理空间数据,您绝对应该利用 PostGIS 和 GeoDjango。不这样做是愚蠢的。

Re 2)我不认为你可以让 Django 使用 .extra() 为你做这个查询(除非接受这张票),但它是 Django 1.2 中新的 .raw() 方法的一个很好的候选者(见下文)。

Re 1)您从第一个查询中获取 id 列表,然后使用“in”查询获取与这些 id 对应的对象的 QuerySet。您的第二个查询无法访问与第一个查询的计算距离;它只是获取一个 id 列表(它也不关心您提供这些 id 的顺序)。

可能的解决方案(放弃所有这些并使用 GeoDjango):

  1. 升级到 Django 1.2 beta 并使用新的 .raw() 方法。这允许 Django 智能地解释原始 SQL 查询的结果并将其转换为实际模型对象的 QuerySet。这会将您当前的两个查询减少为一个,并保留您在 SQL 中指定的顺序。如果您能够进行升级,这是最佳选择。

  2. 根本不需要构建 Django 查询集或 Django 模型对象,只需将所有需要的字段添加到原始 SQL SELECT 中,然后直接从游标中使用这些行。如果您稍后需要模型方法等,可能不是一个选择。

  3. 在 Python 代码中执行第三步,在其中迭代查询集并按照与从第一个查询返回的 ids 列表相同的顺序构造模型对象的 Python 列表。返回该列表而不是 QuerySet。如果您需要进一步过滤,则将无法正常工作。

于 2010-02-16T20:24:47.190 回答