「Odoo 基础教程系列」第二篇——从 Todo 应用开始(1)

By PickPik

在第一篇教程发布以后差很少一个月的今天,终于完成了第二篇内容,这个发布周期拖得实在是有点太长了,我都以为很差意思了?python

从这一篇教程开始,咱们将以建立一个 Todo 应用为目标,将 Odoo 中的一些概念和内容逐渐引入进来,而后利用这些知识去完善咱们的 Todo 应用。虽然这是个很简单的应用,可是但愿你们能够动手一块儿操做,从最简单的开始上手学习如何使用 Odoo 这个框架。git

这篇教程将会带领你们一块儿建立第一个 Odoo 模块,为模型增长菜单和修改视图,对于刚接触的小伙伴来讲可能会有些生涩,不理解也不要紧,慢慢接触上手以后会显得容易得多,同时也欢迎你们积极提问,我会竭尽所能去帮助爱学习的小伙伴的。github

好了,下面就正式开始,一块儿动手完成这一次的内容吧?shell

建立模块

咱们在 Odoo 源码所在目录的同一层级里建立一个目录 mkdir mymodules,这个目录专门用于存放咱们本身建立的模块,和 Odoo 自带的模块区分开来,固然你也能够用其余名字。而后用 odoo-bin 的脚手架功能建立一个空的 Odoo 模块,里面包含了一个完整的项目所需的基本文件和项目结构:数据库

./odoo/odoo-bin scaffold todo mymodules

进去 mymodules/ 看看,是否是多了一个目录 todo/?就是这么简单,咱们的第一个 Odoo 模块就建立好了,接下来先对项目结构进行简单的说明,了解一下各个目录底下应该放置些什么内容。segmentfault

结构说明

下面是咱们刚建立的 todo 模块的目录结构,从上到下一一进行说明:浏览器

todo
├── __init__.py
├── __manifest__.py
├── controllers
│   ├── __init__.py
│   └── controllers.py
├── demo
│   └── demo.xml
├── models
│   ├── __init__.py
│   └── models.py
├── security
│   └── ir.model.access.csv
└── views
    ├── templates.xml
    └── views.xml

在 Python 中,每个包(package)都包含一个 __init__.py 文件,而一个 Odoo 的模块,同时也是一个 Python 包,因此咱们能够看到,生成的项目文件里已经包含了 __init__.py 这个文件,若是打开这个文件,你会看到里面引入了 controllersmodels 这两个包,稍候咱们会讲到这两个,这里先放一放。那么对于这个 __init__.py 文件,咱们没什么特殊需求,是能够不用去理会的,就让它静静地躺在那里完成它的使命就行了。安全

文件 __manifest__.py 用于声明一个 Odoo 模块以及指定它的元数据(metadata),文件里只包含了一个单独的 Python 字典,里面默认只列出了 9 项最基本的配置项,包含了模块(或应用)名,模块的简介和详细介绍,做者和网站,模块的所属分类、版本,还有就是这个模块依赖于其余的哪些 Odoo 模块,须要加载哪些数据文件以及演示数据。除了这里列出的配置项外,还有一些高级配置项,咱们这里暂时不须要理会,后面用到以后将会进行详细的说明。微信

接下来咱们先讲一下 demosecurity 这两个目录。前者是用于存放演示数据的,在 __manifest__.py 中就能够看到有引入该目录下的 demo.xml 文件,在使用演示模式时,初始化一些演示数据能够帮咱们节省很多的时间;然后者经过名字就知道它的做用是跟安全相关的,目录下只有一个 ir.model.access.csv 文件,里面用于定义不一样的角色组对应于不一样模型的相关权限,包括读(read),写(write),建立(create)和删除(unlink)权限,拥有相关权限则为 1,反之为 0。咱们刚提到了角色组,可是没有发现相关定义的位置,咱们只要默认角色组的定义和模型权限定义在同一目录下就能够了,角色组的定义一样也是使用的 .xml 文件,在后面咱们会有专门的一篇文章对角色组和权限进行讲解说明。框架

