第二十七章 编码规范

篇别:第四篇 开发规范

本章学习目标

  • 在团队中落地 PEP 8 与自动化检查(ruff / flake8 等)。
  • 统一 模型、字段、方法、模块、文件 的命名与 目录布局
  • 掌握 Odoo 19 废弃写法 的迁移入口(upgrade_code 等)与 XML/视图 可读性约定。
  • 能写出 可审查xpath 继承(带注释、可定位上游)。

导读:规范是「可执行的共识」

个人风格各异的代码在单人模块中尚可忍受;多模块、多作者、长生命周期时,命名与目录不一致 会直接拖慢 Code ReviewOnboarding。本章把 Python、XML、模块结构 中最常踩坑的点固定成 检查清单;与 第二十八章调试第二十九章测试 配合,形成 写得好 → 测得到 → 查得快 的闭环。


27.1 PEP 8 规范

27.1.1 知识要点

  • 缩进与行宽:4 空格;常用 88 / 100 / 120 列宽团队需统一,并与 ruff / black 配置一致。
  • 导入顺序:标准库 → 第三方 → Odoo → 本模块;避免 from odoo import *
  • 字符串:用户可见文案用 _() 包裹以便 i18n;日志可用 惰性 % 格式化 减少无谓拼接。
  • 比较:与 Noneis;布尔上下文避免 == 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 CI 中 ruff 检查通过

27.1.4 本节练习

  1. 实操:对自定义模块运行 ruff check .,修复 一类 问题(如未使用导入)。
  2. 简答:为何 日志 推荐 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.pyclass library_book → 建议 library_book.py + class LibraryBook,并在 __init__.pyfrom . import library_book

27.2.3 本节练习

  1. 改错def getData()field_Name 如何调整?
  2. 判断:XML ID 在整个数据库中必须全局唯一,通常以 模块名 为前缀。( )

参考答案提示:2. 对。


27.3 目录结构

27.3.1 知识要点

  • 常见顶层models/views/security/data/static/i18n/tests/report/wizard/
  • __manifest__.pydependsdata 顺序建议 权限 → 数据 → 视图 → 菜单,减少 加载期引用错误
  • tests/__init__.py 导入子测试模块;用 @tagged 区分 at_install / post_install
  • demo/:演示数据;注意 Odoo 19 演示数据默认策略 变化(参见第十一章)。
  • OCA 惯例readme/static/descriptionsetup/ 等;对内项目可简化,但 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-2 OCA 风格模块目录(参考)

27.3.4 本节练习

  1. 简答tests/demo/ 分离的好处?
  2. 实操:为你负责模块画 依赖树depends),检查 无环、无冗余

参考答案提示:1. 测试可重复、不污染演示库;CI 不加载 demo。


27.4 Odoo 19 废弃项清单(新增)

27.4.1 知识要点

  • odoo.osv:改用 odoo.modelsodoo.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 upgrade_code 输出摘要

27.4.4 本节练习

  1. 实操:在副本分支跑一次 upgrade_code,记录 三类 典型改写。
  2. 简答:自动化迁移工具为何不能 100% 替代人工?

参考答案提示:2. 语义复杂、第三方补丁、业务自定义 API 无法静态推断。


27.5 XML 与视图编写规范(新增)

27.5.1 知识要点

  • 文件头<?xml version="1.0" encoding="utf-8"?>;根节点 odoodata 与项目统一。
  • recordidmodel 必填;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 视图继承在 IDE 中的折叠与 xpath 注释

27.5.4 本节练习

  1. 实操:为模块内 所有 xpath一行注释(上游视图 XML ID + 选该锚点原因)。
  2. 简答noupdate="1" 下升级模块时,本模块 XML 修改 是否一定写回数据库?

参考答案提示:2. 不一定;常需 forcecreate / 临时 noupdate 调整 或数据迁移脚本,依数据文件策略而定。


本章综合练习

  1. 制定团队 Code Review 清单10 条以内,覆盖 安全、性能、命名、测试)。
  2. pre-commit:列出 不少于 4 个 钩子(如 ruff、trim trailing whitespace、xml lint 等)及用途。
  3. 综合:新同事提交 2000 行单文件视图,你如何建议 拆分与命名
  4. 判断:PEP 8 是 Python 语法的一部分,违反则解释器报错。( )

参考答案提示:4. 错;是风格指南。


本章对应白皮书目录:第二十七章 编码规范。