参考文档 看云文档参考 思否ruter文档参考
odoo开发,需要那些工具
python
postgresql
node.js
less (css是静态, less是动态)
git
运行odoo
1 安装路径: /xx/xx
2 /xx/xx/odoo-bin -c odoo-dev/odoo.conf & # odoo-dev/odoo.conf 配置文件
-c 指定配置文件文件
& 后台运行
odoo技巧
数据库管理:http://127.0.0.1:8069/web/database/manager
数据库选择:http://127.0.0.1:8069/web/database/selector
数据库指定:?db=database
后台
http://127.0.0.1:8069/web/
前台
http://127.0.0.1:8069/
登录
http://127.0.0.1:8069/web/login
Odoo模块组成
业务对象
Python类,这些类会被Odoo框架自动持久化,持久化的方式决定于类的定义。
数据文件
包括视图、菜单、动作、工作流、权限、演示数据等,以XML或CSV文件定义。
Web控制器
处理Web浏览器的请求
静态页面数据
网站或界面使用的图片、CSS或JavaScript文件
模块结构
每个模块都是模块目录中的一个子目录, 可以通过 --addons-path 选项指定模块目录的路径
odoo的设计模式 --> MVC模式
M Model
V View
C Controller
创建模块
1 在odoo源码同层目录下创建目录: mkdir myaddons
2 用odoo-bin的脚手架功能创建空的odoo模块
./odoo-bin scaffold todo myaddons
odoo orm
参考:参考手册
自己的笔记跳转: odoo10 orm操作
视图
自己的笔记跳转: odoo10视图
domains
通过domain来过滤数据记录
domain作用子一个model上: 用model.search(domain)
Domain是定义模型子集的规则集合。domain表达式是由多个(field_name, operator, value)元组构成的列表或数组
field_name -- 字段名或者用.号分隔的Many2one关系的字段如:'street' , 'partner_id.country' operator(str) -- 用于对字段值和给定值进行比较的运算符: =,!=,>,>=,<,<=, =?(值为false或none时返回true,否则与=效果一致) =like()将字段数据与value进行匹配,_代表匹配单个字符、%匹配0或多个字符 like() 将字段数据与%value% 进行匹配, not like 不与%value%匹配 ilike 忽略大小写的like函数 not ilike 忽略大小写的not like =ilike 忽略大小写的=like in 与value的任意值相等,value须为值列表 not in 与value的任意值都不相等 child_of 是否由value派生而来 value 对应值,必须与相应条件对应 多个domain表达式可用运算符进行连接,运算符写在两个表达式之前。 & 逻辑与 ,| 逻辑或,!逻辑非
1 作用在关联字段上
示例: 当为授课选择讲师时,只有instructor值为True的讲师会被显示出来。
instructor_id = fields.Many2one('res.partner', string="Instructor", domain=[('instructor', '=', True)])
注意: 声明为文字列表的domain会在服务端进行计算,右侧不可以是动态的字段; 而声明为字符串的domain是在客户端进行计算的,右侧可以是动态的字段
2 作用在客户端上
当在客户端界面选择记录集时,domain参数可以添加到关联字段上,以限制只显示有效的关联字段。
<field name="instructor_id" domain="[('instructor', '=', True)]"/>
计算字段
odoo中提供一种每次引用字段, 通过方法动态计算的方式来获取字段值的方法
1 创建计算字段
- 创建一个字段
- 将其属性compute设置为(计算)方法名称
2 计算方法
# ORM使用depends()装饰器来指定计算方法的依赖性。当某些依赖关系被修改后,ORM层通过给定的依赖关系来触发字段的重新计算。
@api.multi
@api.depends('value')
def _compute_name(self):
for record in self:
record.name = "Record with value %s" % record.value
默认值
字段加上属性 default
- 可以是一个值
- 有返回值的匿名函数
对象self.env提供对请求参数和其他有用的东西的访问
self.env.cr 或者 self._cr 是数据库游标对象,通常用于查询数据库
self.env.uid或者self._uid 是当前用户的数据库ID
self.env.user 是当前用户记录
self.env.ref(xml_id) 返回XML ID对应的记录
self.env[model_name] 返回给定模型的实例
Odoo 有内置规则:active字段值为False时记录不可见
onchange
为客户端接口提供了一种方法, 只要用户填写了字段中的值, 就可以实时更新form表单, 而无需向数据库保存任何内容
示例
@api.onchange('amount', 'unit_price') # onchange()的参数指定了在那个字段改变时,触发方法 def _onchange_price(self): # 业务逻辑 if flag: return { # 可以返回一个错误信息 'warning': { 'title': "Something bad happened", 'message': "It was very bad indeed", } }
模型约束
python代码级别约束
在记录集上调用这个方法。装饰器参数指定了约束涉及的字段,当涉及的字段中任一发生改变时触发方法执行。如果不满足约束条件,该方法将引发异常:
示例
from odoo.exceptions import ValidationError @api.constrains('age') def _check_something(self): for record in self: if record.age > 20: raise ValidationError("Your record is too old: %s" % record.age) # all records passed the test, don't return anything
sql数据库级别约束
SQL约束通过模型属性_sql_constraints进行定义。
它是一个三元素的元组的列表(name, sql_definition, message)
- name 是SQL约束名称,
- sql_definition 是约束规则,
- message 是违反约束规则时的警告信息。
示例
_sql_constraints = [ ('name_description_check', 'CHECK(name != description)', "The title of the course should not be the description"), ('name_unique', 'UNIQUE(name)', "The course title must be unique"), ]
工作流
工作流是与动态业务对象相关联的模型。工作流也用于跟踪动态(随时间)演进的进程。
下面是工作流的两种实现方式
1 伪工作流
button标签的type='object'
给模型添加一个字段state, 用于定义一个工作流程
按钮 --> 模型的方法
2 真工作流
button标签的type='workflow'
与模型相关的工作流仅在创建模型记录时被创建。因此,在工作流定义之前创建的授课实例是没有与之对应的工作流实例的。
工作流的构成
工作流
# 有点类似一个【model的工作流模板】, 之后此model的工作流实例根据这个模板生成
xml示例代码:
<record model="workflow" id="wkf_session"> <field name="name">OpenAcademy sessions workflow</field> <field name="osv">openacademy.session</field> <field name="on_create">True</field> </record>
活动(节点)
# 活动定义了应在Odoo服务器内完成的工作 例如更改某些记录的状态或发送电子邮件。
不同的调用方式:
<field name="kind">function</field> # python代码 <field name="kind">dummy</field> # 服务器的code # 还有 Subflow 和 Stop all,可以参考官方文档
xml示例代码:
<record model="workflow.activity" id="draft"> <field name="name">Draft</field> <field name="wkf_id" ref="wkf_session"/> <field name="flow_start" eval="True"/> # 标示了活动的开始节点 <field name="kind">function</field> <field name="action">action_draft()</field> </record>
转换
# 控制工作流如何从活动到活动。
xml示例代码:
<record model="workflow.transition" id="session_draft_to_confirmed"> <field name="act_from" ref="draft"/> <field name="act_to" ref="confirmed"/> <field name="signal">confirm</field> # 【信号】: 通过button触发, button会根据name去找到相应的signal </record>
实例
# 创建一个recoed时, 会根据工作流模板, 生成工作流实例
有点类似python面向对象的实例化
条件
当条件被满足时, 自动状态迁移, 有点类似 (条件+onchange)
自动化的工作流: 设置一个condition
xml示例代码:
<record model="workflow.transition" id="session_auto_confirm_half_filled"> <field name="act_from" ref="draft"/> <field name="act_to" ref="confirmed"/> <field name="condition">taken_seats > 50</field> </record>
向导
model --> ir.actions.act_window
在odoo中通过使用向导创建一个表单与用户进行交互, 向导的model使用TransientModel定义
运行wizard: 可以通过ir.actions.act_window模型表里的记录启动, 可以从menu里或者某个button触发;
另外一种方式, 在form view上方的下拉按钮组(context action)中调用, 设置好src_model关联对应的model
1 向导视图中:
src_model 指向上下文的模型 res_model 指向向导的模型 target="new" 将弹出一个新窗口打开向导 special="calcel" 关闭向导窗口而不保存
2 向导模型中
self._context 获取当前对象的模型的上下文
注意: 向导记录不是永久性的, 会在一段时间后自动从数据库中删除. 这就是为什么他们被称为"瞬态"
odoo报表
报表构成
1 在ir.actions.report.xml中定义报表记录, 使用<report>设置报表的各种基本参数
id 外部标示 name 助记符/描述符 model 报告涉及的模型 report_type qweb-pdf/qweb-html report_name 打印出来的名字 groups 允许哪些组可以查看/使用报表 attachment_use 设成True, 报表将使用附件表达式生成的名称存为记录的附件 attachment 表达式 paperformat 纸质格式的外部ID, 默认为公司的纸质格式
2 Qweb view定义报表样式
docs是从context发送过来的变量(上下文), 代表报表内容记录, user代表打印此报表的人 - external_layout 将在报表上添加默认页眉和页脚 - PDF的body将会包含在<div class="page"> - <template>的id必须是<report>里指定的name - <template>里可以使用的变量 docs 记录当前报表 doc_ids docs 记录的id列表 doc_model 模型为 docs 记录 time 引用Python标准库的 time user res.user 记录用户打印报表 res_company 记录当前 user 的公司
Qweb常用指令
1 数据输出
t-esc = 'value' 会做html转义, 有xss攻击的危险 t-raw 不转义
2 条件
t-if t-elif t-else
3 循环
t-foreach t-as
t-esc # 设置: t-set = '...' t-value = ''
4 属性
计算属性: t-attr
有3中不同的形式:
1 t-attr-$name 2 t-attf-$name 3 t-att-mapping t-att-pair
5 设置变量
通过set指令完成
两种方式
1 t-value = '2+1' # 可以是表达式 2 如果没有t-value, 则节点的body将设置为变量的值
6 调用子模版
使用t-call调用其他模版
神奇的0变量
主模版: <div> This template was called with content: <t t-raw="0"/> # 将会渲染为子的内容, 相当于为子的body预留位置 </div> 调用子模版: <t t-call="other-template"> <em>content</em> </t> 将会渲染成: <div> This template was called with content: <em>content</em> </div>
动作
参考 odoo10动作
1 窗口动作
---> ir.actions.act_window
res_model:要打开的视图(窗口)关联的数据模型 view_type:视图类型,默认值为 form,一般情况下我们取默认值就可以了 view_mode:允许打开的视图类型,以逗号分隔,默认值为 tree,form target:打开的窗口类型,常用的有 current(当前窗口打开)和 new (弹窗打开)这两种,默认为 current 还有一些非必填的字段在某些时候我们是会用上的,这里也分列出来: view_ids:关联的视图对象 id,需注意区分和 view_id 的区别 view_id:关联的视图的 id, 例如在不同时候需要打开同一个数据模型不同的表单视图,就可以通过这个字段指定要打开的视图的 id res_id:仅在视图类型为 form 时有效,表示打开该 id 对应的记录的表单视图,如未指定则打开新建页面 context:传递到上下文中的数据,一个字典 domain:过滤规则,对视图中的记录进行过滤 limit:列表中每页显示的记录数量,默认为 80 search_view_id:指定搜索视图,不指定则按默认规则加载 multi:如果设置为 True 且动作绑定了模型(src_model)的话,该动作按钮会只出现在所绑定模型列表视图的「动作」下拉列表中(在搜索视图左侧) views:由 (view_id, view_type) 这样的元组对组成的列表,view_id 为指定视图的 id 或是 False(按默认值取出对应视图),view_type 表示视图类型
2 服务器动作
---> ir.actions.server
model_id:当前的动作是在哪个模型上运行的 binding_model_id:绑定的模型,当前动作将会出现在绑定的模型的视图中 state:服务器动作的类型,总共有 4 种可选的类型,分别是 code(执行 Python 代码),object_create(创建一条新记录),object_write(更新记录),multi(执行多个动作) code:对应 state 的类型 code,为当前动作运行时所要执行的 Python 代码 我们定义的所有模型都会在 ir.model.data 对应的表中存在相应的记录 接下来我们再看到字段 code 里面的内容,在这里面我们有一些变量是可以直接使用的: env:Odoo 的运行环境 model:动作触发时对应的 Odoo 模型实例 record:动作触发时对应的单个记录(如在表单视图中运行对应当前表单所指向的记录),可能是空的 records:动作触发时对应的记录集(如在列表视图中勾选多条记录触发,记录集指向这些选中的记录),可能是空的 Python 库:time, datetime, dateutil, timezone 时间相关的 Python 库 log:用来记录日志信息 Warning:通过 raise Warning('xxxxx') 抛出警告信息
3 URL动作
---> ir.actions.act_url
target:有两个可选值,分别是新窗口(new)打开链接,相当于 <a target='_blank' />,以及当前窗口(self)打开,相当于 <a target='_self' /> url:要打开的目标页面的链接,可以是外部页面也可以是同域下的内部页面
4 客户端动作
触发一个完全右客户端(浏览器)执行的动作
5 报表动作
无
安全与权限
参考 思否文档参考
1 基于组对表的访问权限 --> CSV文件
2 基于组对表中数据行的访问权限 --> XML文件
3 字段级别 // contraint # 通过contraint可以做到字段级别
1 用户组的权限
模块分类: ir.module.category
用户组: res.groups
a 定义用户组
两个系统自带的用户组:
base.group_user 基础用户组
base.user_root 管理员账号
用户组里每个字段所代表的含义:
category_id:该用户组所属的模块分类 # <field name="category_id" ref="todo.module_category_todo"/> implied_ids:在当前用户组下的用户,同时加入该(字段所指定的)用户组中 users:该字段所指定的用户默认被加入到当前用户组中 # 将会默认获得此组权限的用户
b 用户组的权限
(先根据上一步创建用户组)
用户组的权限定义以.csv文件存储的
默认的权限记录文件: security/ir.model.access.csv
里面有8个字段, 分别代表的含义如下;
id:这条权限记录的 id,可以类比为 xml_id # 类似xml的外部标识 name:权限记录的名称 model_id:id:要配置权限的模型的外部 ID (以 model_ 开头) group_id:id:应用此条权限配置的用户组的 id,若为空则默认对所有用户组生效 # 上一步创的用户组id perm_read:读取记录的权限,1 为拥有该权限,0 为不分配该权限 perm_write:编辑更新记录的权限,取值同上 perm_create:创建新记录的权限,取值同上 perm_unlink:删除记录的权限,取值同上
示例
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_app02_contract,app02.contract,model_app02_contract,,1,1,1,0
2 记录集权限
(俗名: 规则)
模型: ir.rule
model_id:要应用该规则的模型的外部 ID # 模型的外部标识ID --> model_**_** domain_force:过滤条件,符合该条件的记录都将按照所定义权限进行检查,其中变量 user 表示当前用户的实例对象,可以直接使用 groups:应用该规则的用户组,如果不指定则默认对全部用户应用该规则
菜单隐藏
只需要在对应的菜单项上添加一个groups属性即可, 里面的值可以是逗号分隔的多个用户组的外部ID