下面要讲的是 Odoo 开发中的核心部分 MVC(同时也是大部分 Web 应用开发所采用的经典模式),MVC 分别表明的是 Model(模型)、View(视图)和 Controller(控制器)。有关 MVC 模式具体的概念和相关的知识,这里就不做详细讲解了,但愿不了解的同窗能够去找找相关的内容学习一下。这里简单说一下它们各自的用途,在 Model 中,咱们定义一切和数据相关的东西,例如对应到数据库表字段的模型字段、各类外键关系以及操做数据的逻辑方法等。View 则是负责数据展现的,咱们经过编写视图控制须要展现出来的数据以及以什么样的形式展现数据等,而且能够在视图上进行交互。Controller 则是在 Model 和 View 之间,负责响应用户操做,从 Model 中获取数据进行处理并返回到 View 中。

以上就是对一个 Odoo 模块的目录结构的一些简单的说明,另外还有一些目录在初始建立的模块里是没有出现的,可是咱们后面会用上,到时候本身手动建立相应的目录便可。

模型

前面一节中咱们说到 Model 是用于定义和数据相关内容的地方,如今就来建立第一个模型吧。

首先打开 models/models.py 将里面注释掉的内容删掉,不要修改文件顶部引入的包,而后添加如下代码:

class TodoTask(models.Model):
     _name = 'todo.task'
     _description = '待办事项'

     name = fields.Char('描述', required=True)
     is_done = fields.Boolean('已完成?')

看起来十分简单,第一个模型就这样建立好了 :)

咱们建立了一个叫作 TodoTask 的类,它继承自基础模型 models.Model,这也是咱们使用 Odoo 开发时最经常使用的一种用于持久化数据的基础模型,除此以外还有 models.TransientModelmodels.AbstractModel 这两种,在以后接触到咱们将会进行讲解,这里暂不涉猎。

先看一下这个模型里带下划线前缀的两个属性:

  • _name - 模型的名称,在外键或者实例化模型对象时会用到,是模型的惟一标识
  • _description - 模型的描述,描述模型的做用,通常状况下不会主动使用到

除了上面列出的两个特殊属性外,还有一些其余属性,例如更改默认显示名称时会用到 _rec_name,继承现有模型时会用到 _inherit 等,这些特殊属性都具备他们各自的用途,可是除了 _name 是定义一个新的模型时必需要有的属性外,其余属性都是可选的。

在 Odoo 的模型定义中,咱们使用 fields 进行字段的定义,在上面这个模型里,咱们简单地定义了 nameis_done 两个字段,它们的类型分别是 CharBoolean,而且咱们指定字段 name 是必填的(添加了 required=True)。若是小伙伴接触过 Django,可能就会说了,Django 的 CharField 是必须指定最大长度 max_length 的,Odoo 的不须要吗?那最大长度是多少呢?对于这个疑问,官方的文档里有这样一句说明:

Basic string field, can be length-limited, usually displayed as a single-line string in clients.

也就是说 Odoo 的字段类型 Char 是不限制长度的,可是一般只是用于单行字符串,若是要存储大量文本内容,仍是使用 Text 更合适一些。

不如咱们先把模块安装好,看看会不会有什么事情发生,和上一篇教程同样,咱们先将 Odoo 服务跑起来,记得先在 Odoo 的源码目录中激活咱们的开发环境:

pipenv shell

./odoo-bin --addons-path=addons,../mymodules --db-filter=^demo$ -d demo

须要注意一下的是 --addons-path 中除了有 addons 外,还加上了咱们刚建立的模块目录 mymodules,这里使用的是相对路径,不要搞错了哦 :)

接下来咱们打开浏览器访问 localhost:8069 而后用超级管理员账号登陆(不记得了?账号密码都是 admin),登陆后在应用列表页面,将搜索框的 Apps 标签去掉,搜索关键词 todo 找到咱们刚建立的模块,点击 Install 按钮安装模块。好了,你很快就会发现,页面又倒回了应用列表页面,若是咱们再次按前面的步骤搜索咱们的模块,你会发现没有了安装按钮,如今显示的是 Installed 已安装,除了这个变化外仿佛一切都没发生过,这是为何呢?

