2013-03-18 54 views
8

我有這兩個模型。按相關ManyToMany字段的距離排序

class Store(models.Model): 
    coords = models.PointField(null=True,blank=True) 
    objects = models.GeoManager() 

class Product(models.Model): 
    stores = models.ManyToManyField(Store, null=True, blank=True) 
    objects = models.GeoManager() 

我想獲得按點距離排序的產品。如果產品中的商店字段是外鍵我會這樣做,它的工作原理。

pnt = GEOSGeometry('POINT(5 23)') 
Product.objects.distance(pnt, field_name='stores__coords').order_by('distance') 

但由於該領域是它與

ValueError: <django.contrib.gis.db.models.fields.PointField: coords> is not in list 

我種的預計,因爲目前還不清楚該商店應使用哪種計算距離打破了多對多場,但沒有任何這樣做的方法。

我需要按特定點距離排列的產品列表。

+0

我也一直有這個困難。也許GeoDjango不可能這樣做?也許你必須爲它創建一個原始的sql? – 2013-04-01 17:06:58

+0

@JoeJ我會發布我做的一個可能的答案,但我不喜歡它。可能原始的SQL可以工作,但我對空間查詢和那個不太舒適。看看答案,看看你的想法。 – manuel 2013-04-03 11:03:39

+4

產品有'ManyToMany'到'Store',但它是有'PointField'的商店。一個產品可以在一個或多個商店... **這是一個商店超過一個商店的距離?**較低?越高 ?所有? – AlvaroAV 2014-06-19 11:18:39

回答

0

這就是我解決它的方法,但我不太喜歡這個解決方案。我認爲效率很低。 GeoDjango應該有更好的方法。所以,直到我找到更好的解決方案,我可能不會使用這個。這就是我所做的。

我添加了一個新的方法,以產品型號

class Product(models.Model): 
    stores = models.ManyToManyField(Store, null=True, blank=True) 
    objects = models.GeoManager() 

    def get_closes_store_distance(point): 
     sorted_stores = self.stores.distance(point).order_by('distance') 
     if sorted_stores.count() > 0: 
      store = sorted_stores[0] 
      return store.distance.m 
     return 99999999 # If no store, return very high distance 

然後,我可以這樣

def sort_products(self, obj_list, lat, lng): 
    pt = 'POINT(%s %s)' % (lng, lat) 
    srtd = sorted(obj_list, key=lambda obj: obj.get_closest_store_distance(pt)) 
    return srtd 

任何更好的解決方案或方法來改善這個是非常歡迎的排序。

+1

您是否碰巧找到了有效的解決方案? – 2014-06-18 19:25:42

+0

@VikasGulati&manuel:我會對這個問題(VikasGulati可能沒有見過)重複評論:什麼是「從產品到點的距離」? (可能是從該點到該產品的商店的最小距離。)準確的輸出是什麼? (可能是按距離升序排列的所有產品的(產品,距離)列表。) – philipxy 2014-06-25 05:45:58

+0

@philipxy:是的距離是最小的。我期望在類似問題中獲得的輸出是按照計算出的最小距離排序的產品清單+每個產品都應該再次按照距離進行預取的相關商店。 – 2014-06-25 09:59:59

1

只是一個想法,也許這會適合你,這應該只需要兩個數據庫查詢(由於預取如何工作)。不要判斷嚴厲,如果它不工作,我還沒有嘗試過:

class Store(models.Model): 
    coords = models.PointField(null=True,blank=True) 
    objects = models.GeoManager() 

class Product(models.Model): 
    stores = models.ManyToManyField(Store, null=True, blank=True, through='ProductStore') 
    objects = models.GeoManager() 

class ProductStore(models.Model): 
    product = models.ForeignKey(Product) 
    store = models.ForeignKey(Store) 
    objects = models.GeoManager() 

則:

pnt = GEOSGeometry('POINT(5 23)') 
ps = ProductStore.objects.distance(pnt, field_name='store__coords').order_by('distance').prefetch_related('product') 
for p in ps: 
    p.product ... # do whatever you need with it 
+0

感謝您的回答。我在一年前發佈了這篇文章,很遺憾我沒有訪問代碼或者測試這個方法的方法。如果有人發現它適用於他們,我會將其標記爲已回答。 – manuel 2014-06-30 11:35:09

0

我將採取「距離從產品到一個點」是從該點到具有該產品的商店的最小距離。我將把輸出作爲按距離升序排列的所有產品的(產品,距離)列表。 (某人發表了評論,表示他們有時也希望(產品,距離,商店)按距離排序,然後存儲在產品中。)

每種型號都有相應的表格。模型的字段是表格的列。每個模型/表應該有一個填充( - 命名)空白語句,其記錄/行是真正的語句。

Store(coords,...) // store [store] is at [coords] and ... 
Product(product,store,...) // product [product] is stocked by store [store] and ... 

由於產品具有存儲(S)爲manyToManyField它已經是產品和放養存儲「ProductStore」表和商店已經是商店和他們的座標的「StoreCoord」表。

您可以在查詢過濾器()中爲具有manyToManyField的模型提及任何對象的字段。

這種情況的SQL很簡單:

select p.product,distance 
    select p.product,distance(s.coord,[pnt]) as distance 
    from Store s join Product p 
    on s.store=p.store 
group by product 
having distance=min(distance) 
order by distance 

應該直截了當這個映射到query。但是,我對Django不夠熟悉,無法爲您提供確切的代碼。

from django.db.models import F 

q = Product.objects.all() 
    .filter(store__product=F('product')) 
    ... 
    .annotate(distance=Min('coord.distance([pnt])')) 
    ... 
    .order_by('distance') 

Min()是aggregation的示例。

您也可以通過明確製作subquery來獲得幫助。

也可以通過raw接口來查詢。但是,上述名稱對於Django原始查詢並不正確。例如,表名將默認爲APPL_store和APPL_product,其中APPL是您的應用程序名稱。此外,距離不是您的pointField操作員。您必須提供正確的距離功能。但是你不應該需要在原始級別進行查詢。

+0

感謝您的回答。我在一年前發佈了這篇文章,很遺憾我沒有訪問代碼或者測試這個方法的方法。如果有人發現它適用於他們,我會將其標記爲已回答。 – manuel 2014-06-30 11:35:52

+0

瞭解。 ([Vikas Gulati]最近發佈了一份賞金(https://stackoverflow.com/users/1283546/vikas-gulati)。) – philipxy 2014-06-30 18:36:45