很多导入问题,表面上看像“模型写不进去”,实际上卡在更前面的地方:
- 文件格式没被正确识别;
- 列头没匹配到字段;
- 日期、金额、二进制内容没解析对;
- 多列映射到同一个字段时被合并了;
- 预校验就已经失败了。
base_import 的源码很适合拿来拆这种问题,因为它把“把表格变成记录”分成了非常清晰的几段。
第一步不是写数据库,而是读文件
_read_file() 会先根据文件内容、用户传入的类型、文件扩展名,去判断到底该走 CSV、XLS、XLSX 还是 ODS。
这一步的重要性常被低估。
因为很多导入失败,根本不是 ORM 的错,而是:
- 文件被错误识别成别的格式;
- 分隔符不对;
- 语言环境导致日期/浮点解析异常。
所以你看到“导入报错”,第一反应不该是怀疑 model.load(),而要先看读取阶段。
parse_preview() 负责“先看一眼再决定怎么导”
parse_preview() 做的事情很多,但核心目标很简单:
- 读出前几行;
- 识别表头;
- 推断列类型;
- 给出字段映射建议;
- 展示样例值;
- 判断是否需要 advanced mode。
它甚至会利用前几行数据来猜:
- 这列像日期;
- 那列像金额;
- 这个表头是不是和字段名很接近。
也就是说,导入 UI 里那些“自动帮你选字段”的体验,背后不是魔法,而是预览和匹配逻辑。
字段映射不是硬匹配,而是逐层猜测
get_fields_tree() 会先把目标模型能导入的字段树整理出来,然后 _get_mapping_suggestions() 再根据:
- 之前保存过的 mapping;
- 字段技术名;
- 字段显示名;
- 语言翻译后的标签;
- 模糊匹配距离;
去推测最可能的对应关系。
这解释了一个很实用的现象:
同样一张表,在不同语言环境下,系统的字段建议可能不一样。
_convert_import_data() 只是先把“要导的列”剪出来
这一步做的事非常朴素:
- 找到被选中的列;
- 去掉没选的列;
- 检查每行长度是否一致;
- 按需跳过表头。
如果这里报错,常见原因往往是:
- 分隔符错了;
- 行长不一致;
- 你以为的“空列”其实被文件解析器读成了别的东西。
真正的类型处理发生在 _parse_import_data()
这里会递归处理字段树,重点包括:
date/datetime:按格式解析;float/monetary:处理千分位、小数点、币种符号、括号负数;binary:支持 URL 或 base64;- 关系字段:继续递归进入子模型。
这也是为什么导入时经常会报出很具体的“某列第几行有问题”。
因为 Odoo 不是等到最后才知道不对,而是在预处理阶段就尽量把错误拦下来。
execute_import() 才是进入 ORM 的真正入口
前面的步骤都只是准备数据。
到了 execute_import(),才会:
- 开 savepoint;
- 转换数据;
- 解析日期和浮点;
- 处理多列映射;
- 处理 fallback values;
- 调
model.load()。
这里最关键的是 savepoint。
它让测试导入和真实导入都能在失败时回滚,不会把半成品脏数据留在库里。
为什么多列映射经常让人困惑
_handle_multi_mapping() 会把多个列合并到同一个字段,例如:
- char 用空格拼接;
- text 用换行拼接;
- many2many 用逗号语义处理。
所以当你看到“我明明导了两列,最后却只进来一列”,很多时候不是丢数据,而是 系统按规则把它们合并了。
导入失败时,最值得先查的 5 件事
- 文件类型有没有被正确识别;
- 列头和字段映射是否合理;
- 日期格式是否和当前导入设置一致;
- 浮点里有没有千分位或币种符号;
- 是否有多列映射被合并,造成你以为的“丢列”。
一句话总结
base_import 不是“把 Excel 一把扔进 ORM”那么简单,而是:
先读文件,再做预览和字段匹配,接着做类型清洗与多列合并,最后才通过
model.load()把数据写进数据库。
DISCUSSION
评论区