2010-02-16 52 views
2

運用this SO post的距離邏輯,我又回到這個代碼適當過濾的一組對象:Django的排序計算字段

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) 

問題是我無法弄清楚如何保持按距離值排序的列表/查詢集。我不希望將其作爲額外的()方法調用,因爲性能方面的原因(在數據庫中每個潛在位置上的一個查詢與一個查詢)。幾個問題:

  1. 如何根據距離對我的列表進行排序?即使脫掉我在模型中定義的本地排序,並使用「order_by()」,它仍然按其他內容排序(id,我相信)。
  2. 我錯了關於性能的事情,Django會優化查詢,所以我應該使用extra()來代替?
  3. 這是完全錯誤的方式來做到這一點,我應該使用地理庫,而不是像putz手動滾動?

回答

1

回答大家的提問以相反的順序:

回覆3)是的,你一定要採取PostGIS中,GeoDjango內置的優勢,如果你正在使用的地理空間數據的工作。這真是愚蠢的不。我不認爲你可以讓Django爲你使用.extra()(除了接受this ticket)做這個查詢,但它是新的.raw()方法的一個很好的候選人。 Django 1.2(見下文)。

Re 1)您將從第一個查詢中獲取id列表,然後使用「in」查詢獲取與這些id對應的對象的QuerySet。您的第二個查詢無法訪問第一個查詢的計算距離;它只是提取一個id列表(它不關心你提供這些id的順序)。

可能的解決方案(短開溝所有這一切,並使用GeoDjango內置的):

  1. 升級到Django的1.2β和使用new .raw() method。這允許Django智能地解釋原始SQL查詢的結果並將其轉換爲實際模型對象的QuerySet。這會將當前的兩個查詢合併爲一個,並保留您在SQL中指定的順序。如果您能夠升級,這是最好的選擇。

  2. 不要打擾構建Django查詢集或Django模型對象,只需將所需的所有字段添加到原始SQL SELECT中,然後使用直接來自遊標的那些行。如果您以後需要模型方法等,可能不會成爲一種選擇。

  3. 在Python代碼中執行第三步,在該代碼中迭代查詢集並按照與第一個查詢返回的ids列表相同的順序構建模型對象的Python列表。返回該列表而不是QuerySet。如果您需要進一步過濾該行,則無法工作。

+0

謝謝,這就是我所想的。如果我可以再次濫用你的善良本性:我以前做過#2,我只是覺得很骯髒。無論如何,只要建立一個查詢集對象,並按照我想要的順序將這些對象粘貼到它中?我現在要去2或3(網站今晚/明天啓動),但我想知道這是否可能。 – Tom

+0

可能嗎?當然。簡單或簡單或乾淨?不,沒有理由這樣做。如果你現在需要一個骯髒的黑客,使用#2或#3。無論如何,稍後右側選項是#1或GeoDjango。 –