> 警告
> 本教程需要已经安装odoo
[TOC]
#### 启动/停止Odoo服务器
Odoo采用C/S架构,客户端通过Web浏览器访问服务端,遵从RPC协议。业务逻辑和扩展通常在服务端执行,而只有添加客户端支持的新特征才会在客户端添加代码(例如,交互过程中新数据的映射表示)。启动服务器,只需要在shell中调用命令odoo-bin,或者完整的路径名调用:
~~~
odoo-bin
~~~
通过`Ctrl-c`或杀死相应的系统进程来停止Odoo服务。
#### 构建一个Odoo模块
服务端扩展和客户端扩展都被封装为模块,这些模块可选择性的被安装,安装完成后通过数据库来加载。模块即可以是全新的业务逻辑,也可以是更改和扩展已有的业务逻辑。比如创建一个中国会计模块,将中国的会计准则添加到Odoo的通用会计中,也可以创建一个全新的实时可视化管理车队的模块。Odoo中的所有功能都是包含在模块中。
##### 模块的组成
Odoo模块包含多个部分:
**业务对象**
Python类,这些类会被Odoo框架自动持久化,持久化的方式决定于类的定义。
**数据文件**
包括视图、菜单、动作、工作流、权限、演示数据等,以XML或CSV文件定义。
**Web控制器**
处理Web浏览器的请求
**静态页面数据**
网站或界面使用的图片、CSS或JavaScript文件
##### 模块结构
每个模块都是模块目录中的一个子目录。可以通过`--addons-path`选项指定模块目录的路径。
> 提示
> 大多数命令行选项可以通过配置文件进行设置
Odoo模块由清单文件进行声明。查看清单文件文档了解详细信息。模块是一个包含`__init__.py`文件的的Python包,`__init__.py`文件包含了模块需要的导入的各Python文件。
例如,如果模块中包含`mymodule.py`文件,`__init__.py`应该这样写:
~~~
from . import mymodule
~~~
Odoo提供了脚手架机制来快速创建新模块,`odoo-bin`子命令`scaffold`用来创建一个空模块
~~~
$ odoo-bin scaffold <模块名> <模块放置路径>
~~~
该命令为模块创建一个子目录,并自动为模块创建一些标准文件。这些文件大多只包含被注释的代码和XML元素。后面将解释这些文件的含义。
> **练习创建模块**
> 使用上面的命令行创建一个空模块*Open Academy*,并将其安装在Odoo中。
>
> 1. 调用命令`odoo-bin scaffold openacademy addons`
> 2. 修改模块中的相关文件
> 3. 不要修改其它文件
`openacademy/__manifest__.py`
~~~
# -*- coding: utf-8 -*-
{
'name': "Open Academy",
'summary': """Manage trainings""",
'description': """
Open Academy module for managing trainings:
- training courses
- training sessions
- attendees registration
""",
'author': "My Company",
'website': "http://www.yourcompany.com",
# Categories can be used to filter modules in modules listing
# Check https://github.com/odoo/odoo/blob/master/odoo/addons/base/module/module_data.xml
# for the full list
'category': 'Test',
'version': '0.1',
# any module necessary for this one to work correctly
'depends': ['base'],
# always loaded
'data': [
# 'security/ir.model.access.csv',
'templates.xml',
],
# only loaded in demonstration mode
'demo': [
'demo.xml',
],
}
~~~
`openacademy/__init__.py`
~~~
# -*- coding: utf-8 -*-
from . import controllers
from . import models
~~~
`openacademy/controllers.py`
~~~
# -*- coding: utf-8 -*-
from odoo import http
# class Openacademy(http.Controller):
# @http.route('/openacademy/openacademy/', auth='public')
# def index(self, **kw):
# return "Hello, world"
# @http.route('/openacademy/openacademy/objects/', auth='public')
# def list(self, **kw):
# return http.request.render('openacademy.listing', {
# 'root': '/openacademy/openacademy',
# 'objects': http.request.env['openacademy.openacademy'].search([]),
# })
# @http.route('/openacademy/openacademy/objects/<model("openacademy.openacademy"):obj>/', auth='public')
# def object(self, obj, **kw):
# return http.request.render('openacademy.object', {
# 'object': obj
# })
~~~
`openacademy/demo.xml`
~~~
<odoo>
<data>
<!-- -->
<!-- <record id="object0" model="openacademy.openacademy"> -->
<!-- <field name="name">Object 0</field> -->
<!-- </record> -->
<!-- -->
<!-- <record id="object1" model="openacademy.openacademy"> -->
<!-- <field name="name">Object 1</field> -->
<!-- </record> -->
<!-- -->
<!-- <record id="object2" model="openacademy.openacademy"> -->
<!-- <field name="name">Object 2</field> -->
<!-- </record> -->
<!-- -->
<!-- <record id="object3" model="openacademy.openacademy"> -->
<!-- <field name="name">Object 3</field> -->
<!-- </record> -->
<!-- -->
<!-- <record id="object4" model="openacademy.openacademy"> -->
<!-- <field name="name">Object 4</field> -->
<!-- </record> -->
<!-- -->
</data>
</odoo>
~~~
`openacademy/models.py`
~~~
# -*- coding: utf-8 -*-
from odoo import models, fields, api
# class openacademy(models.Model):
# _name = 'openacademy.openacademy'
# name = fields.Char()
~~~
`openacademy/security/ir.model.access.csv`
~~~
id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
access_openacademy_openacademy,openacademy.openacademy,model_openacademy_openacademy,,1,0,0,0
~~~
`openacademy/templates.xml`
~~~
<odoo>
<data>
<!-- <template id="listing"> -->
<!-- <ul> -->
<!-- <li t-foreach="objects" t-as="object"> -->
<!-- <a t-attf-href="{{ root }}/objects/{{ object.id }}"> -->
<!-- <t t-esc="object.display_name"/> -->
<!-- </a> -->
<!-- </li> -->
<!-- </ul> -->
<!-- </template> -->
<!-- <template id="object"> -->
<!-- <h1><t t-esc="object.display_name"/></h1> -->
<!-- <dl> -->
<!-- <t t-foreach="object._fields" t-as="field"> -->
<!-- <dt><t t-esc="field"/></dt> -->
<!-- <dd><t t-esc="object[field]"/></dd> -->
<!-- </t> -->
<!-- </dl> -->
<!-- </template> -->
</data>
</odoo>
~~~
#### 对象关系映射
Odoo的关键组件是ORM层。这个层避免了大量手写SQL,并提供扩展性和安全性。业务对象被声明为`Model`类的扩展类,并自动将它们集成到持久层中。可以通过定义时设置属性来配置模型。最重要的属性是`_name`,必填属性,它定义了模块在Odoo系统中的名称。一个最简单的模型定义:
~~~
from odoo import models
class MinimalModel(models.Model):
_name = 'test.model'
~~~
#### 模型字段
字段定义了模型中需要存储的内容和存储的位置。字段通过类的属性来定义:
~~~
from odoo import models, fields
class LessMinimalModel(models.Model):
_name = 'test.model2'
name = fields.Char()
~~~
**通用属性**
和模型一样,字段也可以配置,字段通过属性参数的方式来配置:
~~~
name = field.Char(required=True)
~~~
一些属性可以用于所有字段,以下是最常见的属性:
`string(unicode,default: field's name)`
用户界面中的字段标签(用户可见)
`required(bool,default:False)`
如果为True,这个字段不能为空,它必须有一个默认值或者在创建记录时总是给定一个值。
`help (unicode, default: '')`
长格式,在用户界面上显示的提示。
`index (bool, default: False)`
请求Odoo在列上创建数据库索引。
**简单字段**
有两大类字段:简单字段和关联字段,简单字段的值是存储在模型表中的原子值,而关联字段是指向模型(相同模型或不同模型)的记录行。
简单字段的例子如:Boolean、Date、Char
关联字段的例子如:Many2One、One2Many、Many2Many
**保留字段**
Odoo在所有模型中都创建几个固定字段,这些字段由系统管理,用户程序不应写入。但是可以在用户程序中读取:
`id(Id)`
模型中记录的唯一标识符
`create_date(Datetime)`
记录的创建日期
`create_uid(Many2one)`
创建记录的用户
`write_date(Datetime)`
记录的最后修改时间
`write_uid(Many2one)`
上次修改记录的用户
**特殊字段**
默认情况下,Odoo的name在所有模型上还需要一个字段,用于显示和搜索。用于这些目的的字段可以通过设置字段`_rec_name`来实现。
> 练习定义模型,在openacademy模块中定义新的数据模型*课程*,每门课程包含两个字段,标题和描述,其中标题是必填字段。编辑文件`openacademy/models/models.py`以包含`Course`类。
`openacademy/models.py`
~~~
from odoo import models, fields, api
class Course(models.Model):
_name = 'openacademy.course'
name = fields.Char(string="Title", required=True)
description = fields.Text()
~~~
#### 数据文件
Odoo是一个高度数据驱动的系统,虽然行为是通过Python代码制定的,但一些模块的值是在加载时通过数据文件设置的。
> 提示:
> 一些模块的作用仅仅是为了将数据添加到Odoo中
模块数据通过带有`<record>`元素的XML数据文件来声明。每个`<record>`元素创建或更新数据库中的一个记录行。
~~~
<odoo>
<data>
<record model="{model name}" id="{record identifier}">
<field name="{a field name}">{a value}</field>
</record>
</data>
</odoo>
~~~
* `model`是在记录行中记录的Odoo模型名称
* `id`是外部标识符,它允许引用记录(而不必知道其在数据库中的标识符)
* `<field>`,每个`<field>`对应数据行中的一个字段,name属性是字段名(例如*description*),而`body`就是字段的值。
数据文件通过在manifest文件声明而被载入,他们既可以在`data`列表声明(始终载入)也可以在`demo`列表声明(仅在演示模式下载入)
> 练习定义演示数据,添加演示数据以填充*Course*模型的数据,编辑文件`openacademy/demo/demo.xml`来添加演示数据
`openacademy/demo/demo.xml`
~~~
<odoo>
<data>
<record model="openacademy.course" id="course0">
<field name="name">Course 0</field>
<field name="description">Course 0's description
Can have multiple lines
</field>
</record>
<record model="openacademy.course" id="course1">
<field name="name">Course 1</field>
<!-- no description for this one -->
</record>
<record model="openacademy.course" id="course2">
<field name="name">Course 2</field>
<field name="description">Course 2's description</field>
</record>
</data>
</odoo>
~~~
#### 操作和菜单
操作和菜单是数据库中的常规数据,通常以数据文件声明。操作可以通过三种方式触发:
1.点击菜单项(链接到特定操作)
2.点击视图中的按钮(如果这些按钮已经被连接到操作)
3.作为对象的上下文操作
因为菜单的声明相对复杂,所以有个一个`<menuitem>`的快捷方式来声明`ir.ui.menu`菜单对象,并将其更容易的连接到对应的操作。
~~~
<record model="ir.actions.act_window" id="action_list_ideas">
<field name="name">Ideas</field>
<field name="res_model">idea.idea</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem id="menu_ideas" parent="menu_root" name="Ideas" sequence="10"
action="action_list_ideas"/>
~~~
> 危险
> 操作必须在XML文件中对应的菜单之前声明.数据文件是按顺序执行的,操作的`id`必须在对应的菜单建立之前就存在于数据库中。
>
> 练习定义新菜单项,在开放学院菜单项下定义新菜单项来访问课程。用户应该能够:
>
> * 显示所有课程的列表
> * 建立或编辑课程
> 1.建立`openacademy/views/openacademy.xml`以创建操作和能够触发操作的菜单项。
> 2.添加这个文件到`openacademy/__manifest__.py`下的`data`列表。
`openacademy/__manifest__.py`
~~~
'data': [
# 'security/ir.model.access.csv',
'templates.xml',
'views/openacademy.xml',
],
# only loaded in demonstration mode
'demo': [
~~~
`openacademy/views/openacademy.xml`
~~~
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<data>
<!-- window action -->
<!--
The following tag is an action definition for a "window action",
that is an action opening a view or a set of views
-->
<record model="ir.actions.act_window" id="course_list_action">
<field name="name">Courses</field>
<field name="res_model">openacademy.course</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">Create the first course
</p>
</field>
</record>
<!-- top level menu: no parent -->
<menuitem id="main_openacademy_menu" name="Open Academy"/>
<!-- A first level in the left side menu is needed
before using action= attribute -->
<menuitem id="openacademy_menu" name="Open Academy"
parent="main_openacademy_menu"/>
<!-- the following menuitem should appear *after*
its parent openacademy_menu and *after* its
action course_list_action -->
<menuitem id="courses_menu" name="Courses" parent="openacademy_menu"
action="course_list_action"/>
<!-- Full id location:
action="openacademy.course_list_action"
It is not required when it is the same module -->
</data>
</odoo>
~~~
作者:luohuayong
链接:http://www.jianshu.com/p/06e38310a465
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
- 开发教程
- Odoo10开发教程一(构建模块)
- Odoo10开发教程二(基本视图)
- Odoo10开发教程三(模型关联)
- Odoo10开发教程四(继承)
- Odoo10开发教程五(计算字段和默认值)
- Odoo10开发教程六(高级视图)
- Odoo10开发教程七(工作流和安全)
- 参考手册
- odoo V10中文参考手册(一:ORM API)
- odoo V10中文参考手册(指导规范)
- 技巧
- odoo 常用widget
- Odoo(OpenERP)开发实践:菜单隐藏(1)
- Odoo(OpenERP)开发实践:菜单隐藏(2)
- Odoo(OpenERP)开发实践:数据模型学习
- Odoo中自动备份数据库
- Odoo(OpenERP)应用实践: 使用db-filter参数实现通过域名指定访问哪个数据库
- Odoo(OpenERP)配置文件openerp-server.conf详解
- Odoo(OpenERP v8)数据模型(Data Model)
- odoo10学习笔记十七:controller
- Qweb定义