简介:数据库表结构是理解业务系统数据模型的基础,尤其在 ERP 领域,表名和字段的命名规则直接影响开发与排查效率。Odoo 作为主流开源 ERP,其 PostgreSQL 数据库包含数百张表,字段注释多为英文缩写,给二次开发和报表编写带来挑战。通过将表名、字段、类型和注释整理为中文数据字典,并结合模型映射规则,可以快速定位业务数据落表位置。这一方法在报表 SQL 编写、数据一致性排查和模块开发核对中具有显著技术价值,尤其适用于 Odoo 从业者和学习者。本文分享的 Odoo 17 数据字典,正是基于这一需求,提供全库结构的中文翻译与查询路径,帮助开发者高效应对复杂表结构。
1. 一份把 Odoo 17 数据库摊开给你看的中文数据字典
接手一个跑了半年的 Odoo 17 库,最怕的不是业务逻辑复杂,而是打开数据库一看,几百张表名全是account_move_line、account_partial_reconcile这种,字段注释还全是英文缩写。想查一张发票的税额到底落在哪个字段,翻官方文档翻到眼花,最后还得靠\d+一条条试。这份 Odoo 17 数据字典就是冲着这个场景来的:用 Navicat 从 PostgreSQL 的odoo库导出全库结构,表名、字段、类型、注释都保留,并且做了中文翻译,按public模式逐表编排。它解决的是「知道业务在界面上怎么点,但不知道数据落到哪张表哪个字段」的问题,适合做二次开发、写报表 SQL、排查数据不一致的 Odoo 从业者,也适合刚接触 Odoo 数据模型、想系统看一遍表结构的学习者。
2. 先看懂这份字典的结构:从 PostgreSQL 到 Odoo 模型的三层映射
拿到一份数据库字典,很多人第一反应是直接搜表名。但 Odoo 的表结构不是随手建的,它有一套稳定的命名和映射规则,先把这层规则吃透,字典才用得顺。这一章讲清楚字典里每一层信息对应 Odoo 的哪个概念,以及怎么用字典反推模型定义。
2.1 库、模式、表三级结构在 Odoo 里的含义
字典的目录层级是「服务器 localhost → 数据库 odoo → 模式 public → 表」。对 Odoo 来说,localhost只是导出时连接的实例地址,odoo是默认库名,真正有信息量的是public模式下的表清单。Odoo 所有业务表都建在public下,没有按模块分 schema,所以你会看到account_、stock_、mrp_、product_这些前缀混在一起,靠前缀区分模块归属。
表名和 Odoo 模型名之间是机械替换关系:模型account.move对应表account_move,点号换下划线。反过来,看到account_move_line就知道模型是account.move.line。这个规则几乎无例外,唯一要注意的是自动生成的关系表,比如account_account_account_journal_rel,它不对应任何模型,而是两张表多对多关系的中间表,命名是「表A_表B_rel」。字典里这类_rel结尾的表不少,看到就知道它不是业务实体,别拿它当主表查。
字段层面,Odoo 的每个字段在表里通常有一列,但有几类特殊字段要留意:many2one存的是外键 id,列名常带_id后缀;one2many和many2many不落列,前者靠对方表的外键反向查,后者就是那些_rel表;计算字段如果设了store=True才会落列,否则查表查不到。字典里能看到列,说明它是存储的,这一点对写 SQL 很关键。
2.2 用字典反查模型定义的实操路径
假设业务反馈「发票上的税额和报表对不上」,你要定位税额字段。打开字典搜account_move_line,在字段列表里找tax相关列,会看到tax_line_id、tax_ids这类。tax_line_id是 many2one 指向account_tax,表示这一行是税行;tax_ids在表里通常不直接落列,而是通过account_move_line_account_tax_rel关联。这时候字典的价值就出来了:你能顺着_rel表名找到关联关系,而不是在代码里猜。
再举个查库存的例子。想知道某产品的在手数量存在哪,搜stock_quant,字段里有quantity、product_id、location_id。quantity就是在手数量,location_id区分库位。字典里字段类型标numeric还是double precision也有用,Odoo 的Float字段在 PostgreSQL 里可能是numeric,精度设置不同列类型会不一样,做聚合时要注意别踩浮点误差。
提示:字典是静态快照,导出时间是 2024-08-17,版本标 1.0。如果你装的模块和导出时不一致,表可能多或少,以自己库里的
\dt为准。
2.3 中文翻译带来的检索便利与局限
这份字典的卖点是中文翻译,字段注释从英文变成了中文,比如amount_total注释成「总计」,state注释成「状态」。检索时直接搜中文关键词比搜英文缩写快得多,尤其对不熟悉 Odoo 术语的人。但要注意,翻译是人工或工具做的,个别字段的译名可能和界面上的叫法不完全一致,比如res_partner里的vat可能译成「税号」也可能译成「增值税号」,搜的时候两个词都试一下。
另一个局限是,翻译只覆盖了注释,表名和字段名本身还是英文。所以实际用法是「中文搜注释定位字段,英文列名写 SQL」。我一般会先在字典里用中文找到目标字段,记下英文列名,再回数据库里写查询,两步走比直接猜列名稳。
3. 把字典用进日常开发:三条可复现的查询路径
字典不是拿来看的,是拿来查的。这一章给三条具体路径:写报表 SQL 时怎么用字典确认字段、排查数据问题时怎么顺着字典追关联、做模块开发时怎么用字典核对模型落表。每条都落到可执行的 SQL 和判断逻辑上。
3.1 写报表 SQL 前用字典确认字段与类型
写一张销售报表,要取订单行、产品、数量、单价、折扣。不查字典直接写,很容易把price_unit和price_subtotal搞混。正确做法是先翻字典的sale_order_line表,确认字段:product_id(产品)、product_uom_qty(订购数量)、price_unit(单价)、discount(折扣百分比)、price_subtotal(小计,不含税)。字典里discount类型是numeric,说明它是百分比数值,不是金额,计算时要除以 100。
-- 销售订单行基础报表:字段名均来自数据字典 sale_order_line 表 SELECT so.name AS order_no, -- 订单号,来自 sale_order.name sol.product_id AS product_id, -- 产品 id,many2one 外键 sol.product_uom_qty AS qty, -- 订购数量 sol.price_unit AS unit_price, -- 单价 sol.discount AS discount_pct, -- 折扣百分比,注意是百分数 sol.price_subtotal AS subtotal -- 小计(不含税) FROM sale_order_line sol JOIN sale_order so ON so.id = sol.order_id WHERE so.state IN ('sale', 'done') -- 只取已确认和已完成订单 ORDER BY so.name, sol.id;这段 SQL 的每个列名都能在字典里对上。order_id是 many2one 外键指向sale_order,所以 join 用so.id = sol.order_id。state字段字典注释是「状态」,取值sale、done是 Odoo 销售订单的确认态和完成态,写报表时排除draft、cancel是常规做法。参数上唯一要小心的是discount,它是 0 到 100 的百分数,如果报表要算折后金额,得用price_unit * (1 - discount/100),别直接减。
3.2 顺着字典追多表关联排查数据不一致
业务说「某张发票的应收金额和凭证对不上」。排查路径是:先查account_move找到发票,看amount_total;再查account_move_line看明细行;最后看account_partial_reconcile有没有部分核销。字典里这三张表都在,字段也能对上。
-- 排查发票金额与明细行合计是否一致 SELECT am.id, am.name AS invoice_no, am.amount_total AS header_total, -- 发票头总金额 SUM(aml.debit) AS sum_debit, -- 明细借方合计 SUM(aml.credit) AS sum_credit, -- 明细贷方合计 SUM(aml.balance) AS sum_balance -- 明细余额合计 FROM account_move am JOIN account_move_line aml ON aml.move_id = am.id WHERE am.id = 12345 -- 换成目标发票 id GROUP BY am.id, am.name, am.amount_total;判断逻辑:amount_total应该等于明细行balance的绝对值合计(应收发票方向)。如果对不上,常见原因是明细行里有display_type为tax的税行被重复计入,或者有exclude_from_invoice_tab的行。字典里account_move_line有display_type字段,注释是「显示类型」,查一下这个字段的取值分布就能定位。这一步的关键是字典让你知道有display_type这个字段存在,不然根本想不到去过滤。
3.3 模块开发时用字典核对模型落表
自己写模块加了个模型,跑起来发现表没建或者字段没落。用字典对照能快速判断:模型_name = 'x.demo'应该对应表x_demo,如果字典里(或自己库的\dt)没有这张表,说明模块没升级或模型没被加载。字段方面,fields.Float默认落numeric,fields.Integer落int4,fields.Char落varchar,字典里的类型列能帮你确认字段是不是真的存了。
# 模型定义与落表对照示例 from odoo import models, fields class XDemo(models.Model): _name = 'x.demo' # 对应表 x_demo _description = '演示模型' name = fields.Char(string='名称') # 落 varchar qty = fields.Float(string='数量') # 落 numeric partner_id = fields.Many2one('res.partner', string='客户') # 落 partner_id int4 line_ids = fields.One2many('x.demo.line', 'demo_id', string='明细') # 不落列对照字典:x_demo表里应该有name、qty、partner_id三列,没有line_ids列,因为 one2many 不落列,明细在x_demo_line表的demo_id外键上。如果字典里qty显示成varchar,那多半是字段类型写错了或者被别的模块改了,得回去查。这种核对方式比反复重启服务看日志快。
4. 避坑与排查:用数据字典时最容易翻车的五个地方
字典好用,但它是快照不是实时文档,用的时候有几类坑反复出现。这一章按「现象 → 原因 → 解决」列五条,都是实际排查里攒下来的。
4.1 搜不到表:模块没装或表名带前缀
现象:字典里明明有mrp_production,自己库里\dt却找不到。原因:导出字典的库装了制造模块,你的库没装,或者模块装了但没升级到建表那一步。解决:先SELECT name FROM ir_module_module WHERE state='installed'确认模块状态,再\dt *mrp*看表在不在。如果模块装了表还没有,执行模块升级。别拿字典当「我的库一定有这张表」的依据。
4.2 字段注释是中文但列名对不上
现象:字典里某字段注释是「客户」,你以为是customer_id,实际列名是partner_id。原因:Odoo 里客户统一用res.partner,字段名习惯叫partner_id,中文翻译只译注释不译列名。解决:以列名为准,中文注释只用来定位。搜「客户」找到字段后,看同一行的列名,别自己拼。
4.3 计算字段查不到值
现象:界面上有「总计」字段,字典里也有这列,但 SQL 查出来全是 NULL。原因:该字段是compute且store=False,不落库,字典里能看到列可能是因为导出时包含了视图或历史残留,或者你查的是关联表的非存储字段。解决:确认字段是否store=True,非存储字段只能在 ORM 里读,SQL 查不到。需要落库就改模型加store=True并升级。
4.4 多对多关系查错表
现象:想查用户和组的关联,直接查res_users找组字段,找不到。原因:多对多在 Odoo 里是中间表,用户和组的关系在res_groups_users_rel。解决:字典里搜_rel结尾的表,按「表A_表B_rel」的命名找对应中间表,join 两边的主键查。
4.5 字典版本与运行版本不一致
现象:按字典写的 SQL 报「column does not exist」。原因:字典导出时是 1.0 快照,你的库可能装了额外模块改了表结构,或者 Odoo 小版本升级有字段增删。解决:写 SQL 前先用\d+ 表名核对当前列,字典只作导航。养成「字典定位、库内确认」的习惯,能省掉大半报错。
5. 进阶用法:把静态字典变成可查询的本地知识库
字典是 PDF 或文档形态时,翻页慢、搜关键词还行但跨表关联查不了。我一般会把它转成可查询的结构,最直接的办法是把表名、字段名、类型、注释导成一张本地表,用 SQL 查字典本身。做法是从字典里抽三列:table_name、column_name、comment,灌进一个临时库。
-- 本地字典表结构,用于反查字段 CREATE TABLE dict_columns ( table_name varchar(128), column_name varchar(128), data_type varchar(64), comment_cn text ); -- 示例:查所有含「税」注释的字段 SELECT table_name, column_name, comment_cn FROM dict_columns WHERE comment_cn LIKE '%税%' ORDER BY table_name;灌数据时按字典的章节顺序逐表录入,几百张表手工录不现实,常见做法是用脚本解析字典文本,按「表: xxx」和字段行切分。解析规则不复杂:遇到表:开头的行记表名,后续行按列名、类型、注释三段拆。解析完跑上面那条查询,几秒就能列出所有和税相关的字段,比翻文档快一个量级。
再进一步,可以把字典和实际库的information_schema.columns做对比,找出「字典有但库里没有」和「库里有但字典没记」的字段,前者多半是模块没装,后者多半是自定义字段。对比 SQL 这样写:
-- 对比字典与实际库的字段差异(需在同一库内先导入 dict_columns) SELECT d.table_name, d.column_name FROM dict_columns d LEFT JOIN information_schema.columns c ON c.table_schema = 'public' AND c.table_name = d.table_name AND c.column_name = d.column_name WHERE c.column_name IS NULL; -- 字典有、实际库没有这条查询跑出来的结果,基本就是「你缺的模块或没升级的字段」清单。我拿它排查过一次环境不一致的问题,十分钟定位到三个没装的模块,比逐个比对快得多。
一个具体技巧:给dict_columns的comment_cn建全文索引,用to_tsvector('simple', comment_cn)做中文分词检索,搜「应收」「库存」这类业务词能直接命中字段,不用记英文列名。中文分词在 PostgreSQL 里要装扩展,简单场景用LIKE '%关键词%'也够用,表就几百行,性能不是问题。
从那以后我每次接手新库,第一件事就是把数据字典灌成本地表,先跑一遍差异对比,再开始写业务 SQL。这个习惯帮我避开了好几次「按文档写查询、结果列不存在」的翻车。希望这份字典和这套用法,能帮你在 Odoo 17 的几百张表里少走点弯路。
本文还有配套的精品资源,点击获取