如何在Django的"F"类型上执行算术运算?

3

我有一个事件模型,仅包含一个纬度和经度的FloatField()字段,为了简单起见。

从Python本地的“geopy.distance”库中,您可以通过以下方式获取两个坐标之间的距离:

from geopy.distance import distance
home = (33.28473, -117.20384)
event = (33.829384, -117.38932)
distance = distance(home, event).miles  #returns a float 

在我的应用上下文中,我需要按位置对所有事件进行排序。我尝试了标准的 Django 注释语法:
Event.objects.all().annotate(distance=distance( (F('lat'),F('lon')), home ).miles).order_by('distance')

但是我从geopy库得到以下错误:

TypeError: float() argument must be a string or a number, not 'F'

奇怪的是,这个可以正常工作:

>>> def foo(x):
        return x * 2
>>> Event.objects.all().annotate(cutomField=foo(F('field')))

看起来你可以使用F类型进行算术运算,但geopy似乎不知道这一点。

有没有办法将F类型转换为浮点数,以便geopy可以处理该字段?


你使用哪个数据库?Django对PostgreSQL + PostGIS + GeoDjango有良好的支持。 - Iain Shelvington
@IainShelvington MySQL - Rage
@IainShelvington 计算两个坐标之间的距离不被视为基本算术吗? - Rage
不,不是这样的。显然,Django现在支持MySQL地理空间后端 https://docs.djangoproject.com/en/3.0/ref/contrib/gis/db-api/#geodjango-database-api - Iain Shelvington
@IainShelvington,这对我的需求来说似乎太过繁琐了。我通过使用两点之间的笛卡尔公式(基本算术)解决了我的问题。我只需要一个相对距离排序,考虑到我的坐标范围在美国而不是靠近极地的任何地方,这就足够了。 - Rage
显示剩余5条评论
1个回答

1
Django支持在查询表达式中使用Python常量、变量甚至其他表达式进行取反、加、减、乘、除、模数算术和幂运算。
F()对象代表模型字段或注释列的值。它使得可以引用模型字段的值并且执行数据库操作,而无需将它们从数据库提取到Python内存中。
相反,Django使用F()对象在数据库层面生成描述所需操作的SQL表达式。
以上详细信息来自Django官方文档链接
def foo(x):
    return x*2

qs = MyModel.objects.filter().annotate(res=foo(F('colN')))
print(qs.query)

输出:

从“model_table”中选择col1、col2和(“model_table”。“colN” * 2)作为“res”

例如2(考虑另一个函数)。

def foo_power(x):
    return x**2 ## POWER of 2

qs = MyModel.objects.filter().annotate(res=foo_power(F('colN')))
print(qs.query)

输出:

SELECT col1, col2, (POWER("music_movie"."title",2)) AS "res" FROM "model_table"

这表明计算发生在数据库端:

对于geopy.distance情况:

对于自定义设置计算,我们需要编写自己的Func,并借助django内置的数据库函数进行操作。可以参考https://docs.djangoproject.com/en/3.0/ref/models/expressions/#func-expressions

有没有办法将F类型转换为浮点数,以便geopy可以处理该字段?

没有。您无法将F()即<class 'django.db.models.expressions.F'>强制转换为浮点数。

因为:

def foo_power(x):
    print(type(x), x)
    return x**2 ## POWER of 2

qs = MyModel.objects.filter().annotate(res=foo_power(F('colN')))

输出: <class 'django.db.models.expressions.F'> F(标题)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接