由于咱们没有入口,找不到能够打开咱们的 Todo 应用的地方,接下来咱们就一块儿建立第一个入口——菜单。

建立菜单

光有模型是固然不够的,就好象一幢大楼,钢筋骨架都搭起来了,而后把墙给封死了,没有门咱们进不去呀!

恰好像说了个不怎么恰当的比喻,不如直接动手,建立一个菜单更实际。在模块里的 views 目录下建立一个 menus.xml 文件,而后输入如下内容:

<?xml version="1.0" encoding="utf-8"?>
<odoo>
    <data>
        <!-- 主菜单定义 -->
        <menuitem id="menu_todo" name="Todo"/>
        <!-- 菜单动做定义 -->
        <record id="action_todo_task" model="ir.actions.act_window">
            <field name="name">待办事项</field>
            <field name="res_model">todo.task</field>
            <field name="view_type">form</field>
            <field name="view_mode">tree,form</field>
            <field name="target">current</field>
        </record>
        <!-- 子菜单定义 -->
        <menuitem action="action_todo_task" id="submenu_todo_task" name="待办事项"
                  parent="menu_todo" sequence="10"/>
    </data>
</odoo>

上面各部份内容我都写上了备注,你们一看就应该知道它们各自是干什么的了。在 Odoo 中定义一个菜单,使用的是 menuitem,而后咱们须要为菜单指定属性 idname,前者是这个菜单的惟一标识,然后者则是这个菜单所显示的名字。

而后咱们定义了一个菜单动做,也就是点击一个菜单时所要执行的动做,Odoo 中动做的类型分为几个类型,从 model 这个属性能够看出来一个动做是属于什么类型的,act_window 表示咱们定义的这个动做是和窗口有关的,例如打开一个弹窗或者一个列表页面,均可以经过窗口动做实现。其余类型的动做咱们先放一放,后面会有专门的一篇文章用于讲述动做相关的内容,咱们先简单地了解一下定义一个窗口动做有哪些要素。

和定义菜单同样,必不可少的会有 id 这一属性,之后咱们将默认全部在 .xml 文件中定义的数据,都须要带有 id 属性,毕竟这是咱们可以经过代码找到它们的惟一标识了。这里咱们用了 record 标签,包裹了一系列的 field 来定义咱们的动做,除了能够定义动做外,包括前面提到的菜单在内的一切具备实际模型的对应的数据,咱们均可以这样定义,最大的区别就在于 model 这个属性所指定的值,它对应到咱们定义的数据是来自哪一个模型的,被包裹在里面的 field 则对应到一个模型的各个字段。刚开始可能不太明白,不过没关系,后面会一直使用到,很快就能掌握的了。

最后咱们定义了一个子菜单,能够看到和最开始定义的菜单不同的是,咱们这里多了 3 个属性,分别是 actionparentsequence。其中 action 的值是咱们定义的动做的 id,表示点击这个菜单,就执行对应 id 的动做,parent 指定的值是咱们定义的第一个菜单的 id,表示当前菜单是指定菜单的子菜单,sequence 用于指定当前菜单的位置,若是咱们在主菜单下有多个子菜单,咱们能够经过这个属性去指定各个子菜单排列的前后顺序。

关于菜单和动做,暂时就先到这里,最后不要忘了把文件 menus.xml 放到 __manifest__.pydata 列表中,否则咱们定义的内容是不会被加载的哦:

# 如今应该是这样的
'data': [
    # 'security/ir.model.access.csv',
    'views/views.xml',
    'views/templates.xml',
    'views/menus.xml',
],

