如何构建自定义 Odoo 模块:OWL、ORM 和继承开发人员指南

构建自定义 Odoo 模块的开发人员指南。涵盖模块结构、OWL框架、ORM继承、视图、安全规则、测试和OCA指南。

E

ECOSIRE Research and Development Team

ECOSIRE 团队

2026年2月19日4 分钟阅读799 字数

如何构建自定义 Odoo 模块:OWL、ORM 和继承开发人员指南

当 Odoo 的 82 个官方模块和 40,000 多个社区模块不能满足您的具体业务需求时,自定义模块开发可以填补空白。本指南涵盖了 2026 年构建 Odoo 模块的基础知识,包括模块结构、OWL 前端框架、ORM 模式、继承机制以及符合 OCA(Odoo 社区协会)指南的最佳实践。

什么是自定义 Odoo 模块?

自定义 Odoo 模块 是一个独立的包,包含 Python 后端逻辑、XML 视图定义、JavaScript 前端组件和扩展 Odoo 功能的安全规则。模块可以添加全新功能、修改现有行为或将 Odoo 与外部系统集成。 Odoo 本身的每个部分都是一个模块,使得该架构具有本质上的可扩展性。

模块目录结构

组织良好的模块遵循以下标准结构:

my_custom_module/
├── __init__.py              # Python package init
├── __manifest__.py          # Module metadata and dependencies
├── models/
│   ├── __init__.py
│   └── my_model.py          # Business logic and data models
├── views/
│   ├── my_model_views.xml   # Form, tree, and kanban views
│   └── menu.xml             # Menu items and actions
├── security/
│   ├── ir.model.access.csv  # Access control list
│   └── security.xml         # Record rules and groups
├── data/
│   └── data.xml             # Default data records
├── static/
│   └── src/
│       ├── js/              # OWL components
│       ├── css/             # Stylesheets
│       └── xml/             # QWeb templates
├── wizard/                  # Transient models for wizards
├── reports/                 # QWeb report templates
└── tests/                   # Unit tests

清单文件

__manifest__.py 文件定义模块的标识:

{
    'name': 'My Custom Module',
    'version': '18.0.1.0.0',
    'category': 'Custom',
    'summary': 'Short description of what the module does',
    'description': """
        Long description with details about
        features and configuration.
    """,
    'author': 'ECOSIRE',
    'website': 'https://ecosire.com',
    'license': 'LGPL-3',
    'depends': ['base', 'sale', 'stock'],
    'data': [
        'security/ir.model.access.csv',
        'views/my_model_views.xml',
        'views/menu.xml',
        'data/data.xml',
    ],
    'assets': {
        'web.assets_backend': [
            'my_custom_module/static/src/js/**/*',
            'my_custom_module/static/src/css/**/*',
            'my_custom_module/static/src/xml/**/*',
        ],
    },
    'installable': True,
    'application': False,
    'auto_install': False,
}

版本约定: {odoo_version}.{major}.{minor}.{patch}(例如,18.0.1.0.0)。

使用 ORM

Odoo 的对象关系映射(ORM)是所有后端开发的基础。模型映射到数据库表,ORM 提供 CRUD 操作、计算字段、约束和工作流管理。

定义模型

from odoo import models, fields, api

class ProjectTask(models.Model):
    _name = 'my_module.task'
    _description = 'Project Task'
    _order = 'priority desc, create_date desc'

    name = fields.Char(string='Task Name', required=True)
    description = fields.Html(string='Description')
    state = fields.Selection([
        ('draft', 'Draft'),
        ('in_progress', 'In Progress'),
        ('done', 'Done'),
        ('cancelled', 'Cancelled'),
    ], default='draft', tracking=True)
    assigned_to = fields.Many2one('res.users', string='Assigned To')
    deadline = fields.Date(string='Deadline')
    priority = fields.Selection([
        ('0', 'Normal'),
        ('1', 'Important'),
        ('2', 'Urgent'),
    ], default='0')
    tag_ids = fields.Many2many('my_module.tag', string='Tags')
    progress = fields.Float(compute='_compute_progress', store=True)

字段类型参考

|字段类型| Python 类型 |使用案例| |---|---|---| |查尔 | STR |短文本(名称、参考文献)| |文字| STR |长纯文本 | |网页 | STR |富文本内容 | |整数|整数 |整数| |浮动 |浮动|小数 | |布尔 |布尔 |真/假标志 | |日期 |日期 |没有时间的日期 | |日期时间 |日期时间 |日期与时间 | |选择| STR |下拉选项 | |多对一 |整数 |链接到一条记录 | |一对多 |列表 | Many2one 的反向 | |多对多 |列表 |链接到多个记录 | |二进制|字节|文件附件 |

计算字段和约束

@api.depends('subtask_ids.state')
def _compute_progress(self):
    for task in self:
        total = len(task.subtask_ids)
        done = len(task.subtask_ids.filtered(
            lambda t: t.state == 'done'
        ))
        task.progress = (done / total * 100) if total else 0

@api.constrains('deadline')
def _check_deadline(self):
    for task in self:
        if task.deadline and task.deadline < fields.Date.today():
            raise ValidationError(
                "Deadline cannot be in the past."
            )

继承机制

Odoo 提供了三种类型的继承,每种类型都有不同的用途:

1.类继承(扩展)

通过添加字段或覆盖方法来扩展现有模型。这是最常见的模式。

