2013-03-18 6 views
8

私はこの2つのモデルを持っています。関連する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を作成する必要がありますか? –

+0

@JoeJ私は可能な答えとして投稿しますが、私はそれが好きではありません。おそらく未処理のSQLは機能するかもしれませんが、私は空間問合せとそれほど混乱しません。答えをチェックして、あなたの考えを見てください。 – manuel

+4

商品は 'ManyToMany'から' Store'までありますが、 'PointField'を持つ店舗です。製品は1つ以上の店舗に置くことができます... **店舗数が1店舗以上の商品の距離ですか**?より高いです ?すべて ? – AlvaroAV

答えて

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

効率的なソリューションを見つけましたか? –

+0

@VikasGulati&manuel:質問にコメントを繰り返します(VikasGulatiが見たことがないかもしれません):「製品からポイントまでの距離」とは何ですか? (おそらく、その製品の店舗から店舗までの最短距離)。出力は正確に何ですか? (おそらく、すべての製品の(製品、距離)のリストが距離の昇順でソートされています) – philipxy

+0

@philipxy:はい距離は最小です。同様の問題で達成しようとしていたアウトプットは、計算された最小距離でソートされた製品リストでした+それぞれの製品は、関連するストアをあらかじめ距離別にソートしておく必要があります。 –

1

ちょっと考えてみてください。おそらくこれがうまくいくでしょう。これは(プリフェッチの仕組みのせいで)2つのデータベースクエリしか必要としません。それが動作しない場合、私はそれを試していないが、厳しく判断してはいけない:

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

あなたの答えをありがとう。私は1年以上前にこれを投稿しましたが、残念ながら私はこのコードにアクセスできず、これをもうテストする方法もありません。誰かが彼らのために働くことがわかったら、私はそれを答えとしてマークします。 – manuel

0

を私がする「点までの製品からの距離」を取りますポイントからその製品の店までの最短距離。私は、距離を昇順でソートしたすべての製品の出力を(製品、距離)のリストにします。 (賞金を払った人のコメントは、距離によってソートされた後、商品内に保管されることを望んでいることを示す)

すべてのモデルには対応する表があります。モデルのフィールドは、テーブルの列です。すべてのモデル/テーブルは、レコード/行が真のステートメントを作成するものである場合には、(名前付き)記入欄を持つ必要があります。

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

商品の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') 

aggregationの例です。

明示的にsubqueryとすることによっても助けられます。

rawインターフェイスでこれを照会することもできます。しかし、上記の名前はDjangoの生のクエリには適切ではありません。たとえば、テーブル名はデフォルトでAPPL_storeとAPPL_productになります。ここで、APPLはアプリケーション名です。また、distanceはあなたのpointField演算子ではありません。正しい距離を指定する必要があります。しかし、生のレベルで照会する必要はありません。

+0

あなたの答えをありがとう。私は1年以上前にこれを投稿しましたが、残念ながら私はこのコードにアクセスできず、これをもうテストする方法もありません。誰かが彼らのために働くことがわかったら、私はそれを答えとしてマークします。 – manuel

+0

を理解してください。 (賞金は最近[Vikas Gulati](https://stackoverflow.com/users/1283546/vikas-gulati)によって投稿されました。) – philipxy

関連する問題