跳至主要内容

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

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

E
ECOSIRE Research and Development Team
|2026年2月19日4 分钟阅读794 字数|

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

当 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 Team

Technical Writing

The ECOSIRE technical writing team covers Odoo ERP, Shopify eCommerce, AI agents, Power BI analytics, GoHighLevel automation, and enterprise software best practices. Our guides help businesses make informed technology decisions.

ECOSIRE

使用 Odoo ERP 转变您的业务

专家 Odoo 实施、定制和支持可简化您的运营。