ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Odoo 17 中文数据字典:ORM字段与数据库表映射指南

Odoo 17 中文数据字典:ORM字段与数据库表映射指南 简介本资源是Odoo 17官方数据库结构的完整中文翻译版数据字典面向Odoo二次开发工程师、ERP实施顾问及进阶学习者解决英文原版字典阅读门槛高、字段含义理解困难、模块间关联关系不清晰等实际问题。压缩包仅含1个PDF文件8.07MB采用Navicat自动生成结构严谨、层级分明涵盖服务器配置、数据库实例、public模式下全部核心表如account_account、account_move_line等财务与业务主表及其字段定义、外键关系与关联中间表便于快速定位模型字段、理解底层数据流向与模块耦合逻辑。内容预览显示其已按标准技术文档格式组织含引言、数据库概览、表清单及逐表字段说明适合作为开发调试、SQL查询优化、API字段映射与定制化模块设计的权威参考依据。目前已有133人下载学习是少有的系统化、可直接投入实战的Odoo 17底层数据认知资料。1. Odoo 17 数据字典已翻译为中文为什么你改了模型字段却查不到数据库表为什么ERP二次开发总在“猜字段”这不是一份简单的术语对照表而是一份能直接塞进你开发流程里的Odoo 17 实体级数据地图。当你在models/res_partner.py里加了一个x_invoice_terms字段却在 PostgreSQL 里死活找不到对应列当你被业务方问“客户信用额度存在哪张表”而你翻了三遍官方文档还卡在res.partner和account.move的嵌套关系里——这时候一份准确、结构清晰、带上下文注释的中文数据字典就是你避免反复psql -c \d res_partner或硬啃英文源码的后悔药。它不替代模型定义但能让你在 10 秒内确认partner_id是外键还是普通字段active字段是否参与搜索视图过滤company_id在sale.order表中是否允许为空本篇全程基于 Odoo 17.0 官方发布版2023年10月GA版本所有字段名、表名、约束、默认值均来自真实数据库 schema Python 模型元数据解析不是网页爬虫拼凑不是旧版迁移残留更不是靠“大概意思”意译。适合正在做 Odoo 17 定制开发、系统集成、数据迁移或审计合规的技术负责人、实施顾问与后端工程师。2. 从源码到字典用 Odoo 自身机制导出结构化中文数据字典Odoo 不提供开箱即用的“导出数据字典”按钮但它的 ORM 层和 CLI 工具链天然支持结构化元数据提取。关键在于绕过 Web 界面渲染层直取模型定义与数据库 schema 的交集。常见做法是组合odoo-bin命令行工具 Python 脚本 PostgreSQLinformation_schema查询三者互补验证避免仅依赖 ORM会漏掉_sql_constraints或手动建的索引或仅依赖 DB会缺字段语义、compute逻辑、related关系。我一般会先跑一次odoo-bin --dbyour_db --updateall --stop-after-init确保模型已加载再执行导出脚本——这步省略会导致ir.model.fields表未完全填充字段描述为空。2.1 用 odoo-bin shell 提取核心模型元数据含中文翻译Odoo 17 的ir.model.fields表已内置多语言支持只要你的数据库启用了中文语言包base.lang中有zh_CN记录字段field_description就会自动存储翻译后的名称。以下命令在 Odoo 服务目录下执行确保odoo-bin可访问且数据库连接正常# 进入 Odoo 安装目录如 /opt/odoo/odoo-server cd /opt/odoo/odoo-server # 启动交互式 shell连接指定数据库 ./odoo-bin shell -d your_odoo_db --no-http # 在 Python shell 中执行注意需提前启用中文语言环境 from odoo import models, fields, api env self.env # 获取所有已安装模块的模型排除 test/model/_test* models_list env[ir.model].search([(state, , base)]).mapped(model) # 导出字段基础信息name, field_description, ttype, required, readonly fields_data [] for model_name in models_list: ... try: ... model env[model_name] ... for field_name, field in model._fields.items(): ... if field_name.startswith(_): continue # 跳过私有字段 ... fields_data.append({ ... model: model_name, ... field: field_name, ... description: field.string or , ... type: field.type, ... required: field.required, ... readonly: field.readonly, ... help: field.help or , ... }) ... except Exception as e: ... print(fSkip model {model_name}: {e}) ... # 写入 CSV示例实际建议用 pandas 或 JSON import csv with open(/tmp/odoo17_fields_raw.csv, w, newline, encodingutf-8) as f: ... writer csv.DictWriter(f, fieldnames[model,field,description,type,required,readonly,help]) ... writer.writeheader() ... writer.writerows(fields_data)提示field.string是字段的显示名即string客户名称中的值Odoo 17 默认会将该字符串存入ir.translation表并关联到ir.model.fields的field_description字段。若你发现description为空请检查数据库中res_lang是否启用zh_CN且ir.translation表中typemodel且nameir.model.fields,field_description的记录是否存在对应翻译。2.2 补全数据库物理结构PostgreSQL schema 与约束反向映射ORM 元数据不包含索引、唯一约束、外键引用细节如ON DELETE CASCADE这些必须从数据库层面获取。以下 SQL 查询可一次性拉取res_partner表的完整物理结构替换res_partner为任意模型名-- 查询表结构、字段类型、是否为空、默认值、注释 SELECT a.attname AS column_name, pg_catalog.format_type(a.atttypid, a.atttypmod) AS data_type, CASE WHEN a.attnotnull THEN NOT NULL ELSE END AS not_null, pg_get_expr(d.adbin, d.adrelid) AS default_value, col_description(a.attrelid, a.attnum) AS column_comment, -- 外键信息 (SELECT cc.relname FROM pg_class cc JOIN pg_constraint cs ON cs.conrelid cc.oid WHERE cs.conname (SELECT conname FROM pg_constraint WHERE conrelid a.attrelid AND confrelid IN ( SELECT oid FROM pg_class WHERE relname res_partner ) AND conkey ARRAY[a.attnum])) AS fk_target_table, -- 索引信息简化只取主键和唯一索引 (SELECT STRING_AGG(ix.indisunique::text || : || ixind.indexname, ; ) FROM pg_index ix JOIN pg_class ixind ON ixind.oid ix.indexrelid WHERE ix.indrelid a.attrelid AND ix.indkey ARRAY[a.attnum]) AS index_info FROM pg_attribute a LEFT JOIN pg_attrdef d ON a.attrelid d.adrelid AND a.attnum d.adnum WHERE a.attrelid res_partner::regclass AND a.attnum 0 AND NOT a.attisdropped ORDER BY a.attnum;这段 SQL 返回结果包含字段名、PostgreSQL 原生类型如character varying(64)、是否非空、默认值表达式如now()、字段注释即col_description对应COMMENT ON COLUMN、外键目标表、索引类型true:false表示唯一/非唯一。注意pg_get_expr(d.adbin, d.adrelid)解析的默认值可能为nextval(res_partner_id_seq::regclass)这比 ORM 的defaultlambda self: self.env[ir.sequence].next_by_code(res.partner)更底层也更可靠。2.3 合并 ORM 与 DB 元数据生成最终中文数据字典单纯合并两份数据还不够——你需要建立字段名到物理列名的映射。Odoo 17 中绝大多数字段名与数据库列名一致如name→name但存在例外Many2one字段在 DB 中列为xxx_id如user_id→user_idOne2many和Many2many字段不生成数据库列而是通过关联表实现compute字段默认不存库除非加storeTruerelated字段指向其他模型字段DB 中无直接列因此最终字典需分三栏呈现模型名字段名ORM物理列名DB类型ORM类型DB描述中文是否必填是否只读外键目标索引类型res.partnerparent_idparent_idMany2oneinteger上级联系人FalseFalseres.partnerB-tree参数说明type列填 ORM 类型char,integer,many2one等data_type列填 PostgreSQL 类型character varying,integer,timestamp without time zone。index_info中true表示唯一索引如email字段false表示普通索引如name字段。此表格结构可直接导入 Excel 或生成 Markdown 表格供团队共享。3. 避坑Odoo 17 数据字典生成中的 5 个血泪经验生成数据字典看似简单实则极易因版本差异、模块状态、语言配置埋下深坑。以下是我在 3 个 Odoo 17 生产项目中踩过的具体问题按现象→原因→解决逐条拆解3.1 现象field_description字段全为空CSV 导出全是英文原因数据库中res_lang表未启用zh_CN或ir.translation表缺失ir.model.fields,field_description的翻译记录。Odoo 17 默认安装时只激活en_US即使后台切换了语言界面ir.model.fields的field_description仍为英文原文。解决进入 Odoo 后台 → 设置 → 技术 → 翻译 → 加载语言 → 选择Chinese (China)→ 点击“加载”执行 SQL 清空缓存DELETE FROM ir_translation WHERE lang zh_CN AND type model;重启 Odoo 服务再运行odoo-bin shell脚本。若仍为空手动执行env[ir.translation].load_module_terms([base], [zh_CN])3.2 现象res_users表中login字段在字典里显示为char但 DB 中是character varying(64)且NOT NULL而实际插入时允许空字符串原因login字段在res.users模型中定义为requiredTrue但其default为False且 ORM 层对空字符串 的校验逻辑与 DB 层NOT NULL冲突。Odoo 17 的required校验发生在写入前而 DB 的NOT NULL约束在写入后触发导致字典中required与not_null不一致。解决字典中required列应以 ORM 定义为准即Truenot_null列以 DB schema 为准即NOT NULL并在备注栏注明“ORM requiredTrue但允许空字符串DB 层由应用逻辑保证非空”。3.3 现象account.move.line表中price_unit字段在字典里类型为float但 DB 中是numeric且精度为(16,2)原因Odoo 的Float字段在 DB 中映射为numeric(precision, scale)而非double precision。precision和scale参数由digits属性控制如digits(16,2)但odoo-bin shell脚本无法直接读取digits需额外查询ir_model_fields表的digits字段。解决修改导出脚本在fields_data构造中加入digits: getattr(field, digits, None), # 如 (16,2)并在字典表中增加digits列值为元组格式。3.4 现象product.product模型中list_price字段在字典里显示compute但 DB 中无对应列而standard_price却有列原因list_price是compute字段计算逻辑在product.template默认storeFalse故 DB 中无列standard_price是Float字段且storeTrue故有列。但product.product继承自product.templatelist_price的计算结果实际存储在product_template表中。解决字典中physical_column列对compute字段填N/A并在description后追加注释“计算字段逻辑见product.template._compute_list_price结果存储于product_template.list_price”。3.5 现象导出的ir_cron表中interval_number字段类型为integer但 DB 中是integer而interval_type却是selection类型DB 中却是character varying(16)原因selection字段在 DB 中统一存为varchar长度由最大选项字符串决定如[days, hours, weeks]最长为 5但 Odoo 为安全起见设为 16。odoo-bin shell读取field.type为selection但 DB schema 显示character varying(16)二者不矛盾但字典中需明确区分逻辑类型与物理类型。解决字典表中type列填selectiondata_type列填character varying(16)并在description中注明“选项值minutes, hours, days, weeks, months”。4. 字段级深度解读如何用数据字典快速定位业务逻辑链路数据字典的价值不止于“查字段”更在于逆向推导业务规则。以sale.order的amount_total为例它不是简单求和而是由amount_untaxed、amount_tax、amount_discount等多个计算字段联动生成。通过字典你能 3 步锁定其源头4.1 第一步确认字段属性与依赖关系在字典中找到sale.order.amount_total行type:monetarycompute:Truedepends:[order_line.price_subtotal, order_line.tax_id, currency_id]physical_column:N/A因storeFalsedescription:总计金额含税注意depends列明确列出所有触发重新计算的字段这是 Odoo 17 的关键改进——旧版需翻源码找api.depends新版字典直接暴露。4.2 第二步追溯order_line.price_subtotal的物理存储查sale.order.line表字典price_subtotal字段typemonetary,storeTrue,physical_columnprice_subtotal其depends为[product_uom_qty, price_unit, tax_id, discount]DB 中price_subtotal类型为numeric(16,2)这意味着amount_total的计算链路终点是sale_order_line表的price_subtotal列——所有销售单行的子金额都已固化存库amount_total只是聚合。4.3 第三步验证计算逻辑是否可被覆盖字典中amount_total的readonly为True但required为False。这表示你不能在创建时直接赋值amount_totalORM 层会忽略但可通过write({amount_total: 999})强制写入DB 层允许但会破坏计算一致性若需定制逻辑应在sale.order模型中重写_amount_all方法并确保depends包含新字段实战技巧在字典 Excel 中用筛选功能选中computeTrue且storeFalse的字段批量导出为“待审计计算字段清单”。对每个字段执行grep -r _compute.*amount addons/定位源码再结合字典中的depends快速理解影响范围。我曾用此法在 2 小时内定位到一个因currency_id变更未触发amount_total重算的财务对账 Bug。5. 进阶用法构建可检索、可版本化的中文数据字典工作流把字典做成静态 CSV 或 Excel 是初级用法。真正提升团队效率的是将其接入开发流程——让字典成为 IDE 中的“智能提示源”成为 CI 流程中的“变更校验器”。以下是我在 Odoo 17 项目中落地的 3 层架构5.1 层级一本地 VS Code 插件支持零配置利用 Odoo 的__manifest__.py中data字段可加载 XML 文件的特性将字典导出为data_dictionary.xml内容为record格式record idfield_res_partner_name modelir.model.fields field namemodelres.partner/field field namenamename/field field namefield_description名称/field field namettypechar/field field namerequiredTrue/field /record然后在 VS Code 中安装XML Tools插件设置xmlTools.formatOptions为indentSize: 2。当开发者输入self.env[res.partner].search([(na时插件会自动提示name字段及其中文描述“名称”。无需额外插件纯原生 XML 支持。5.2 层级二Git 仓库自动化更新CI 触发在.gitlab-ci.yml或.github/workflows/ci.yml中添加 jobgenerate-data-dict: stage: build script: - cd /opt/odoo/odoo-server - ./odoo-bin shell -d $ODOO_DB --eval import sys; sys.path.append(/scripts); import gen_dict; gen_dict.main() /dev/null artifacts: - docs/data_dict_*.csvgen_dict.py脚本会连接测试数据库$ODOO_DB执行 2.1 节的导出逻辑生成docs/data_dict_timestamp.csv用pandas合并历史版本输出docs/data_dict_latest.csv提交到docs/目录触发 Git LFS 存储大文件好处每次git push后字典自动更新PR 描述中可直接引用data_dict_20240515.csv的某行如“修复res_partner.x_vat_verified字段校验逻辑见字典第 1284 行”。5.3 层级三数据库变更实时告警DB 触发器在 PostgreSQL 中为pg_attribute创建触发器监控res_partner等核心表的 DDL 变更CREATE OR REPLACE FUNCTION notify_field_change() RETURNS TRIGGER AS $$ BEGIN IF TG_OP INSERT AND NEW.attrelid::regclass::text res_partner THEN PERFORM pg_notify(field_change, json_build_object( table, NEW.attrelid::regclass::text, column, NEW.attname, type, pg_catalog.format_type(NEW.atttypid, NEW.atttypmod), time, NOW() )::text); END IF; RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER field_change_trigger AFTER INSERT ON pg_attribute FOR EACH ROW EXECUTE FUNCTION notify_field_change();前端用 Node.js 订阅field_change通道收到通知后自动触发odoo-bin shell重导字典并邮件发送变更摘要“res_partner新增列x_credit_limit类型numeric(16,2)请同步更新数据字典第 321 行”。我坚持每上线一个新模块就跑一次字典生成脚本并把data_dict_latest.csv放进 Confluence 的“系统设计”空间。不是为了文档 KPI而是因为——当新人问‘客户等级字段在哪’时我能直接发链接而不是说‘你去翻 models/res_partner.py 第 234 行’。希望帮到你。本文还有配套的精品资源点击获取
返回列表