第二十七章 编码规范
篇别:第四篇 开发规范
本章学习目标
- 在团队中落地 PEP 8 与自动化检查(ruff / flake8 等)。
- 统一 模型、字段、方法、模块、文件 的命名与 目录布局。
- 掌握 Odoo 19 废弃写法 的迁移入口(
upgrade_code等)与 XML/视图 可读性约定。 - 能写出 可审查 的 xpath 继承(带注释、可定位上游)。
导读:规范是「可执行的共识」
个人风格各异的代码在单人模块中尚可忍受;多模块、多作者、长生命周期时,命名与目录不一致 会直接拖慢 Code Review 与 Onboarding。本章把 Python、XML、模块结构 中最常踩坑的点固定成 检查清单;与 第二十八章调试、第二十九章测试 配合,形成 写得好 → 测得到 → 查得快 的闭环。
27.1 PEP 8 规范
27.1.1 知识要点
- 缩进与行宽:4 空格;常用 88 / 100 / 120 列宽团队需统一,并与 ruff / black 配置一致。
- 导入顺序:标准库 → 第三方 → Odoo → 本模块;避免
from odoo import *。 - 字符串:用户可见文案用
_()包裹以便 i18n;日志可用 惰性%格式化 减少无谓拼接。 - 比较:与
None用is;布尔上下文避免== True。 - CI:合并前跑
ruff check(及可选ruff format);与 pre-commit 钩子一致,避免「本地绿、CI 红」。
27.1.2 案例
pyproject.toml 片段(示意)
[tool.ruff]
line-length = 100
target-version = "py312"
[tool.ruff.lint]
select = ["E", "F", "I", "UP"]
27.1.3 截图占位

27.1.4 本节练习
- 实操:对自定义模块运行
ruff check .,修复 一类 问题(如未使用导入)。 - 简答:为何 日志 推荐
logger.info("id=%s", rec.id)而非 f-string?
参考答案提示:2. 避免在日志级别被过滤时仍做 昂贵字符串格式化。
27.2 命名规范
27.2.1 知识要点
| 对象 | 约定 | 示例 |
|---|---|---|
| 模型名 | snake_case,点分模块前缀 |
library.book |
| Python 文件 | 与模型或功能区一致 | library_book.py |
| 模型类 | CamelCase |
class LibraryBook(models.Model) |
| 字段名 | snake_case,Many2one 常用 _id |
partner_id |
| 私有方法 | 前缀 _ |
_compute_available |
| XML ID | 模块前缀 + 描述 | library.action_book |
- 方法名:
compute_*、inverse_*、search_*、action_*等与官方风格对齐;按钮方法 常用action_前缀。 - 避免:与 内置 / ORM 方法 同名覆盖;单字母字段(除少量数学模型外)。
27.2.2 案例
改错:文件 books.py 中 class library_book → 建议 library_book.py + class LibraryBook,并在 __init__.py 中 from . import library_book。
27.2.3 本节练习
- 改错:
def getData()、field_Name如何调整? - 判断:XML ID 在整个数据库中必须全局唯一,通常以 模块名 为前缀。( )
参考答案提示:2. 对。
27.3 目录结构
27.3.1 知识要点
- 常见顶层:
models/、views/、security/、data/、static/、i18n/、tests/、report/、wizard/。 __manifest__.py:depends、data顺序建议 权限 → 数据 → 视图 → 菜单,减少 加载期引用错误。tests/:__init__.py导入子测试模块;用@tagged区分 at_install / post_install。demo/:演示数据;注意 Odoo 19 演示数据默认策略 变化(参见第十一章)。- OCA 惯例:
readme/、static/description、setup/等;对内项目可简化,但 security 与 tests 勿省。
27.3.2 案例
my_module/
__init__.py
__manifest__.py
models/
__init__.py
my_model.py
views/
my_model_views.xml
security/
ir.model.access.csv
my_model_rules.xml
tests/
__init__.py
test_my_model.py
27.3.3 截图占位

27.3.4 本节练习
- 简答:
tests/与demo/分离的好处? - 实操:为你负责模块画 依赖树(
depends),检查 无环、无冗余。
参考答案提示:1. 测试可重复、不污染演示库;CI 不加载 demo。
27.4 Odoo 19 废弃项清单(新增)
27.4.1 知识要点
odoo.osv:改用odoo.models、odoo.fields等。- 控制器:
type='json'等旧写法迁移为type='jsonrpc'(以 19 文档为准)。 - 记录环境:避免
record._cr/_uid/_context,统一record.env.*。 - 升级工具:官方提供
upgrade_code(CLI)辅助扫描与改写,必须人工 diff,不可盲合并。
27.4.2 案例
# 旧(避免)
# from odoo.osv import osv
# 新
from odoo import models, fields, api
27.4.3 截图占位

27.4.4 本节练习
- 实操:在副本分支跑一次
upgrade_code,记录 三类 典型改写。 - 简答:自动化迁移工具为何不能 100% 替代人工?
参考答案提示:2. 语义复杂、第三方补丁、业务自定义 API 无法静态推断。
27.5 XML 与视图编写规范(新增)
27.5.1 知识要点
- 文件头:
<?xml version="1.0" encoding="utf-8"?>;根节点odoo或data与项目统一。 - record:
id与model必填;noupdate用于 手工可改数据 时谨慎使用。 - xpath:唯一锚点;注明上游模块与视图 ID;避免 过于脆弱 的
position="inside"无标识插入。 - 优先级:
priority控制继承链顺序;冲突时 显式调数字并 注释原因。 - 可读性:多视图文件 按 模型 或 功能 拆分,单文件不宜过大。
27.5.2 案例
<!-- 继承 sale.order 表单:在 sheet 内 notebook 前插入页(示例锚点请以实际为准) -->
<record id="view_order_form_inherit_library" model="ir.ui.view">
<field name="name">sale.order.form.inherit.library</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form"/>
<field name="arch" type="xml">
<xpath expr="//sheet" position="inside">
<group string="图书馆扩展" groups="library.group_library_user">
<field name="x_library_note"/>
</group>
</xpath>
</field>
</record>
27.5.3 截图占位

27.5.4 本节练习
- 实操:为模块内 所有 xpath 补 一行注释(上游视图 XML ID + 选该锚点原因)。
- 简答:
noupdate="1"下升级模块时,本模块 XML 修改 是否一定写回数据库?
参考答案提示:2. 不一定;常需 forcecreate / 临时 noupdate 调整 或数据迁移脚本,依数据文件策略而定。
本章综合练习
- 制定团队 Code Review 清单(10 条以内,覆盖 安全、性能、命名、测试)。
- pre-commit:列出 不少于 4 个 钩子(如 ruff、trim trailing whitespace、xml lint 等)及用途。
- 综合:新同事提交 2000 行单文件视图,你如何建议 拆分与命名?
- 判断:PEP 8 是 Python 语法的一部分,违反则解释器报错。( )
参考答案提示:4. 错;是风格指南。
本章对应白皮书目录:第二十七章 编码规范。