具有距离值的GeoDjango距离滤波器存储在模型中 - 查询

Dan*_*ezo 14 python django geodjango

我有一个Order模型,它有一个originPointField和一个rangeIntegerField.此外,还有一个UserProfile模型,它有一个geo_locationPointField.现在,我有一个User实例,user.我想选择所有Orders,其之间的距离Order.origin,并user.userprofile.geo_location在小于该值(米)的Order.range示范田.

再次简化模型:

class Order(models.Model):
    origin = models.PointField()
    range = models.IntegerField(blank=True, default=10000)

class UserProfile(models.Model):
    geo_location = models.PointField()
Run Code Online (Sandbox Code Playgroud)

我有这个工作(静态通过距离):

>>> Order.objects.filter(origin__distance_lte=(user.profile.geo_location, D(m=3000)))
Run Code Online (Sandbox Code Playgroud)

我的下一个(不成功)尝试是使用F()表达式来使用Order.range字段中的值:

>>> Order.objects.filter(origin__distance_lte=(user.profile.geo_location, D(m=F('range'))))
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/Users/danger/devel/.virtualenvs/proj/lib/python2.7/site-packages/django/contrib/gis/measure.py", line 165, in __init__
self.m, self._default_unit = self.default_units(kwargs)
  File "/Users/danger/devel/.virtualenvs/proj/lib/python2.7/site-packages/django/contrib/gis/measure.py", line 49, in default_units
if not isinstance(value, float): value = float(value)
 TypeError: float() argument must be a string or a number
Run Code Online (Sandbox Code Playgroud)

我认为问题是D()不是懒散地运行 - 我能理解.所以我试着从range我应该工作的字段(整数)中获取原始值,但是:

>>> Order.objects.filter(origin__distance_lte=(user.profile.geo_location, F('range')))
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/Users/danger/devel/.virtualenvs/proj/lib/python2.7/site-packages/django/db/models/query.py", line 69, in __repr__
data = list(self[:REPR_OUTPUT_SIZE + 1])
File "/Users/danger/devel/.virtualenvs/proj/lib/python2.7/site-packages/django/db/models/query.py", line 84, in __len__
self._result_cache.extend(self._iter)
File "/Users/danger/devel/.virtualenvs/proj/lib/python2.7/site-packages/django/db/models/query.py", line 273, in iterator
for row in compiler.results_iter():
File "/Users/danger/devel/.virtualenvs/proj/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 680, in results_iter
for rows in self.execute_sql(MULTI):
File "/Users/danger/devel/.virtualenvs/proj/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 725, in execute_sql
sql, params = self.as_sql()
File "/Users/danger/devel/.virtualenvs/proj/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 68, in as_sql
where, w_params = self.query.where.as_sql(qn=qn, connection=self.connection)
File "/Users/danger/devel/.virtualenvs/proj/lib/python2.7/site-packages/django/db/models/sql/where.py", line 92, in as_sql
sql, params = child.as_sql(qn=qn, connection=connection)
File "/Users/danger/devel/.virtualenvs/proj/lib/python2.7/site-packages/django/db/models/sql/where.py", line 95, in as_sql
sql, params = self.make_atom(child, qn, connection)
File "/Users/danger/devel/.virtualenvs/proj/lib/python2.7/site-packages/django/contrib/gis/db/models/sql/where.py", line 47, in make_atom
spatial_sql = connection.ops.spatial_lookup_sql(data, lookup_type, params_or_value, lvalue.field, qn)
File "/Users/danger/devel/.virtualenvs/proj/lib/python2.7/site-packages/django/contrib/gis/db/backends/postgis/operations.py", line 531, in spatial_lookup_sql
raise ValueError('Argument type should be %s, got %s instead.' % (arg_type, type(value[1])))
ValueError: Argument type should be (<class 'decimal.Decimal'>, <class 'django.contrib.gis.measure.Distance'>, <type 'float'>, <type 'int'>, <type 'long'>), got <class 'django.db.models.expressions.F'> instead.
Run Code Online (Sandbox Code Playgroud)

那么我怎样才能完成我想要的目标呢?任何帮助表示赞赏!

Jam*_*ett 8

我认为你将不得不放弃一些SQ​​L来做你想做的事情,因为GeoDjango助手没有办法让你制作一个Distance同时是Django F对象的合适对象(即字段查找).您没有说明您正在使用哪个数据库,但这是您使用PostGIS执行此操作的方式:

Order.objects.all().extra(
    where=['ST_Distance(origin, ST_PointFromText(%s, 4326)) <= CAST(range AS double precision) / 1000'],
    params=[user.profile.geo_location.wkt]
)
Run Code Online (Sandbox Code Playgroud)

让我们解释一下这里发生了什么,因为它非常棘手.从左边开始:

  • .extra()允许您在查询中添加额外的字段和限制; 我们在这里添加一个限制
  • ST_Distance()是GeoDjango distance_lte操作员转换成的PostGIS函数(来自Django文档)
  • ST_PointFromText() 从WKT语法转换为PostGIS Geometry对象
  • 4326Django的默认SRID,但不适用于PostGIS,所以我们必须指定它
  • CAST因为你使用了一个整数,我们必须你的字段加倍精度
  • 那么我们必须除以1000来将米转换为千米,我相信这就是我们所需要的
  • GeoDjango Point字段有一个访问器.wkt,它提供了我们在ST_PointFromText()调用之前需要的WKT表示

请注意,根据PostGIS文档,ST_Distance()不使用索引,因此您可能希望使用ST_DWithin()而不是调查(之后记录ST_Distance()).