class SaleOrderExtend(models.Model):
    _inherit = 'sale.order'

    custom_reference = fields.Char(string='Custom Ref')
    approved_by = fields.Many2one('res.users')

    def action_confirm(self):
        # Add custom logic before standard confirmation
        for order in self:
            if order.amount_total > 10000 and not order.approved_by:
                raise UserError("Orders over $10,000 require approval.")
        return super().action_confirm()

2.原型继承

创建一个新模型,复制现有模型中的所有字段和方法。

class CustomPartner(models.Model):
    _name = 'my_module.partner'
    _inherit = 'res.partner'   # Copies structure
    _description = 'Custom Partner'

3.委托继承

创建一个新模型,通过 Many2one 链接委托给现有模型。父模型的字段透明地显示在子模型上。

class LibraryMember(models.Model):
    _name = 'library.member'
    _inherits = {'res.partner': 'partner_id'}

    partner_id = fields.Many2one('res.partner', required=True,
                                  ondelete='cascade')
    membership_date = fields.Date()
    member_number = fields.Char()

OWL 框架(前端)

Odoo 18 使用 OWL(Odoo Web Library)作为其前端框架。 OWL 是一个基于组件的框架,类似于 React 或 Vue,但专为 Odoo 的需求而设计。

基本 OWL 组件

/** @odoo-module */

import { Component, useState } from "@odoo/owl";
import { registry } from "@web/core/registry";

class TaskDashboard extends Component {
    static template = "my_module.TaskDashboard";

    setup() {
        this.state = useState({
            tasks: [],
            filter: 'all',
        });
        this.loadTasks();
    }

    async loadTasks() {
        this.state.tasks = await this.env.services.orm.searchRead(
            "my_module.task",
            [["state", "!=", "cancelled"]],
            ["name", "state", "assigned_to", "deadline"]
        );
    }

    get filteredTasks() {
        if (this.state.filter === 'all') return this.state.tasks;
        return this.state.tasks.filter(
            t => t.state === this.state.filter
        );
    }
}

QWeb 模板 (XML)

<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
    <t t-name="my_module.TaskDashboard">
        <div class="o_task_dashboard">
            <div class="task-filters">
                <button t-on-click="() => state.filter = 'all'">All</button>
                <button t-on-click="() => state.filter = 'in_progress'">
                    In Progress
                </button>
            </div>
            <div class="task-list">
                <t t-foreach="filteredTasks" t-as="task" t-key="task.id">
                    <div class="task-card">
                        <span t-esc="task.name"/>
                    </div>
                </t>
            </div>
        </div>
    </t>
</templates>

安全配置

每个模型都需要明确的访问规则。没有它们,任何用户都无法访问模型的数据。

访问控制列表 (ir.model.access.csv)

id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_task_user,task.user,model_my_module_task,base.group_user,1,1,1,0
access_task_manager,task.manager,model_my_module_task,my_module.group_manager,1,1,1,1

记录规则(security.xml)

<record id="task_own_rule" model="ir.rule">
    <field name="name">Own Tasks Only</field>
    <field name="model_id" ref="model_my_module_task"/>
    <field name="domain_force">
        [('assigned_to', '=', user.id)]
    </field>
    <field name="groups" eval="[(4, ref('base.group_user'))]"/>
</record>

测试你的模块

Odoo 支持使用 unittest 框架和 Odoo 特定测试类的 Python 单元测试:

from odoo.tests.common import TransactionCase

class TestTask(TransactionCase):
    def setUp(self):
        super().setUp()
        self.task = self.env['my_module.task'].create({
            'name': 'Test Task',
            'state': 'draft',
        })

    def test_task_creation(self):
        self.assertEqual(self.task.state, 'draft')
        self.assertEqual(self.task.progress, 0)

    def test_deadline_constraint(self):
        with self.assertRaises(ValidationError):
            self.task.write({
                'deadline': fields.Date.subtract(
                    fields.Date.today(), days=1
                ),
            })

使用以下命令运行测试: odoo-bin -d test_db --test-enable -i my_custom_module --stop-after-init

常见问题

问:构建自定义 Odoo 模块需要多长时间? 简单的模块(新字段、基本视图)需要 1-3 天。中等模块(新模型、工作流程、报告)需要 1-3 周。复杂的模块(多模型系统、外部集成、自定义 OWL 组件)需要 4-12 周。对于需要自定义模块但缺乏内部 Odoo 开发人员的企业,请从我们的团队中聘请经验丰富的 Odoo 开发人员

问:我应该修改核心 Odoo 代码还是创建一个单独的模块? 始终创建一个单独的模块。修改核心代码会破坏可升级性并在版本更新期间产生合并冲突。使用继承来扩展自定义模块中的现有模型和视图。

问:OCA 指南是什么? Odoo 社区协会 (OCA) 发布了模块质量的编码标准,包括命名约定、文档要求、测试覆盖率期望和代码风格规则。遵循 OCA 指南可确保您的模块可维护并与更广泛的社区生态系统兼容。

获得专业帮助

构建自定义 Odoo 模块需要 Python、JavaScript、PostgreSQL 和 Odoo 框架约定方面的专业知识。无论您需要简单的现场扩展还是复杂的多模块系统,ECOSIRE 的 Odoo 定制服务 都能提供根据 OCA 标准构建的生产就绪模块。

联系我们 讨论您的自定义模块需求并获得开发估算。

分享:
E

作者

ECOSIRE Research and Development Team

在 ECOSIRE 构建企业级数字产品。分享关于 Odoo 集成、电商自动化和 AI 驱动商业解决方案的洞见。

通过 WhatsApp 聊天