You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Django ORM中使用annotate()在MariaDB运行Haversine公式报错如何解决

问题原因

  1. 函数未返回构造好的QuerySet,且QuerySet惰性求值特性导致直接打印仅输出内存地址:你当前编写的函数最后一行仅构造了带annotate的QuerySet,没有return语句返回该对象;同时Django的QuerySet默认不会立即执行SQL,直接打印只会输出对象的内存地址,需要主动触发求值(如转列表、遍历、调用first()等)才会执行查询拿到计算结果。
  2. 字面量参数未用Value包裹:site.lat、site.lon是Python Decimal类型的字面量,直接传入数据库函数Radians会被Django识别为数据库字段名而非固定值,导致生成的SQL语句逻辑错误,必须用Value()包裹后再传入。
  3. 幂运算写法错误:Django ORM表达式不支持直接用**运算符做幂运算,你代码中的Sin(...)**2写法无法生成正确的SQL,需要使用Pow()数据库函数实现幂运算。
  4. 可选检查:你给出的MeteorologicalSite模型定义中无radiation字段,若实际模型未定义该字段,filter(radiation=True)会直接抛出字段不存在的错误,可先核对模型定义。
  5. 类型适配问题:经纬度字段为Decimal类型,而MariaDB三角函数返回值为浮点类型,未做类型转换可能导致运算异常,建议将最终计算结果显式转换为统一类型。

修正后的代码示例

from django.db.models import F, Value, FloatField
from django.db.models.functions import Cos, Sin, ASin, Sqrt, Radians, Pow, Cast

# 如果该函数不是类方法可以去掉self参数
def haversine_formula(site):
    # 字面量用Value包裹,指定字段类型适配运算
    lat1 = Radians(Value(site.lat, output_field=FloatField()))
    lon1 = Radians(Value(site.lon, output_field=FloatField()))
    lat2 = Radians(F("lat"))
    lon2 = Radians(F("lon"))
    r = 6372.8

    sql_haversine_formula = Cast(
        2 * r * ASin(
            Sqrt(
                Pow(Sin((lat1 - lat2) / 2), 2) +
                Cos(lat1) * Cos(lat2) * Pow(Sin((lon1 - lon2) / 2), 2)
            )
        ),
        output_field=FloatField()
    )

    # 构造QuerySet并返回,确认你的MeteorologicalSite实际存在radiation字段
    queryset = MeteorologicalSite.objects.filter(radiation=True).annotate(
        distance=sql_haversine_formula
    )
    return queryset

结果获取方式

调用函数后主动触发QuerySet求值即可拿到计算结果:

# 示例:传入你的Site实例获取所有气象站点的距离
target_site = Site.objects.get(id=xxx)
result = haversine_formula(target_site)
# 遍历获取每个站点的计算距离
for site_obj in result:
    print(site_obj.distance)
# 直接提取所有距离值的列表
distance_list = list(result.values_list('distance', flat=True))

内容的提问来源于stack exchange,提问作者Saarsa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 19:36:03