第一次接触Odoo模块开发时,我完全被各种配置文件搞晕了。后来发现其实只要掌握几个核心文件,就能快速搭建起一个可运行的模块。让我们以图书管理系统为例,从最基础的模块创建开始。
首先需要确保开发环境已经配置好。我习惯在项目根目录下创建独立的模块文件夹,比如/odoo-dev/my-modules/。这个路径需要添加到Odoo配置文件的addons_path中,否则系统会找不到你的模块。记得每次修改配置文件后都要重启Odoo服务。
创建模块最快捷的方式是使用scaffold命令:
bash复制./odoo-bin scaffold library_app /odoo-dev/my-modules/
这个命令会自动生成模块的基础结构,其中最关键的是两个文件:
__manifest__.py:模块的身份证,包含名称、版本、依赖等元信息__init__.py:Python包的初始化文件我建议第一次创建时先保持默认结构,等熟悉后再做调整。一个常见的坑是忘记在__init__.py中导入子模块,导致模型无法注册。我的习惯是在每个子目录(如models、views)都放一个__init__.py,形成清晰的导入链。
模型是Odoo应用的核心,相当于数据库的表结构。在图书管理系统中,我们至少需要定义Book模型。在models/library_book.py中:
python复制from odoo import fields, models
class LibraryBook(models.Model):
_name = 'library.book'
_description = '图书信息'
name = fields.Char('书名', required=True)
isbn = fields.Char('ISBN')
author = fields.Char('作者')
date_published = fields.Date('出版日期')
cover_image = fields.Binary('封面图片')
active = fields.Boolean('在售', default=True)
这里有几个实用技巧:
models.Model_name要用点号命名法,建议模块名.模型名格式required=True避免空值创建模型后,记得在models/__init__.py中导入:
python复制from . import library_book
模型建好后,我发现普通用户居然看不到数据。这是因为Odoo有严格的权限控制,必须显式授权。权限配置通常放在security/ir.model.access.csv:
csv复制id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_book_user,图书普通权限,model_library_book,base.group_user,1,1,0,0
access_book_manager,图书管理权限,model_library_book,base.group_system,1,1,1,1
这个CSV文件定义了:
实际项目中,我建议创建专门的用户组而不是直接用base.group_user。可以在security/library_security.xml中定义:
xml复制<record id="library_group_librarian" model="res.groups">
<field name="name">图书管理员</field>
<field name="category_id" ref="base.module_category_services_library"/>
<field name="implied_ids" eval="[(4, ref('base.group_user'))]"/>
</record>
有了模型和权限,接下来要让数据在界面上可见。Odoo的视图系统非常灵活,我们先从基础的列表和表单开始。
在views/library_views.xml中定义菜单结构:
xml复制<menuitem id="menu_library_root" name="图书管理系统"/>
<menuitem id="menu_library_books" name="图书管理" parent="menu_library_root" action="action_library_books"/>
然后是窗口动作(点击菜单后触发的操作):
xml复制<record id="action_library_books" model="ir.actions.act_window">
<field name="name">图书列表</field>
<field name="res_model">library.book</field>
<field name="view_mode">tree,form</field>
</record>
接着设计列表视图:
xml复制<record id="view_tree_library_book" model="ir.ui.view">
<field name="name">library.book.tree</field>
<field name="model">library.book</field>
<field name="arch" type="xml">
<tree>
<field name="name"/>
<field name="author"/>
<field name="date_published"/>
</tree>
</field>
</record>
表单视图可以更复杂些:
xml复制<record id="view_form_library_book" model="ir.ui.view">
<field name="name">library.book.form</field>
<field name="model">library.book</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<field name="name"/>
<field name="isbn"/>
<field name="author"/>
<field name="date_published"/>
</group>
<group>
<field name="cover_image" widget="image" options="{'preview_image': 'cover_image'}"/>
</group>
</sheet>
</form>
</field>
</record>
基础CRUD功能已经实现,现在添加一些业务逻辑。比如自动检查ISBN有效性,在models/library_book.py中添加:
python复制from odoo.exceptions import ValidationError
import re
class LibraryBook(models.Model):
# ... 原有字段定义 ...
def _check_isbn(self):
for record in self:
if record.isbn and not re.match(r'^[0-9-]+$', record.isbn):
raise ValidationError("ISBN只能包含数字和连字符")
_constraints = [
(_check_isbn, 'ISBN格式错误', ['isbn'])
]
def button_mark_out_of_print(self):
self.write({'active': False})
return True
这个例子展示了:
_constraints添加数据验证还可以添加智能按钮到表单视图:
xml复制<header>
<button name="button_mark_out_of_print" string="标记绝版" type="object" class="oe_highlight"/>
</header>
好的管理系统离不开高效的查询功能。在列表视图中添加搜索面板:
xml复制<search>
<field name="name" string="书名"/>
<field name="author"/>
<filter name="active" string="在售图书" domain="[('active','=',True)]"/>
<filter name="out_of_print" string="绝版图书" domain="[('active','=',False)]"/>
</search>
对于常用查询,可以定义默认筛选条件:
python复制class LibraryBook(models.Model):
# ... 其他代码 ...
def _search_expensive_books(self, operator, value):
domain = [('retail_price', '>', 100)]
return domain
随着功能增加,模块可能需要依赖其他模块。在__manifest__.py中声明:
python复制{
'depends': ['base', 'web', 'mail'],
# ... 其他配置 ...
}
开发过程中,模型变更需要升级模块。我发现最可靠的操作顺序是:
有时候视图变更不生效,可以尝试:
python复制{
'assets': {
'web.assets_backend': [
'library_app/static/src/css/library.css',
],
}
}
开发过程中难免遇到问题,我常用的调试方法:
?debug=1tail -f /var/log/odoo/odoo-server.log几个常见错误和解决方案:
__init__.py是否导入了模型文件ir.model.access.csv配置正确__manifest__.py的data列表中记得在开发过程中频繁测试,每个小功能完成后都验证一下。我习惯在模块根目录放一个test文件夹,里面放测试用的数据文件和脚本。