本文介绍了Django的Distance函数未返回Distance对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是使用@Nargiza解决此问题时出现的意外行为:。

This is an unexpected behavior that came up on the road to solve this problem with @Nargiza: 3d distance calculations with GeoDjango.

在 函数:

并从 对象,我们可以获取每个。

And from a Distance object, we can get the distance in each and every one of the supported units.

但是:

让模型为:

class MyModel(models.Model):
    ...
    coordinates = models.PointField()

然后执行以下操作:

p1 = MyModel.objects.get(id=1).coordinates
p2 = MyModel.objects.get(id=2).coordinates
d = Distance(p1, p2) # This is the function call,
                     # so d should be a Distance object
print d.m

应打印 p1 p2

should print the distance between p1 and p2 in meters.

相反,我们收到以下错误:

Instead, we got the following error:

AttributeError: 'Distance' object has no attribute 'm'

我们最终找到了解决方法( code> d =距离(m = p1.distance(p2))),但问题仍然存在:

We found a workaround eventually (d = Distance(m=p1.distance(p2))) but the question remains:

为什么距离 function 没有返回 Distance object ?

这是一个错误,还是我们错过了

Why the Distance function didn't return a Distance object?
Is this a bug, or we missed something?

提前感谢您的时间。

推荐答案

那些两者之间并没有那么紧密的联系。文档确实确实说它返回了距离对象,但是还有一个额外的步骤:

Those two aren't that closely related to each other. Docs do indeed say that it "returns" Distance object, but there is an extra step:

django.contrib.gis。 db.models .functions.Distance database 函数,它接受两个表达式(可能包含数据库字段名称)并返回对象,可用作查询的一部分。

django.contrib.gis.db.models.functions.Distance is a database function, it takes two expressions (that may include database field names) and returns a Func object that can be used as a part of a query.

简单地说,它需要在数据库中执行。它将使用数据库函数(例如)计算距离,然后将其作为一个 django.contrib.gis.measure.Distance 对象。

So simply put, it needs to be executed in a database. It will calculate distance using database function (e.g. postgis ST_Distance) and then bring it back as a django.contrib.gis.measure.Distance object.

因此,除非有人想弄乱SQL编译器和数据库连接,否则这是最简单的方法得到两点之间的距离是 Distance(m = p1.distance(p2))

So, unless one wants to mess with SQL compilers and db connections, easiest way to get Distance between two points is Distance(m=p1.distance(p2))

编辑:一些代码来说明这一点:

Some code to illustrate the point:

您可以在。它很小,很容易理解。它所做的就是为您提供一种方便的方法来进行距离转换,比较和算术运算:

You can check out code for Distance (measurement) class in django/contrib/gis/measure.py. It's fairly small and easy to understand. All it's doing is giving you a convenient way to do conversion, comparison and arithmetic operations with distances:

In [1]: from django.contrib.gis.measure import Distance

In [2]: d1 = Distance(mi=10)

In [3]: d2 = Distance(km=15)

In [4]: d1 > d2
Out[4]: True

In [5]: d1 + d2
Out[5]: Distance(mi=19.32056788356001)

In [6]: _.km
Out[6]: 31.09344

现在,让我们看一下Distance函数:

Now, let's take a look at the Distance function:

在模型中添加 __ str __ 方法,以便我们可以在看到模型时看到距离值由queryset api和db_table短名称返回,因此我们可以在查询中查找:

Add __str__ method to the model so we can see distance value when it returned by the queryset api and short db_table name so we can look in the queries:

class MyModel(models.Model):
    coordinates = models.PointField()

    class Meta:
        db_table = 'mymodel'

    def __str__(self):
        return f"{self.coordinates} {getattr(self, 'distance', '')}"

创建某些对象并执行简单的 select * from 查询:

Create some objects and do a simple select * from query:

In [7]: from gisexperiments.models import MyModel

In [8]: from django.contrib.gis.geos import Point

In [10]: some_places = MyModel.objects.bulk_create(
    ...:     MyModel(coordinates=Point(i, i, srid=4326)) for i in range(1, 5)
    ...: )

In [11]: MyModel.objects.all()
Out[11]: <QuerySet [<MyModel: SRID=4326;POINT (1 1) >, <MyModel: SRID=4326;POINT (2 2) >, <MyModel: SRID=4326;POINT (3 3) >, <MyModel: SRID=4326;POINT (4 4) >]>

In [12]: str(MyModel.objects.all().query)
Out[12]: 'SELECT "mymodel"."id", "mymodel"."coordinates" FROM "mymodel"'

无聊。让我们使用距离函数将距离值添加到结果中:

Boring. Let's use Distance function to add distance value to the result:

In [14]: from django.contrib.gis.db.models.functions import Distance

In [15]: from django.contrib.gis.measure import D  # an alias

In [16]: q = MyModel.objects.annotate(dist=Distance('coordinates', origin))

In [17]: list(q)
Out[17]:
[<MyModel: SRID=4326;POINT (1 1) 157249.597768505 m>,
 <MyModel: SRID=4326;POINT (2 2) 314475.238061007 m>,
 <MyModel: SRID=4326;POINT (3 3) 471652.937856715 m>,
 <MyModel: SRID=4326;POINT (4 4) 628758.663018087 m>]

In [18]: str(q.query)
Out[18]: 'SELECT "mymodel"."id", "mymodel"."coordinates", ST_distance_sphere("mymodel"."coordinates", ST_GeomFromEWKB(\'\\001\\001\\000\\000 \\346\\020\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\'::bytea)) AS "distance" FROM "mymodel"'

您可以看到它使用 ST_distance_sphere sql函数使用值来计算距离从 mymodel。坐标 和我们的起源点的字节表示。

You can see it uses ST_distance_sphere sql function do calculate distance using values from "mymodel"."coordinates" and byte representation of our origin point.

我们现在可以将其用于数据库管理系统中的过滤和排序以及许多其他事情(快速):

We can now use it for filtering and ordering and lots of other things, all inside the database management system (fast):

In [19]: q = q.filter(distance__lt=D(km=400).m)

In [20]: list(q)
Out[20]:
[<MyModel: SRID=4326;POINT (1 1) 157249.597768505 m>,
 <MyModel: SRID=4326;POINT (2 2) 314475.238061007 m>]

注意 .m ,您需要将浮点数传递给过滤器,它将无法识别距离对象。

Notice .m you need to pass float number to the filter, it won't be able to recognize Distance object.

这篇关于Django的Distance函数未返回Distance对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-11 20:14