GeoDjango中的多对多表注释

时间:2014-10-10 23:32:01

标签: django annotations postgis geodjango

我正在尝试查询与Foo具有多对多关系的模型Address,其中地址将在距离给定点的指定距离内,并按升序距离对结果进行排序。看起来像注释就可以做到这一点但是我无法弄清楚如何在GeoDjango中做到这一点,因为它不支持地理注释。

这是我的基本模型结构:

# app name is bar
from django.contrib.gis.db import models

class Location(models.Model):
    latlon = Models.PointFields(spatial_index=True)
    # other fields ommitted
    objects = models.GeoManager()

class Address(models.Model):
    latlon = models.PointField(spatial_index=True)
    # other fields omitted
    objects = models.GeoManager()

class Foo(models.Model):
    addresses = models.ManyToManyField(Address)
    # other fields omitted
    objects = models.GeoManager()

使用上述模型,我能够构建一个查询,选择所有Foo对象,这些对象的地址距离特定点的特定距离。例如:

from django.contrib.gis.geos import Point
from django.contrib.gis.measure import Distance

new_york = Point(-73.98497, 40.75813)  # == Location.latlon

Foo.objects.filter(addresses__latlon__distance_lte=(new_york, Distance(mi=20)))

生成类似以下内容的查询:

SELECT
  "bar_foo"."id",
  ...
FROM "bar_foo"
  INNER JOIN "bar_foo_address"
    ON ("bar_foo"."id" = "bar_foo_address"."foo_id")
  INNER JOIN "bar_address"
    ON ("bar_foo_address"."address_id" = "bar_address"."id")
WHERE (ST_distance_sphere("bar_address"."latlon",ST_GeomFromEWKB(
           '\x0101000020e6100000aaf1d24d628052c096218e75715b4440' :: BYTEA)) <= 32186.88)

这很有效,除非我遇到麻烦,如果我想根据他们与给定点的距离对所有foos进行排序。我试过像:

(Foo
 .objects
 .filter(addresses__latlon__distance_lte=(new_york, Distance(mi=20)))
 .distance(Location.latlon)
 .order_by('distance'))

# produces

TypeError: ST_Distance output only available on GeometryFields.

当我阅读一些源代码时,我试图修改查询但仍然出现错误:

(Foo
 .objects
 .filter(addresses__latlon__distance_lte=(new_york, Distance(mi=20)))
 .distance(Location.latlon)
 .order_by('distance', field_name='addresses_latlon'))

# produces

ValueError: <django.contrib.gis.db.models.fields.PointField: latlon> not in self.query.related_select_cols

我想这与AddressFoo具有多对多关系的事实有关。不幸的是,GeoDjango不支持常规注释,所以我不能做类似的事情:

# hypothetical syntax
(Foo
 .objects
 .annotate(distance=DistanceAnnotation('addresses__latlon', new_york, unit='mi'))
 .filter(distance__lte=20)
 .order_by('distance'))

# which would generate

SELECT
  "bar_foo"."id",
  (ST_distance_sphere("bar_address"."latlon",ST_GeomFromEWKB(
           '\x0101000020e6100000aaf1d24d628052c096218e75715b4440' :: BYTEA)) as distance,
  ...
FROM "bar_foo"
  INNER JOIN "bar_foo_address"
    ON ("bar_foo"."id" = "bar_foo_address"."foo_id")
  INNER JOIN "bar_address"
    ON ("bar_foo_address"."address_id" = "bar_address"."id")
WHERE distance <= 32186.88)
ORDER BY distance ASC

所以问题是如何使用现有的API进行常规注释?或者也许其他一些方法我可以达到预期的效果?

0 个答案:

没有答案