2013-03-18 56 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