第二章 Hello Odoo
本章我们将带大家一起来做一个学校模块,其中包含班级,教师,学生和成绩等信息,我们将此模块命名为mommy_school,模块的命名一定要体现出模块的用途,且不要跟已有的模块名重名,所以在模块前加上公司名是一个不错的选择。
脚手架
Odoo给我们提供了一个快速创建模块的命令:
odoo scaffold mommy_school .
执行完命令之后,我们会在目标文件夹下看到如下的文件夹:
drwxr-xr-x 10 root root 4096 Oct 30 14:08 ./
drwxr-xr-x 60 root root 4096 Oct 30 14:08 ../
-rw-r--r-- 1 root root 68 Oct 30 14:10 changelog.md
drwxr-xr-x 3 root root 4096 Oct 16 08:21 controllers/
drwxr-xr-x 2 root root 4096 Oct 16 08:21 demo/
-rw-r--r-- 1 root root 71 Oct 16 08:21 __init__.py
-rw-r--r-- 1 root root 1108 Oct 30 14:11 __manifest__.py
drwxr-xr-x 3 root root 4096 Oct 30 14:08 models/
drwxr-xr-x 2 root root 4096 Apr 15 2024 __pycache__/
-rw-r--r-- 1 root root 41 Oct 30 14:10 README.md
drwxr-xr-x 2 root root 4096 Oct 16 08:21 security/
drwxr-xr-x 4 root root 4096 Oct 16 08:21 static/
drwxr-xr-x 2 root root 4096 Oct 16 08:21 tests/
drwxr-xr-x 2 root root 4096 Oct 16 08:21 views/
然后我们再根据我们的实际情况对其中的某些文件进行修改就可以了。
odoo模块结构
现在我们来认识一下这些文件夹的作用:
- controllers: 主要是一些HTTP请求相关的业务逻辑。
- models: 模型文件夹,业务的模型都放在这里。
- security: 权限相关的文件。
- views: 视图文件夹,关于界面布局的逻辑在这里。
- static: 静态文件夹,前端使用的qweb文件和js、css文件和图片等静态文件放到这个文件夹内。
- __init__:熟悉python的都懂,不说了
- __manifest__: 9.0之前叫_openerp_.py,Odoo模块必须的文件,包含必要的模块信息。
mainfest
接下来我们修改一下模块的信息,修改_manifest_.py文件,如以下:
# -*- coding: utf-8 -*-
{
'name': "mommy_shcool",
'summary': """
demo module for tutorial.""",
'description': """
This module is writing for who wants learn odoo developments.
""",
'author': "KevinKong",
'website': "https://www.odoomommy.com",
# Categories can be used to filter modules in modules listing
# Check https://github.com/odoo/odoo/blob/12.0/odoo/addons/base/data/ir_module_category_data.xml
# for the full list
'category': 'tutorial',
'version': '18.0.1.0',
# any module necessary for this one to work correctly
'depends': ['base'],
# always loaded
'data': [
'security/ir.model.access.csv',
'views/views.xml',
# 'views/templates.xml',
],
# only loaded in demonstration mode
'demo': [
'demo/demo.xml',
],
"application": True
}
常见的manifest中的键值信息有:
- name:模块名称,在模块列表中显示的名称,仅作名称使用,没有限制条件。
- summary: 模块的摘要信息,通常比较简短。
- description: 模块的详细信息,用于说明模块如何使用,说明文档等内容,可以放置图文信息以帮助用户更好地使用模块。
- author:作者信息
- website:模块的主页
- category: 模块的分类,没有限制,可以自己新加分类,建议根据模块的用途进行分类。
- version:版本信息, 推荐在版本中包含odoo的版本信息,例如18.0.1.0(17.0版本之前可以不写17.0)
- data:数组,需要加载的静态文件路径,通常放的是视图文件、权限文件csv和qweb模板文件的路径。
- demo:演示数据
- application: 布尔值,标示本模块是否为一个应用。
- qweb: qweb文件路径
- installable: 布尔值,标示本模块是否可以被安装。
- external_dependencies: 模块的外部依赖,通常用来填写Python的依赖包。({'python':['openpyxl','simplejson']})
加载模块
写完这个,我们可以验证一下Odoo是否能够正确加载我们的模块。
模块的路径需要写到配置文件中,如果是笔者这种安装方式,配置文件在/etc/odoo/odoo.conf
启动odoo后,打开debug模式(URL/web?debug=1),在应用-更新本地模块中点击更新:
然后再搜索mommy_shcool:
这样就说明odoo正确的加载了我们的模块。
第一个模块
接下来我们来写我们的第一个模块。
定义模型
odoo开发的第一步就是要先建立自己的数据模型,也就是我们常说的models,模型对应的是数据库中的数据表,但是模型的概念比表更多,很多属性并不存储在数据库中。现在,我们先建立一个学生的档案信息。这里我们将此学生模型命名为mommy.school.student, 一个学生通常可以有一下属性:
- 姓名
- 性别
- 出生日期
- 身份证号
- 年龄
- 籍贯
- 身高
- 体重 ...
姓名是字符串,因此使用Char类型,性别只有男和女两种,因此可以使用Selection的类型来控制选择范围。出生日期是日期/时间类型,这里我们选择日期类型。身份证号是一个18位的固定字符串,使用Char类型。籍贯是一个可以选择的类型,对应Odoo内置的省份字段,使用Many2one类型。身高是一个数字,使用Integer类型,体重是一个带小数的数字,因此使用Float类型。
因此我们的模型可以写成下面的代码:
class mommy_school_student(models.Model):
_name = "mommy.school.student"
_description = "学生"
age = fields.Integer("年龄")
birth_date = fields.Date("出生日期")
birth_place = fields.Many2one("res.country.state",string="籍贯")
height = fields.Integer("身高")
gender = fields.Selection(GENDERS,string="性别")
id_no = fields.Char("身份证号")
name = fields.Char("姓名")
weight = fields.Float("体重")
odoo所有的持久化模型都要继承自models.Model类,当然也存在于另外一种非持久化的类,这个等到后面再详细介绍。这里,我们先简单记住,所有的要在数据库中建立表结构的对象,都要继承自models.Model类。
定义视图文件
odoo能够快速开发应用的一个很重要的原因就是它封装了很多常见的操作,像创建、编辑、搜索和删除等,默认情况下,我们不用重写这些逻辑就可以使用,甚至不用写视图文件。但是为了说明起见,我们这里还是要编写一些视图文件。
表单视图
我们首先来写学生的表单视图:
<record id="view_mommy_school_student_form" model="ir.ui.view">
<field name="name">student form</field>
<field name="model">mommy.school.student</field>
<field name="arch" type="xml">
<form string="" class="">
<sheet>
<div class="oe_title">
<label for="name" string="name" class="oe_edit_only"/>
<h1>
<field name="name"/>
</h1>
</div>
<group>
<field name="gender"/>
<field name="age"/>
<field name="birth_date"/>
<field name="birth_place"/>
<field name="height"/>
<field name="weight"/>
<field name="id_no"/>
</group>
</sheet>
</form>
</field>
</record>
列表视图
然后我们写列表视图:
<record id="view_mommy_school_student_list" model="ir.ui.view">
<field name="name">student list</field>
<field name="model">mommy.school.student</field>
<field name="priority">1</field>
<field name="arch" type="xml">
<list string="">
<field name="name"/>
<field name="gender"/>
<field name="age"/>
<field name="birth_date"/>
<field name="birth_place"/>
<field name="id_no"/>
<field name="height"/>
<field name="weight"/>
</list>
</field>
</record>
动作
动作是指在odoo中打开一个视图的动作,动作可以绑定多个视图,下面的例子我们就绑定了刚才写的列表和表单视图:
<record id="mommy_school_student" model="ir.actions.act_window">
<field name="name">Student</field>
<field name="path">student</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">mommy.school.student</field>
<field name="view_mode">list,form</field>
</record>
菜单
动作的触发有多个条件,这里我们以菜单绑定的动作作为示例:
<menuitem id="menu_mommy_school_root" name="School" web_icon="mommy_school,static/description/icon.png" />
<menuitem id="menu_mommy_school_student" name="Student" parent="menu_mommy_school_root" action="mommy_school_student" sequence="1"/>
我们这里创建了两个菜单,第一个是模块的根菜单,第二个是我们应用下面的学生菜单。
加载更改后的模块
XML文件写完以后,在应用-模块中点击升级模块对模块进行升级。
当修改了py文件时,需要重启odoo进程,而修改了xml等静态文件则需要升级模块。
升级完成后,进入odoo发现没有我们写完的menu。这是因为,我们只创建了model却没有给任何一个组赋予访问这个模型的权限,因此我们无权查看这个模块的界面。
添加模型的访问权限
默认情况下,我们不拥有对新创建的对象的访问权限,因此我们需要在安装模块时,给指定的用户添加访问权限。odoo提供的方式之一,就是按照一定的格式编写一个csv文件,声明在manifest文件中。这样当升级模块时,odoo就能够读取csv文件中的权限设置,并写入到数据库中。
给book模型添加权限,编辑security文件夹中的ir.model.access.csv文件,csv文件从左右到依次是,id,名称,模型id,组ID,读权限,写权限,创建权限、删除权限。
其中模型是model_加上模型名组成的,其中的点号要换成下划线。
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_mommy_school_student,access_mommy_school_student,model_mommy_school_student,,1,1,1,1
- id: 该权限记录的唯一标示
- name: 访问权限的名字
- model_id:id: 模型名称,格式为 模块名.模型名
- group_id:id: 设置的用户组,格式为 模块名.组xml_id
- perm_read: 对应访问权限
- perm_write: 对应编辑权限
- perm_create: 对应创建权限
- perm_unlink: 对应删除权限
后四列,当为1时表示拥有这个权限,0代表没有权限。
设置完权限,我们重新升级模块,就可以看见我们自定义的菜单出来了:
点击菜单我们可以创建一个学生:
查看学生列表:
开发者调试模式
正常情况下,代码文件发生改变需要重启odoo进程或升级模块,对开发者来说,频发的Ctrl+C和upgrade有时候比较费事,Odoo提供了一种免重启的模式,当代码文件发生变化时自动重载并更新视图文件,大大简化了开发的步骤。
启动这个模式的命令是在启动odoo的命令后添加一个–dev参数:
odoo -c /etc/odoo/odoo.conf --dev=all
注意:可能会碰到下面的错误:
'inotify' module not installed. Code autoreload feature is disabled
这是因为缺少依赖库inotify, 使用pip命令安装即可:
pip install inotify
总结
本章,我们学习了一个模块的基本组成结构,并尝试编写了我们的第一个模块。下一步,我们将认识odoo中模型的基本字段类型和基本的视图类型。
本节完整代码请关注公众号OdooHub回复【18.1.2】获取。