简介:Odoo 17 数据字典将 Navicat 生成的全套英文数据库说明翻译为中文,面向 Odoo 开发、实施与初学者。文档基于 PostgreSQL 的 odoo 库及 public schema 展开,按模块逐表收录 account_account、account_account_tag 等业务表及关联中间表,包含字段名、数据类型、长度、默认值、键约束与备注,能清晰呈现销售、采购、库存、财务模块的数据模型和表间关系。压缩包内为单个 PDF 文件,大小约 8.07MB,便于离线阅读与关键字检索,目录结构适合快速定位。目前已有 133 人浏览学习。借助这份字典,可在二次开发时核对字段来源、编写 SQL 报表、排查数据异常,减少逐表翻译整理的重复劳动;对初学者而言,也能按章节逐步理解 Odoo 底层表结构,是一份省时又直观的参考手册。
1. 一份 Odoo 17 中文数据字典到底解决什么问题
接手过 Odoo 项目的人应该都有过这个瞬间:打开 pgAdmin 看到几百张数据表,全是sale_order、stock_move_line、account_move这样的英文名,想查一个“销售订单明细里税率存哪个字段”都得靠猜。特别是给客户做报表、做外部系统对接、或者接手一份别人开发的代码时,数据库结构不透明,改哪张表、加哪个字段全凭经验,风险极高。odoo 17 数据字典(已翻译为中文)就是干这个用的——把 Odoo 的模型定义、字段清单、类型、关系、约束条件系统性地整理成一份可读的文档,再把字段名和说明翻译成中文,让开发、实施、数据分析人员能快速定位业务数据存放位置。
这份字典对三类人价值最大:一是做二次开发的工程师,需要确认某个字段是many2one还是selection,是否必填、是否可翻译;二是做数据迁移或 BI 报表的开发者,需要知道业务表之间的关联键和约束关系;三是刚接手 Odoo 项目的维护人员,面对几十个自定义模块时,数据字典比源码逐行看效率高得多。下文我会讲清楚字典里到底有什么、怎么自己生成一份、怎么避免常见的坑,并分享我把字典当成日常开发工具后的做法。
2. 数据字典里的底层结构:三张元数据表与翻译机制
2.1 Odoo 的数据字典不是一个文件,而是数据库里的元数据
很多人以为“数据字典”是一份现成的 PDF 或 Excel,其实在 Odoo 里,数据字典的源头是数据库中的几张元数据表。Odoo 的 ORM 会把每个 Python 模型(比如sale.order)映射为一张 PostgreSQL 表(sale_order),同时把模型和字段的定义录入系统表:ir_model(模型表)、ir_model_fields(字段表)、ir_model_constraint(约束表)、ir_model_relation(关联表)。你从“设置 → 技术 → 模型”界面里看到的每个模型的字段列表,本质上是ir_model_fields这张表的内容渲染出来的。
这意味着生成数据字典,等价于对ir_model_fields做一次结构化的查询和导出。你的 Odoo 实例里有哪些自定义模型、哪些标准模型被修改过、哪些字段加了x_前缀,全部能从这几张表查出来。我最早做数据字典时,最惊讶的是ir_model_fields里居然有几千条记录,原来 Odoo 标准模型加上安装的模块,字段总数轻松超过一万。直接用这个表做字典,必须先做筛选和归类,否则文档会膨胀到没人愿意看。
2.2 中文翻译藏在语言模块里,不在数据库表字段描述中
标题强调“已翻译为中文”,这是数据字典实操里最容易出错的地方。Odoo 模型的description(比如“Sales Order”)和字段的string(比如“Order Date”)在代码里通常写英文,中文翻译存放在ir_translation表或编译后的.po文件中。如果你直接查ir_model_fields表,看到的大多是英文name和string,和“中文数据字典”还差一步。
具体来说,Odoo 的ir_model_fields里有两个字段需要区分:name是 ORM 的技术字段名(如x_custom_field),field_description是界面上的标签(如Custom Field)。中文翻译依靠语言的field.translation机制,系统会根据当前语言环境动态渲染。要生成中文版数据字典,常见做法是查ir_model_fields拿到英文基础数据,再根据模块自带的i18n/zh_CN.po翻译文件或者数据库里的ir_translation记录做二次替换。很多中文数据字典生成工具做不好这一步,输出的文档英文占九成,实际使用价值大打折扣。
2.3 生成字典前先确认信息来源:数据库实时导出 vs 代码静态解析
从技术路线上看,获取字段信息有两条路。一条是从数据库实时读取,用 SQL 或 ORM 查ir_model_fields,拿到的是当前实例的真实状态。比如某开发者给res_partner加了十个x_开头自定义字段,数据库实时导出一定包含它们,而且能顺带查出字段类型、是否存储、是否可翻译等属性。另一条是从源码层面静态解析,读取所有模块的 Python 模型定义和 XML 视图,生成字典。这种方式适合在没有运行数据库的情况下做基础结构分析,比如刚从代码仓库拉下来一个项目,还没初始化数据库时。
我个人的习惯是:只要数据库能连上,优先用数据库实时导出。因为静态解析需要自己处理继承、混入 mixin、动态字段(比如_inherit后增加的字段)等复杂逻辑,很容易漏掉一些由第三方模块通过代码动态添加的字段。数据库导出的结果和系统实际的表结构一一对应,不会出现纸上谈兵的问题。唯一要注意的是,如果你需要的是“标准模型的原生文档”,那直接用各模块doc/目录下的现成字典更快;如果你需要的是“这个项目实际的字段全貌”,那必须实时导出。本篇文章后面给的脚本,都是建立在“数据库可连接”的前提下。
3. 自己动手生成一份中文数据字典:两条可行的路径
3.1 路径一:SQL 直查元数据表,快速导出全量字段清单
我生成数据字典的第一步永远是 SQL。直接连 PostgreSQL,从ir_model_fields里查出所有模型和字段的基础信息。这个方案不需要启动 Odoo 应用,只要数据库在跑就能执行,适合在部署服务器上快速摸底。下面的查询是一份经过筛选的基线版本,把技术字段、日志字段等噪声排除后,输出模型名、字段名、字段标签、类型、是否必填和来源模块:
SELECT m.model AS 模型标识, m.name AS 模型说明, f.name AS 字段标识, f.field_description AS 字段标签, f.ttype AS 字段类型, f.required AS 是否必填, f.store AS 是否存储, f.translatable AS 是否可翻译, f.module AS 来源模块 FROM ir_model_fields f LEFT JOIN ir_model m ON m.id = f.model_id WHERE m.model NOT LIKE 'ir.%' AND m.model NOT LIKE '%_rel%' AND f.name NOT IN ('id', 'create_date', 'create_uid', 'write_date', 'write_uid', '__last_update') AND f.model IS NOT NULL ORDER BY m.model, f.name;这个查询的逻辑:先关联ir_model拿到模型的可读名称;过滤掉ir.%开头的内部技术模型和_rel结尾的自动关联表;再把所有模型都有的id、create_uid这类审计字段剔除掉。运行时注意参数:ttype在 Odoo 里表示字段的 ORM 类型,比如many2one、one2many、char、selection,这些类型直接决定了你在代码里怎么取值。required在数据库里是 boolean,注意在 Navicat 里显示可能带括号。translatable非常重要,凡是可翻译字段,你在代码里动态改值时必须用with_context(lang=...)处理。
上面的 SQL 查出来的是英文标签,还要对field_description做中文翻译。常见做法是把查询结果导出为 CSV,然后手工对照模块翻译文件批量替换。但更高效的办法是把翻译文件直接加载到查询里,让输出本身就带中文说明。比如优先从ir_translation表里取当前语言对应的值:
SELECT m.model AS 模型标识, COALESCE(mt.value, m.name) AS 模型中文描述, f.name AS 字段标识, COALESCE(ft.value, f.field_description) AS 字段中文标签, f.ttype AS 字段类型 FROM ir_model_fields f LEFT JOIN ir_model m ON m.id = f.model_id LEFT JOIN ir_translation mt ON mt.res_id = m.id AND mt.name = 'ir.model,name' AND mt.lang = 'zh_CN' LEFT JOIN ir_translation ft ON ft.res_id = f.id AND ft.name = 'ir.model.fields,field_description' AND ft.lang = 'zh_CN' WHERE m.model NOT LIKE 'ir.%' ORDER BY m.model, f.name;这里有个重要的替换逻辑:ir_translation表里name对应翻译的字段路径,lang指定语言代码,res_id指向记录 ID。如果关联不到中文翻译,就用COALESCE回退到英文原始值,保证字典不会出现空行。这个方案的坑点在于zh_CN语言包需要提前安装并完成翻译写入,否则关联出来全是 NULL。实际操作中,我通常先在界面上把语言切换成中文,让系统自动生成翻译记录,再执行 SQL,这样命中率最高。
3.2 路径二:用 Odoo shell 走 ORM,拿到模型注释和 help 文本
SQL 方案能拿到字段的基本属性,但拿不到一些写在代码里的业务说明,比如字段的help提示文本、related关联路径、compute方法名。这些信息在字段使用上相当重要——例如related字段往往在字典里看起来类型是char,实际值却来自另一张表,不了解这一点很容易误用。要拿到这些,就要用 ORM 方式查。下面的脚本是 Odoo 17 环境里通过odoo shell运行的核心片段,批量提取指定模型的字段属性:
from odoo import api, models def dump_model_doc(env, model_name): model = env[model_name] result = [] for name, field in model._fields.items(): result.append({ 'name': name, 'string': field.string, 'type': field.type, 'help': field.help or '', 'related': field.related or '', 'compute': getattr(field, 'compute', '') or '', 'required': field.required, 'translatable': getattr(field, 'translate', False), }) return result # 用法示例:在 odoo shell 里执行 # res = dump_model_doc(env, 'sale.order') # for r in res: # print(f"{r['name']:30s} {r['type']:10s} {r['string']}")这个脚本的关键参数:model._fields是当前模型继承体系下所有字段的集合,包括继承了父模型的字段,比数据库导出的结果更完整。field.string是界面标签,也可能是英文,中文翻译需要主动读取语言环境下的值,或者配合env['ir.model.fields'].search去取field_description。field.related和field.compute可以让你把字段的本质追到源头,这对理解“从表里没有这个字段,但对象有这个属性”特别有帮助。
如果你需要的是一份格式规整的文档,请在将以上结构化数据导出后,自行组装为 Markdown 或 Excel 表格。注意_fields的顺序由 Python 字典维护,实际输出不一定按你想看的顺序排列,建议在导出时按name排序。用 ORM 方案生成全库字典时,务必先列出所有模型列表env.registry.models,再逐个 dump——这个过程可能耗时几分钟,属于正常现象,不要误以为是死循环。
3.3 两种路径怎么选:场景决定工具
SQL 和 ORM 两种方案并非二选一,而是按需求场景搭配。如果你只是想知道“这个库有哪些表、哪些字段”,五分钟的 SQL 查询足够;如果你要写开发文档、做代码评审、分析字段逻辑链路,ORM 方案给的related和compute字段会直接改变你对某个字段的理解。例如sale.order里的amount_total在数据库里根本没有存储列,它是计算字段;用 SQL 查会看到它也可能被标记为store=False,但你不知道它怎么算的。ORM 方案可以告诉你compute='_compute_amount_total',再去源码里查这个方法就能彻底弄明白。
实际项目里我的工作流是:先用 SQL 全量拉一遍字段清单,按业务模块归类;然后用 ORM 脚本单独导出重点模型的字段细节;最后把两份结果合一,补充中文翻译和类型说明。这套流程做一次大约一两个小时,但产出的一百多页字典文档能支撑后续几个月的开发查询需求。效率关键在于别追求一步到位,而是把 SQL 查询做成模板,每次新增模块时只跑增量查询。
4. 把中文数据字典用起来:查表、定位字段与梳理关系的三个高频场景
4.1 场景一:拿到一个数据库,快速定位业务对应的模型和表
接手一个新项目或排查问题时,最常听见的问题是:“订单号存在哪张表?”有了中文数据字典后,这个问题的答案在十秒内能找到。做法是打开字典文档的模型列表,先按模块过滤出sale、stock、account等业务前缀,再通过字段标签的中文关键词反向搜索。比如你想找“客户发票的付款状态”,直接在字典的字段标签列搜“付款”或“发票”,马上会定位到account.move模型里的payment_state字段。
效率窍门在于不要把字典当小说读,而是把它当搜索引擎用。我会在 Excel 里对“字段标签”列设置筛选,需要查什么业务名词就输入关键词。例如查“税”会找出tax_totals、amount_tax、tax_ids等一系列字段,配合“类型”列可以立刻判断它们之间的从属关系。对于实施人员来说,这一招能减少大量翻代码的时间;对于开发人员来说,也能在写业务逻辑前先确认字段是否已被某个模块占用,避免出现重复定义。
4.2 场景二:开发自定义模块前,核对字段约束、类型和继承来源
开发过程中最怕的就是“以为有某个字段,实际上没有,或者类型不对”。比如你想在res_partner上挂一个vat字段,以为它是char,结果字典显示它在res.partner里是char没错,但可能是通过base_vat模块加入的,且带有格式校验函数。如果你不知道这一点,直接在视图里硬改值,可能触发表单校验,导致保存失败。数据字典在这里充当了“接口说明书”——每次写models.py之前,先查对应模型的字段清单,确认目标字段是否已存在、类型是否匹配、是否必填。
这里推荐一个我常用的检查流程:在字典里筛出“来源模块”列,凡是sale、stock、account之外的自定义模块字段,全部标注为重点检查对象。因为这些字段往往没有标准文档,类型和约束全靠读代码。利用字典的“是否必填”和“是否可翻译”两列,能提前发现store为假的字段不能直接 SQL 查询,这类字段必须在 ORM 层取值的特殊情况。
4.3 场景三:梳理模型关系,理解 many2one、one2many 和 many2many
数据字典最值钱的部分之一是它对“关系字段”的标注。sale.order里的order_line是one2many,反向指向sale.order.line;sale.order.line里的order_id是many2one;product.template和product.product之间通过product_variant_ids形成关联。这些关系光看表结构很难一目了然,因为关联字段不一定在数据库里有实际的 FK 约束,Odoo 的关联更多是 ORM 层面的逻辑映射。
字段类型里的relation属性直接告诉你关联的目标模型。例如查询ir_model_fields时,relation字段会写明指向哪个模型。在中文数据字典里,我习惯单独做一张“关系表”,把many2one、one2many、many2many全部抽出,记录源模型、字段名、目标模型。这张表的用途很直接:当你写 SQL 做跨表 JOIN 时,不需要再猜外键叫什么名字,按关系表里的字段名和模型关系一一对应。比如要从销售订单关联到客户档案,先找sale.order上的partner_id字段,relation 指向res.partner,连表字段名就是partner_id。
5. 数据字典避坑:五条实操中的踩坑记录
5.1 继承字段在字典里看不到来源
现象:数据字典里明明列出了sale.order的user_id字段,类型是many2one,但你在sale.order的 Python 模型定义里却搜不到这个字段的定义。
原因:这个字段是通过_inherits机制从res.users或中间模型继承过来的。ir_model_fields只展示当前模型的所有字段,不区分字段是自身定义还是继承所得。看源码时如果只盯着当前文件,自然找不到。
解决:在字典生成脚本里增加“继承来源”信息,通过env[model]._inherits拿到父模型列表,然后在导出时对每个字段标注它是否属于继承字段。我在实际项目里会额外查询ir.model.fields的inherits列(如果存在),或者用 ORM 的field.related辅助判断,这样字典里就能看到字段的真实定义位置,不用再靠猜。
5.2 中文翻译不生效,字典全是英文
现象:按前文 SQL 查询导出的字典,模型说明和字段标签大量显示英文,只有少量模块显示中文,即使已经安装了中文语言包。
原因:ir_translation表中的翻译记录是按模块加载的。如果某个模块是在安装语言包之后才安装的,该模块的翻译不会自动补全,需要手动更新翻译。另一个常见原因是,数据库里存的是翻译前的原始值,而语言包未完全加载。
解决:在生成字典前,先进入 Odoo 界面执行“设置 → 翻译 → 加载可用的翻译”,选择简体中文、覆盖现有翻译后重新加载。加载完成后再跑字典查询,命中率会有质的提升。如果依然中文不全,检查模块的i18n/zh_CN.po文件是否包含完整词条,或者使用第三方模块的中文补丁。
5.3 information_schema 查询结果和 ORM 字段对不上
现象:直接查 PostgreSQL 的information_schema.columns看到的表名是sale_order_xxx,列名是x_studio_xxx之类,但数据字典里对应的是sale.order和字段x_my_field。
原因:Odoo 的物理表名由模型名转换而来,点号变下划线;自定义字段名统一加x_前缀。物理表和 ORM 模型之间的映射规则不是一一对等的,直接查系统表拿到的只是存储层信息,缺少业务层的语义。
解决:不要用information_schema代替ir_model_fields。数据字典要以ir_model_fields为底表,物理表名只作为附加信息展示。需要还原表结构时,用env['sale.order']._table获取实际的表名,再与存储层信息对应。记住:业务字段永远看 ORM,底层结构仅作参考。
5.4 手工编写的字段说明在模块升级后被覆盖
现象:为了补全中文数据字典,直接在ir_model_fields记录里更新了某些字段的field_description,但执行模块升级后,这些修改全部回到英文。
原因:ir_model_fields的记录在每次模块更新时会被重新同步,根据 Python 代码里的定义重写字段标签。如果直接改数据库记录而不改源码,升级必然覆盖自定义的说明。
解决:补全中文字段说明的正规渠道是修改模块的源码中的string属性,或维护独立的翻译文件。如果只是临时用于文档输出,那就把翻译写入外部词典文件,不要直接改数据库。我的习惯是写一个独立的 Python 脚本维护一份字段别名映射,在生成字典时用这个映射覆盖显示名,这样既能定制中文说明,又不影响系统运行。
5.5 无关字段占用大量篇幅:如何去噪
现象:生成的字典有几千行,其中相当一部分是id、create_uid、write_date等技术字段,以及display_name、__last_update这类自动字段,阅读时严重干扰。
原因:没有在查询里对字段类型和名称做过滤。Odoo 每个模型都自动附加约十个技术字段,几十个模型加起来就是几百行垃圾信息。
解决:生成字典时把字段名列表中加入黑名单,把ttype为boolean且字段名包含active等不常关心的字段排除。前文 SQL 里已经加了f.name NOT IN (...),在 ORM 方案里也一样,跳过技术字段后再输出。实际操作中,我还会额外排除id以外的xxx_uid字段,并按“业务模块”分组,让字典更便于翻阅。
6. 把数据字典嵌进日常开发:维护基线、接口校验与新人培训
我在某公司的长期项目里养成的一个习惯是:每个月跑一次数据字典快照,存成带日期的文件,同时保留一份“基线字典”。这个做法的直接收益是,当有同事改动字段没有同步更新文档时,我能通过对比快照发现差异。对比不用全量做,只需要看ir_model_fields中字段的name和ttype是否变化,新增或删除的字段会立刻暴露。写一个简单的 Python 脚本,用 pandas 读取前一个月份的 CSV 和当前 CSV,做一个merge找出新增与消失的字段,几分钟就能完成一次结构盘点。
对于自定义模块开发来说,还有一个小技巧:在编写新模型前,用数据字典筛选“模型标识”前缀,看是否已有同名字段被占用。比如你准备在stock.picking上创建x_custom_remark,先在字典里搜一下x_custom开头现在有哪些字段,避免因为字段名冲突导致安装报错。数据字典还可以作为接口文档提供给外部系统对接方,让对方拿着中文标签就能核对 JSON 字段对应关系,省去反复问接口字段含义的时间。
另一个我常用的做法是把字典输出成按模型分组的 Markdown 文件,每组附上几个核心字段的非空说明,直接放到项目 Wiki 里当作开发索引。新入职的开发人员拿到这份文档后,只需要 30 分钟就能了解这个库的数据分布,不需要人肉翻代码。为了持续保证这份文档的时效性,我把生成脚本写成了一个命令行工具,每次发布前执行一次,看差异再决定是否更新 Wiki——数据字典不是一次性交付物,而是和代码一起演进的资产。最后想提醒一点:字典工具生成的内容,永远以当前数据库状态为准,遇到字段含义含糊的情况,回源码确认比翻字典更有保障。别偷懒,这两步配合用才能真正让你的开发少踩坑。这是我的切身体会,希望帮到你。
本文还有配套的精品资源,点击获取