地址定位

Odoo 地址自动定位到底做了什么:base_geolocalize 的经纬度补全、服务调用与失败回退讲透

结合 base_geolocalize 源码,讲清 Odoo 如何把联系人地址组装成查询字符串、如何选择 OpenStreetMap 或 Google、为什么地址一改就清空旧坐标,以及失败时为什么只发提醒不强写坐标。

其他 框架
进阶 开发者 1 分钟阅读
0 评论 0 点赞 0 收藏 10 阅读

很多人把 Odoo 的地址定位理解成一个按钮:

  • 点一下;
  • 调一个地图接口;
  • 回来两串经纬度。

/home/ubuntu/odoo-temp/addons/base_geolocalize 这套实现,其实比“调 API”多了好几层边界控制。

它真正关心的是:

什么时候该查、查什么地址、查失败怎么办,以及旧坐标什么时候必须作废。

一、最重要的安全动作,反而发生在 write()

res_partner.write() 一上来就做了一件很值钱的事:

如果你改了这些地址字段:

  • street
  • zip
  • city
  • state_id
  • country_id

但又没有同时显式带上新的 partner_latitude / partner_longitude,Odoo 会把原坐标清零。

这一步特别关键,因为它体现了一个很清晰的原则:

旧地址对应的旧坐标,一旦地址变了,就不再可信。

很多系统的坑就在这里:地址改了,地图坐标却还挂着老值,最后附近搜索、路线、地图标点全错。

Odoo 在这里选择“宁可清空,也别错着留”。

二、真正的查找流程分成“完整地址”与“降级地址”两轮

_geo_localize() 的流程并不复杂,但很像有经验的人工检索:

第一轮:尽量用完整地址查

先把:

  • street
  • zip
  • city
  • state
  • country

拼成查询串,交给 geocoder。

第二轮:完整地址没命中,再降级到城市级别

如果第一轮没找到,Odoo 会退一步,只拿:

  • city
  • state
  • country

再查一次。

这说明框架作者知道真实世界地址很脏:

  • 门牌不标准;
  • 街道写法不一致;
  • 邮编不完整;
  • 州/省翻译混乱。

所以它不把“完整地址没命中”直接判死,而是再给一个更宽松的 fallback。

三、provider 不是写死的,而是配置驱动

base.geocoder._get_provider() 会先看系统参数:

  • base_geolocalize.geo_provider

如果没配或配错,再回退到数据库里第一条 provider 记录。

这意味着 geolocation 不是硬编码某家服务,而是:

  • 服务可换;
  • 配置可以后补;
  • 数据结构先抽象成 provider,再谈具体 API。

从扩展设计看,这是很标准的平台做法。

四、默认 OpenStreetMap,Google 是显式收费路线

OpenStreetMap / Nominatim

默认链路走 _call_openstreetmap()

  • https://nominatim.openstreetmap.org/search
  • 带上 Odoo 的 User-Agent
  • 返回第一条命中结果
  • 转成 (lat, lon)

这个默认链路的优点是开箱即用,但它也暗示:

  • 结果质量依赖外部服务;
  • 查询文本质量很重要;
  • 命中不稳定时必须接受 None。

Google Maps

_call_googlemap() 的态度更直接:

  • 没 API key 就抛 UserError
  • 即便有 key,Google 状态不是 OK 也会报配置/计费问题
  • 还会在 force_country 存在时加国家组件约束

也就是说,Google 不是“自动兜底更强服务”,而是一条明确需要付费配置和运维意识的路线。

五、为什么 geo_localize() 会主动跳过导入、测试和 registry 未就绪场景

geo_localize() 里有一个非常容易被忽略的门:

如果没有 force_geo_localize,遇到这些场景会直接返回 False:

  • import_file
  • 当前在测试中
  • registry 还没 ready

这其实非常合理。

因为 geolocation 是外部调用,天然不稳定、不可预测、还会拖慢流程。

如果在:

  • 批量导入联系人;
  • 测试环境;
  • 系统初始化阶段;

都默认强行打外部 API,系统会非常脆。

所以 Odoo 选择:

把地址定位当成增强动作,而不是基础事务的阻塞步骤。

六、失败时为什么只提醒,不写“猜的坐标”

geo_localize() 查不到结果时,并不会写一个模糊坐标,也不会偷偷沿用旧值。

它做的是:

  • 把失败 partner 收集起来;
  • 给当前用户发一个 simple_notification danger 提醒;
  • 告诉你哪些地址没匹配上。

这套处理很克制,也很对。

因为 geolocation 这件事最怕“看起来成功,实际上错了”。

错坐标比没坐标更危险。

七、反向定位 _get_localisation() 也不是只依赖地图 API

base_geocoder._get_localisation() 先尝试:

  • request.geoip.city.name
  • request.geoip.country_code

只有拿不到时,才调用 OpenStreetMap reverse geocoding。

这说明 Odoo 在做反向地址展示时,也会优先利用已有请求上下文,而不是每次都打外部服务。

换句话说,作者很清楚:

  • 已知上下文最便宜;
  • 外部 API 最贵也最不稳;
  • 能少打一层就少打一层。

八、测试用例暴露了两个关键边界

test_geolocalize.py 至少说明两件事:

1)Google 没 key 就不该“悄悄失败”

这里明确期待抛 UserError,而不是静默吞掉。

2)失败提醒是正式产品行为,不是调试日志

测试会 mock bus 发送,确认用户真能收到“哪些地址没匹配上”的提醒。

这代表产品层面对 geolocation 的定位是:

  • 可以失败;
  • 但失败要可见;
  • 且不能污染主数据。

九、实战里最该注意什么

1)改地址后坐标被清空,不是 bug

那是为了避免旧坐标假装还有效。

2)批量导入不要默认期待即时定位

源码本来就倾向跳过这类场景。

3)Google 不只是“填个 key”

它还意味着账单、API 启用状态和错误处理成本。

4)查不到时先怀疑地址质量

很多问题不在 geocoder,而在:

  • 街道写法不规范;
  • 国家/州名语言不统一;
  • 地址字段本来就不完整。

总结

base_geolocalize 真正做的不是“地图集成”这么简单。

它在源码里明确处理了四个问题:

  • 地址变更后旧坐标怎么失效;
  • 查询时先完整查、再降级查;
  • provider 怎么配置与切换;
  • 查不到时如何提醒而不污染数据。

如果只记一句,可以记这句:

Odoo 的地址定位设计重点不是“尽量写出坐标”,而是“只在结果足够可信时才把坐标写回联系人”。

DISCUSSION

评论区

想参与讨论?先 登录 再发表评论。
还没有评论,你可以成为第一个留言的人。