Django按计算字段排序

时间:2010-02-16 19:09:48

标签: django sorting distance haversine

使用this 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. 这是完全错误的做法吗?我应该使用geo库而不是像putz一样手动滚动它?

1 个答案:

答案 0 :(得分:1)

以相反的顺序提出问题:

重新3)是的,如果你正在处理地理空间数据,你肯定应该利用PostGIS和GeoDjango。愚蠢的不是。

Re 2)我认为你不能让Django使用.extra()为你做这个查询(除非接受this ticket),但它是新的.raw的优秀候选者( )Django 1.2中的方法(见下文)。

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

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

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

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

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