好咧,菜单有了,也就有了入口,如今咱们刷新页面看看有没有什么变化,好像仍是同样没有什么不同?固然了,由于咱们修改了模块里的文件内容,这些变更并无被加载,因此咱们须要先升级模块。同样的步骤,先找到咱们的 Todo 应用模块,而后点击卡片进入表单页面,能够看到有个 Upgrade 按钮,咱们点击这个按钮把模块升级一下,而后你就会发现,咱们终于看到了期待已久的菜单和 Todo 应用的列表页面啦~

待办事项列表页

咱们的 Todo 应用已经初具原型了,点击 Create 按钮,建立一些数据试试看吧 :)

还记得在建立模型的时候给字段 name 加上了一个 required=True 的属性吗?在建立记录的表单页咱们能够看到,这个字段是淡紫色背景的,这说明这一项是必填的内容,若是不填写,点击 Save 按钮,就会发现标签显示的「描述」变成了红色,输入框也被红色边框包裹起来了,而且能够看到在窗口右上角出现了一个警告提示。

待办事项表单页警告

在建立好一条待办事项的记录后,点击 Save 保存,若是想要继续建立,能够直接在表单页上点击 Create 继续建立新的记录,也能够返回列表页查看所有的待办事项记录。若是完成了一个待办事项,咱们点击对应的记录,进入到表单页面,而后点击 Edit 编辑按钮,而后勾选「已完成?」表示咱们已经完成了这个任务。

想要删除掉一些记录该怎么办?能够在列表页先勾选须要删除的记录,而后点击上面的 Action 菜单,能够看到 Delete 这个选项,点击后会询问是否要删除,点击 Ok 确认就能够删除啦~除了列表页上能够删除记录,还能打开一条待办事项进入表单页面,而后打开 Action 菜单执行删除操做。

建立视图

在建立菜单以后,咱们直接就看到了相应的列表和表单页面,但是咱们并无建立任何相关的内容,这是 Odoo 自动帮咱们作了处理。若是一个模型没有建立对应的视图(View),Odoo 就会根据默认的规则进行显示。

但是默认在列表页只显示了一个 name 字段,咱们想要知道一个事项是否完成了,并不能直接在列表页上知道,须要一个个点击进去查看,若是有不少的记录,这可就太蛋疼了!因此咱们仍是须要本身建立一个列表视图,把是否已完成显示在列表页上。

打开 views/views.xml,把里面注释掉的内容都删掉,而后添加如下内容:

<odoo>
    <data>
        <record id="todo_task_view_tree" model="ir.ui.view">
            <field name="name">todo.task.view_tree</field>
            <field name="model">todo.task</field>
            <field name="type">tree</field>
            <field name="arch" type="xml">
                <tree string="Todo">
                    <field name="name"/>
                    <field name="is_done"/>
                </tree>
            </field>
        </record>
    </data>
</odoo>

建立视图相关的内容,咱们须要指定属性 model="ir.ui.view",而后还有两个关键的地方是 type 的值,若是建立的是列表视图,则填写 tree,若是是表单视图则是 form,除了这两种视图外还有其余类型的视图,咱们暂时不会接触到。而后是 arch 的值,里面的内容决定了咱们的视图长什么样,列表视图须要用 <tree></tree> 包裹起来,表单视图则是 <form></form>,包裹的内容具会有所不一样,这里咱们只须要在列表视图中将要显示的字段列举出来便可。

完成这一切以后,升级一下模块,而后看看效果如何。

修改后的列表页

能够看到列表里多了一列「已完成?」,这样咱们就能够一目了然,知道哪些事项是已经完成了的,而后就能够直接勾选将他们删除掉啦 XD

源码下载

从这一篇教程开始,我会把全部代码提交到 Github 的仓库中,而且我会保留相应的文件修改的 commits 以供追溯,你们能够经过查看 commits 看到各个文件的变更,若是发现什么问题,也能够直接提 issue 寻求帮助?

仓库地址:Odoo-Tutorial-Demo

相关文章

微信群

最后惯例,感兴趣的小伙伴能够加入微信群一块儿交流,加好友时备注加群,接受后会拉进群内。

微信号

相关文章
相关标签/搜索