第2章:Odoo模块基础——创建第一个Odoo模块
好,咱们直接动手。这一章我会带你从零创建一个Odoo模块。别怕,其实就几个文件的事。我刚开始学的时候,也被那一堆目录结构吓到过,后来发现——说白了,Odoo模块就是个有规矩的文件夹。
2.1 模块的目录结构
一个标准的Odoo模块长这样:
my_module/
├── __init__.py
├── __manifest__.py
├── models/
│ ├── __init__.py
│ └── my_model.py
├── views/
│ ├── my_model_views.xml
│ └── menu_views.xml
├── security/
│ └── ir.model.access.csv
├── data/
│ └── demo_data.xml
├── static/
│ └── description/
│ └── icon.png
└── tests/
├── __init__.py
└── test_my_model.py
嗯,这里要注意:__init__.py 是Python包的标志,每个子目录下都得有。我见过不少新手漏掉 models 目录下的 __init__.py,结果模块死活加载不上——排查了半天,就少了个空文件。
2.2 理解 manifest.py 文件
这是模块的身份证。Odoo启动时,第一个读的就是它。我个人习惯把 __manifest__.py 叫做「模块说明书」。
{
'name': "我的第一个模块",
'version': '1.0',
'category': 'Uncategorized',
'summary': '这是一个学习用的模块',
'description': """
这个模块用来演示Odoo模块的基础结构。
包含模型、视图、权限等核心内容。
""",
'author': "你的名字",
'website': "https://your-website.com",
'depends': ['base'],
'data': [
'security/ir.model.access.csv',
'views/my_model_views.xml',
'views/menu_views.xml',
],
'demo': [
'data/demo_data.xml',
],
'installable': True,
'application': True,
'auto_install': False,
}
关键字段解读:
depends:依赖哪些模块。哪怕你的模块再简单,也得依赖base。我曾经犯过一个错——把依赖写成了['Base'],大写B,结果Odoo报错说找不到模块。大小写敏感,切记。data:模块安装时要加载的XML、CSV文件。顺序很重要,先加载权限文件,再加载视图。demo:演示数据,只在开启演示数据时加载。我建议开发阶段都写上,方便测试。installable:能不能安装。设为False的话,模块列表里都看不到它。
避坑指南: 我曾经把一个模块的 version 写成 '1.0.0',结果升级时Odoo死活不认。后来发现Odoo的版本号格式是 主版本.次版本,比如 17.0.1.0.0。嗯,这个坑踩过一次就记住了。
2.3 模型(Model)与字段(Field)基础
模型就是数据库表,字段就是表的列。Odoo用Python类来定义模型,ORM会自动帮你建表。你想想看,这比直接写SQL爽多了吧?
# models/my_model.py
from odoo import models, fields, api
class LibraryBook(models.Model):
_name = 'library.book'
_description = '图书'
_order = 'name'
name = fields.Char(string='书名', required=True)
author = fields.Char(string='作者')
isbn = fields.Char(string='ISBN号', size=13)
publication_date = fields.Date(string='出版日期')
price = fields.Float(string='价格', digits=(10, 2))
active = fields.Boolean(string='在库', default=True)
category_id = fields.Many2one('library.category', string='分类')
reader_ids = fields.Many2many('library.reader', string='借阅者')
这里有几个要点:
_name是模型的唯一标识,用点号分隔。我习惯用模块名.模型名的命名方式,比如library.book。_description是模型的中文描述,会在界面显示。_order指定默认排序字段,不写的话按ID排序。
字段类型我列个表,方便你查阅:
| 字段类型 | Python类型 | 说明 |
|---|---|---|
| Char | str | 字符串,最长255字符 |
| Text | str | 长文本,无长度限制 |
| Integer | int | 整数 |
| Float | float | 浮点数,可指定精度 |
| Boolean | bool | 布尔值 |
| Date | date | 日期 |
| Datetime | datetime | 日期时间 |
| Many2one | int | 多对一关系,存关联记录的ID |
| One2many | list | 一对多关系,存关联记录的ID列表 |
| Many2many | list | 多对多关系,通过中间表关联 |
注意: 字段名不要用Python保留字,比如 type、id、class。我见过有人用 type 做字段名,结果ORM报错说语法错误——改个名就好了。
2.4 视图(View)基础
视图就是界面。Odoo的视图用XML定义,说白了就是告诉系统:这个字段放左边,那个按钮放上面。
先看一个最简单的树形视图(列表视图):
<!-- views/my_model_views.xml -->
<odoo>
<record id="view_library_book_tree" model="ir.ui.view">
<field name="name">library.book.tree</field>
<field name="model">library.book</field>
<field name="arch" type="xml">
<tree string="图书列表">
<field name="name"/>
<field name="author"/>
<field name="price"/>
<field name="active"/>
</tree>
</field>
</record>
<record id="view_library_book_form" model="ir.ui.view">
<field name="name">library.book.form</field>
<field name="model">library.book</field>
<field name="arch" type="xml">
<form string="图书">
<sheet>
<group>
<field name="name"/>
<field name="author"/>
<field name="isbn"/>
<field name="publication_date"/>
<field name="price"/>
<field name="category_id"/>
</group>
</sheet>
</form>
</field>
</record>
</odoo>
然后加上菜单,不然用户找不到入口:
<!-- views/menu_views.xml -->
<odoo>
<menuitem id="menu_library_root" name="图书管理" sequence="10"/>
<menuitem id="menu_library_book" name="图书列表"
parent="menu_library_root"
action="action_library_book"
sequence="10"/>
<record id="action_library_book" model="ir.actions.act_window">
<field name="name">图书</field>
<field name="res_model">library.book</field>
<field name="view_mode">tree,form</field>
</record>
</odoo>
个人经验: 我刚开始写视图时,经常忘记在 __manifest__.py 的 data 列表里加上XML文件。结果模块安装后,菜单都看不到。排查步骤很简单:先看模块有没有报错,再看视图有没有加载。嗯,现在我会在写XML的同时就把manifest更新了。
2.5 权限文件——别漏了它
没有权限文件,你的模型在界面上就是只读的。Odoo的权限用CSV文件定义:
# security/ir.model.access.csv
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_library_book_user,library.book.user,model_library_book,base.group_user,1,1,1,1
这行CSV的意思是:给所有内部用户(base.group_user)对 library.book 模型的全部权限(读、写、创建、删除)。
我曾经踩过的坑: CSV文件的列名不能有空格,而且顺序必须严格按照 id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink 来。有一次我手抖把 perm_read 写成了 perm read,中间多了个空格——Odoo直接忽略了这个权限,用户打开模块就报权限错误。
2.6 完整流程:从创建到安装
- 在
addons目录下创建模块文件夹,比如library。 - 创建
__init__.py和__manifest__.py。 - 创建
models/目录,写模型文件。 - 创建
views/目录,写视图和菜单。 - 创建
security/目录,写权限CSV。 - 重启Odoo服务,进入「应用」菜单,搜索你的模块,点击安装。
如果一切顺利,你就能在菜单里看到「图书管理」了。如果看不到——别急,先检查Odoo日志,大部分问题日志里都有提示。
好了,这一章的内容就这些。你跟着敲一遍,应该半小时就能跑起来。下一章我们会深入模型的关系字段,讲讲One2many和Many2many怎么用——那可是Odoo的精髓所在。