Odoo 17中文数据字典生成实战:从ORM元数据到团队开发基线

发布时间:2026/10/22 12:16:52
Odoo 17中文数据字典生成实战:从ORM元数据到团队开发基线
简介为 Odoo 17 整理的中文数据字典面向企业信息化实施、ERP 二次开发及 Odoo 学习者帮助快速定位数据库表结构与字段含义减少在官方英文文档和源码中反复翻找的时间。整个资料压缩为一个 PDF 文件大小约 8.07MB便于下载后离线查阅和关键词搜索。内容由 Navicat 生成并翻译为中文目录按服务器、数据库、模式、表逐级展开覆盖 account_account、account_account_tag、account_account_account_journal_rel 等财务与基础资料模块的核心表每个表均列明字段名、数据类型、键约束及中文注释同时清晰呈现表间关联关系可直接用于二次开发、报表定制、权限梳理和运维排查。目前已有 133 人次学习下载适合需要系统理解 Odoo 17 数据模型的中高级开发人员、实施顾问也适合希望深入掌握 ERP 底层结构的爱好者学习时对照数据字典可快速理解各模块的数据流转提升开发与配置效率。1. 拿到「Odoo 17 数据字典已翻译为中文」后先确认它到底解决哪个问题当手头拿到一份「Odoo 17 数据字典已翻译为中文」先应确认它的用途Odoo 17 默认仓库里有上百个数据模型、几千个字段做二次开发、接口对接、数据迁移时查一个字段名经常要在界面、源码、数据库三处来回折腾。数据字典就是把模型、字段、类型、关联关系和中文标签整理成一份可检索清单让开发、实施、数据迁移三个角色在同一份元数据上对齐口径。它对三类人最有用写二次开发的工程师需要快速确认字段技术名、字段类型和继承关系实施顾问要把中文标签当作和业务方沟通的桥梁做数据迁移的要按字段类型、必填性和关联模型去生成映射表避免临上线才发现两边字段对不上。比文档本身更值得关注的是生成方式。生成方式决定了这份数据字典能不能和你当前的数据库对得上也决定了项目升级到 17.0 后续版本时能不能低成本重新生成一份。所以下面不讨论「这份文档怎么打开」而是完整地把「Odoo 17 中文数据字典是怎么产出来的、哪里有坑、怎么让它成为团队基线」讲清楚。2. Odoo 17 的数据字典从哪来ir.model、ir.model.fields 与字段类型2.1 模型的元数据仓库ir.model 与 ir.model.fieldsOdoo 的 ORM 有一个区别于普通 Web 框架的设计所有注册到系统的模型和字段都会在数据库里留下持久化的元数据记录。数据字典的原始来源就是这两张核心表对应的模型。ir.model每行对应一个模型里面存了模型的技术名model字段例如sale.order、显示名称name、是否 transient 模型、是否 abstract 模型等。ir.model.fields则是字段级的元数据仓库每个字段一行包含字段技术名name、字段标签field_description、字段类型ttype、关联模型relation、是否存储store、是否必填required等关键属性。这两张表在 Odoo 17 中依然存在而且数据量很可观。以一次典型实施为例安装销售、采购、库存、财务几个常用模块后ir.model里会有上千个模型ir.model.fields里会有几千到上万条字段记录。人肉去源码里翻字段定义完全不现实从这两张表出发做数据字典才是正路。提示ir.model 里既包含业务模型也包含 Odoo 平台内部的系统模型。生成数据字典时一般要过滤掉 transient 和 abstract 模型否则文档里会出现大量ir.*、base.*开头、普通业务人员根本用不到的系统表。2.2 字段类型与关联关系在数据字典里如何表达字段类型在 Odoo 17 里由ir.model.fields.ttype决定。做数据字典时需要把这层「内部类型」映射成文档里人能看懂的说明。常见类型如下ttype内部值数据字典里的表达说明char短文本单行字符串常见的有name、编号text长文本多行文本比如备注boolean布尔是/否integer整数数量、序号float浮点数金额以外的数值monetary金额常配合currency_id使用date / datetime日期 / 时间注意 date 不含时分秒binary附件/二进制文件、图片、电子签名html富文本邮件正文、说明selection枚举下拉选项值对标 keymany2one多对一关联指向另一张表如partner_id指向res.partnerone2many一对多关联例如订单行order_line属于订单many2many多对多关联例如标签tag_idsreference动态引用可指向任意模型jsonJSON 数据Odoo 17 中出现频率变高用于扩展属性对于关联类字段数据字典里必须给出relation也就是目标模型的技术名例如res.partner、product.product。只写「关联」不写目标表等于没写。另外selection字段的选项数据并不直接存在ir.model.fields上而是存放在另一张子表ir.model.fields.selection中每行对应一个选项。这也会是后面避坑环节里容易踩的地方。2.3 一份合格的中文数据字典至少要有哪几列在输出文档之前先把列设计好。做过几版数据字典之后我一般会保留下面这些列少了后面一定会回来补。列典型示例为什么需要模型技术名sale.order定位模型模型中文名销售订单文档可读性字段技术名x_studio_custom开发时真正用到的名字字段中文标签客户参考和业务方沟通字段类型many2one决定怎么取值关联模型res.partner关联查询、迁移用必填True / False校验数据完整性只读True / False界面行为存储True / False非存储字段不落库SQL 里查不到可翻译True / False判断多语言数据是否要额外处理归属模块sale快速知道字段来自哪个模块帮助说明help 字段原文字段语义补充这 12 列是底线。实际项目里我通常还会加一列「备注」留给实施人员填写业务叫法。注意字段标签不一定等于业务叫法例如技术名x_custom_code的标签可能是「自定义编号」但业务上叫「客户编码」。备注列就是用来沉淀这类差异的。2.4 三条生成路线怎么选源码、运行时、数据库做 Odoo 17 的中文数据字典基本有三条路线区别非常大。第一条是扫源码把插件目录里的 Python 模型定义和 XML 视图里的字段声明抓出来。优点是离线可用、不依赖数据库环境缺点也很明显Odoo 的字段大量来自继承和 mixin一个sale.order的完整字段列表分散在十几个模块里扫源码很容易漏而且 Studio 自定义字段、数据库里手工改过的结构在源码里根本看不到。第二条是查数据库直接用 SQL 查ir_model_fields表。速度快、能覆盖所有持久化字段但它拿到的是数据库里的原始值field_description是英文标签中文翻译要靠额外关联翻译表字段继承关系也不直观。第三条是从运行中的 ORM 读取用 Odoo shell 或自定义脚本调用fields_get()让 Odoo 自己把继承、动态 selection、翻译全部算好再输出。这也是我推荐的方式。它拿到的是运行时真实字段集合而且可以按登录语言拿到中文标签天然能应对 Odoo 的继承机制。后面第 3 章的脚本就是按第三条路线写的。3. 用 ORM 生成一份可用的中文数据字典脚本与参数3.1 生成前确认两件事中文语言包与翻译是否就位在写脚本前必须先确认数据库里已经启用了中文语言并且各个模块的中文翻译已经导入。否则脚本就算指定中文Odoo 也只会退回默认英文标签生成出来还是半中半英。常见做法是进入实例的 shell 环境先做一次语言检查python3 odoo-bin shell -d 你的数据库名 --addons-pathaddons然后在 shell 里执行langs env[res.lang].search([(code, , zh_CN)]) print(langs.mapped(active)) print(langs.mapped(is_instantiate))如果active为 False说明中文语言没有启用。最常见的补救方式是到「设置 → 语言→ 简体中文」手动激活并加载翻译也可以在启动时直接带参数python3 odoo-bin -d 你的数据库名 --load-languagezh_CN--load-languagezh_CN会在加载模块后自动安装中文语言并对已有模块导入翻译。这里要特别注意语言启用是第一步模块翻译导入是第二步。只启用语言、不导入翻译或者后来才新增模块而没导入该模块的中文翻译数据字典里一样会出现英文残留。提示自研模块如果没写i18n/zh_CN.po那这个模块里的字段标签永远不会有中文版。做 Odoo 17 项目时我一般把中英文翻译文件当作交付物的一部分和模块代码一起进版本库。3.2 最小脚本遍历模型输出 Markdown 数据字典确认翻译就位后就可以在 Odoo shell 里直接跑生成脚本。下面是一个最小可用的 Markdown 版本。它遍历非 transient、非 abstract 的模型对每个模型调用fields_get()并把字段信息拼成 Markdown 表格。# 在 odoo shell 中执行生成 markdown 格式的数据字典 MODEL_DOMAIN [ (transient, , False), (is_abstract, , False), ] def dump_md(env, output_pathodoo17_data_dict.md): models env[ir.model].search(MODEL_DOMAIN, ordermodel) lines [] for m in models: title f## {m.name}{m.model} lines.append(title) lines.append() lines.append(| 字段技术名 | 中文标签 | 类型 | 关联模型 | 必填 | 存储 |) lines.append(| --- | --- | --- | --- | --- | --- |) fields env[m.model].with_context(langzh_CN).fields_get() for fname, attrs in fields.items(): relation attrs.get(relation) or label attrs.get(string) or ftype attrs.get(type) or required 是 if attrs.get(required) else 否 store 是 if attrs.get(store) else 否 lines.append( f| {fname} | {label} | {ftype} | {relation} f| {required} | {store} | ) lines.append() with open(output_path, w, encodingutf-8) as f: f.write(\n.join(lines)) print(done) dump_md(env)这段脚本的逻辑是先查ir.model拿到模型列表再对每个模型调用fields_get()。fields_get()返回的是一个字段技术名到属性字典的映射比直接查数据库多算好了继承字段和动态 selection同时with_context(langzh_CN)保证拿到的string和selectionlabel 是中文。参数说明MODEL_DOMAIN里的transient和is_abstract用来排除向导类模型和抽象基类ordermodel让输出按模型技术名字母排序方便 diff。fields_get()用来取字段它会合并当前模型继承到的所有字段这正是它比扫源码更精确的原因。3.3 必须按项目调整的几个生成参数上面只是最小脚本实际项目中我会按需要调整几个地方。第一个是模型范围。有些项目只需要输出销售、采购、库存几个核心模块有些则要全量。如果是全量建议在脚本里把模型做的再多一层过滤直接排除平台内部模型MODEL_DOMAIN [ (transient, , False), (is_abstract, , False), (model, not like, ir.%), (model, not like, base.%), ]第二个是字段范围。Odoo 17 里很多模型继承了mail.thread会带出message_ids、message_follower_ids、activity_ids等一长串消息字段。做业务数据字典时这些字段通常应该过滤掉否则一张订单表里一半是消息相关字段。做法是在循环里判断字段类型或名称前缀比如SKIP_PREFIX (message_, activity_, mail_) ... if fname.startswith(SKIP_PREFIX): continue第三个是翻译行为。fields_get()返回的字段标签跟当前用户的语言有关。在 shell 里环境语言默认是 en_US所以必须像示例里那样显式指定with_context(langzh_CN)。如果数据库里某些模块的中文翻译本来就没装全这里依然会返回英文不要以为脚本出问题。第四个是输出格式。Markdown 适合放在 Git 仓库里做 diff也适合直接在代码托管平台上预览。但如果要发给不熟悉 Markdown 的业务人员最好转成 CSV 或 Excel。常见做法是让脚本多输出一个 CSV 文件然后由实施顾问用 Excel 打开另存为 xlsx比让业务方装浏览器插件现实得多。3.4 补充方案直接从 ir.model.fields 读取字段归属fields_get()也不是万能的。它返回的是「当前这个模型实际有哪些字段」但不会告诉你字段属于哪个模块也不会暴露 Studio 自定义字段和模块字段的区别。如果要按模块归类就需要直接读ir.model.fields。下面这段代码用来补齐字段归属信息fields_domain [ (model.model, , sale.order), ] field_records env[ir.model.fields].search(fields_domain) for f in field_records: module_name f.modules.name or studio label f.field_description print(f.name, label, f.ttype, module_name)这段脚本的关键在于f.modules字段。模块字段属于哪个模块就指向ir.module.module的对应记录如果该模块字段是 Studio 自定义出来的modules会是空值这时我用studio占位。field_description在 ORM 查询时会按当前语言做翻译转换所以这里同样建议用with_context(langzh_CN)包一层。和fields_get()相比这种方式的优势是能拿到每个字段的模块归属、帮助文本、组权限等更多信息代价是要自己处理继承字段的重复问题。以sale.order为例它继承自mail.thread但ir.model.fields里每个继承字段都会以sale.order为模型再存一条记录所以直接查询会把message_ids等字段完整带出来需要脚本里再过滤一次。实践中更稳的做法是用fields_get()生成主文档再用ir.model.fields生成模块归属映射表两张表按字段技术名关联。4. 生成中文数据字典的避坑清单5 个常见的翻车点4.1 字段标签仍然是英文翻译没进数据库现象脚本里已经写了with_context(langzh_CN)生成出来的文档里还是大量英文标签只有少数字段是中文。原因中文语言包虽然启用了但某些模块的翻译并没有导入。Odoo 的翻译是「语言启用 模块导入」两步走后装的模块如果没走一遍翻译导入翻译条目就不会出现在数据库里。另一个原因是自研模块代码里直接写了stringCustomer Reference且没有提供中文 po 文件这种情况无论怎么设置语言都出不来中文。解决先确认语言已激活并一次性导入既有模块翻译再对新装模块执行一次模块升级或重新导入翻译。检查方法很简单在 shell 里执行t env[ir.translation].search([ (lang, , zh_CN), (src, like, %Customer Reference%), ], limit1) print(t.value if t else NO TRANSLATION)如果查不到记录说明翻译确实没导入。对自研模块则要在模块目录下补i18n/zh_CN.po并在升级模块时让 Odoo 自动导入。4.2 继承字段重复刷屏mixin 字段带出了一堆用不到的记录现象生成销售订单的数据字典里面message_ids、message_follower_ids、activity_ids等消息字段占了半页真正的业务字段被挤到下面。原因Odoo 17 里sale.order等模型继承了mail.threadmixinfields_get()会把所有继承字段合并进返回结果而且不会区分「本模型定义」和「从基类继承」。从技术上讲这些字段确实存在但从业务数据字典的角度看就是噪音。解决生成时用字段名前缀过滤掉消息类字段。更稳妥的方式是读取fields_get()返回的inherited属性它在 Odoo 17 里会标记该字段是否是继承字段。过滤逻辑可以是if attrs.get(inherited) and fname.startswith((message_, activity_)): continue如果不想过滤也可以把这批字段在文档里统一标为「继承自 mail.thread」让读者知道它们在每个业务模型里都会出现。两种方式选一个别不做处理直接全量输出。4.3 selection 选项只有 value 没有中文 label现象字段类型输出为selection时文档里只有draft、done这样的技术值业务人员不知道draft到底是「草稿」还是「未提交」。原因很多人直接从数据库ir_model_fields_selection子表查选项那个表里存的是选项的 value 和默认 label并不会自动映射翻译。即便用fields_get()如果 selection 的 label 没有翻译条目返回的也是英文字符串。解决在脚本中直接使用fields_get()返回的selection属性它已经是列表形式并且会随langzh_CN返回翻译后的 label。注意fields_get()会真实求值动态 selection如果某个 selection 是函数动态生成打印出来之前建议先转成字符串避免 Odoo 只返回一个方法名。示例selection_opt attrs.get(selection) or [] selection_str ; .join(f{v}{label} for v, label in selection_opt)这一条在生成中文数据字典时尤其重要selection 是字段值语义的最后一公里漏掉它看似小事实际用起来频繁翻车。4.4 Studio 自定义字段混进来标签来源跟模块翻译不是一套现象文档里出现x_studio_...开头的字段标签显示正常中文但它的 help 或备注是空的而且不受语言包控制。原因Studio 字段由用户在界面里创建标签是创建时填写的很多直接就是中文它不会进入任何模块的 po 翻译体系。对于「已翻译为中文」的语义而言这类字段实际上是反过来的——它的「翻译」是用户手工输入的原始值。解决默认在脚本里把x_前缀字段单独分类输出并加一列「来源 Studio 自定义」。如果业务方在意这些字段就保留并标注如果这次数据字典只针对标准模块就把x_字段过滤掉。注意过滤要放在fields_get()之后因为 Studio 字段同样会出现在返回结果里。4.5 字段清单与数据库实际列对不上非存储字段与历史残留现象文档里列出的字段数量和数据库表实际列数对不上有时候文档有一列数据库没有有时候数据库有列文档没写。原因Odoo 17 里有大量非存储非 store的计算字段它们在 ORM 层存在、可以被界面使用但不会在数据库表里生成对应列。反过来某些从旧版本升级上来的数据库里保留了历史字段列ORM 层早已不注册但表结构还在。解决数据字典里必须保留「存储」这一列。做迁移或写原生 SQL 时只认存储字段做业务分析时计算字段也要带上。如果项目里有数据库整理需求用信息模式检查实际表列然后把两份清单做差集对比快速找出是「缺字段」还是「残留列」psql -d 你的数据库名 -c select column_name from information_schema.columns where table_name sale_order;顺便提醒一下Odoo 17 表名把点号替换成下划线sale.order对应物理表sale_order。查数据库时别直接复制模型名。5. 把数据字典落成开发基线的三个技巧生成一份数据字典不难难的是让它活下来成为项目里长期有效的东西。下面是我自己沉淀下来的三个用法。第一个是把数据字典放进 Git 仓库和版本升级绑定。每次 Odoo 17 升级或新装模块后重跑一次生成脚本然后直接对比前后两份文档git diff --stat odoo17_data_dict_before.md odoo17_data_dict_after.md字段的新增、删除、类型变更都会在这一步暴露出来。相比逐个模块读更新日志这种方式来得直接也更适合在代码评审里作为字段变更的凭证。第二个是额外输出一张「模型关系摘要表」只保留 many2one 和 many2many 关系不输出普通字段。几十个模型挨个点开看关联能让人崩溃但一张关系表放在手边写报表 join、做数据迁移、梳理接口字段都高效得多。关系摘要表可以由脚本自动生成本质上就是把上一版文档里的relation列单独汇总。第三个是给字段加「业务备注」列。数据字典生成时中文标签是翻译出来的但业务上怎么叫它、会不会有历史遗留叫法翻译管不了。我会在实施过程中把「技术名—中文标签—业务俗称」对照表维护在数据字典的同一份文件里比如在 Markdown 表格末尾追加一列备注。这样后续接手的同事看到的不只是字段名而是字段和业务的对应关系。我自己吃过一次亏某个项目只做了「模型到字段」的正向文档没整理关联摘要上线前写迁移脚本时不得不一个个去界面里点开字段查看关联目标浪费了大半天。后来每版数据字典都强制带上关系摘要表并约定每次版本升级都重跑一遍生成脚本。从那时起数据字典才真正变成了开发基线而不是一次性交差。希望帮到你。本文还有配套的精品资源点击获取