视图继承
res.partner
扩展视图使用inherit_id字段引用父类,其中arch字段由任意数量的xpath元素组成
expr
在父视图中XPath表达式选择单个元素,如果不匹配元素或多于一个,则会引发错误
position:
应用于匹配元素的操作
(1)inside 匹配元素的末尾追加
(2)before 匹配元素的同级元素添加在其后面
(3)after 匹配元素的同级元素添加在其前面
(4)replace 替换匹配的元素
(5)attributes 使用新的属性替换匹配元素的属性
odoo orm查询数据库
查询语句
demo=self.env['res.users'].search([('id', '=',1)])
在res.users表中查找id=1的记录,返回的是一个res.user的对象
创建语句
demo.create({'name':'jenny'})
在demo对象res.user中插入一条记录
删除语句
test = self.env['res.user']
#获取res.user表为对象
test.search([('id','=',1)]).unlink()
#将该表中id为1的记录删除掉
更新语句
rs = demo.search([('name','=',client_id)])
rs.write({'id':1})
odoo方法
create方法
为模型创建一个新纪录
create(vals)→record
demo.create({'name': "测试1" ,'stated': "说明1"})
#这个函数的意思就是在demo对象中,也就是res.user中插入一条记录
参数:
vals 是一个{模型字段的名称:字段的值}组成的字典,可以使用模型的 create()方法创建新 record。如果需要,odoo 将自动调用模型的 default_get()方法去补充一些必填但vals 中不存在的字段。
返回值:
新纪录
browse方法
在当前环境下,返回一个[ids]列表与数据库中一一对应的一个 recordset,参数可以为空,一个单独的 id,或者一个 id 序列
browse([ids])→records
unlink方法
从数据库删除当前 recordset 中的 record,可能抛出的异常
unlink()
返回值:
如果没有异常,返回True,否则抛出异常
write方法
用 vals 字典参数去更新 recordset 中的所有 record 的字段
write(vals)
参数:
vals(dict) 以字典形式出现的字段名称和值,例如:
{'foo':1, 'bar':"A"}
search方法
search()方法接收一个域表达式,并返回符合条件记录的记录集
- 关键字参数
- order是一个数据库查询语句中ORDER BY使用的字符串,通常是一个逗号分隔的字段名列表.每个字段都可接DESC关键字,用于表示倒序排列
- limit设置获取记录的最大条数
- offset忽略前n条记录,可配合limit使用来一次查询指定范围记录
域表达式
域表达式是一组条件组成的列表,每个条件都是一个('字段名', '运算符', '值')组成的元祖
'&'符号表示AND运算符,'|'符号表示OR运算符,'!'符号表示NOT运算符
name_search
通常使用这个方法,根据关联字段拼接新的展示名称,如果对display_name根据指定的搜索域调用search()方法,这个方法等同于name_get()
name_search(name=",args=None,operator='ilike',limit=100)→records
参数
-name:名称,string类,必填
-args:搜索域[(字段名称,运算符,值)],选填,默认效果:匹配所有record,用于筛选record的条件列表,如果为空,则匹配所有record
-operator:运算符,String类,必填,与name联用,用于筛选record的display_name字段,通常使用'like','='.
-limit:数量.Integer类,选填,默认效果:获取所有record,用于控制返回的recordset中record的数量
返回值
返回一个由元祖组成的列表,元祖由每个符合条件的id和display_name组成
实现内容过滤
name_get (显示)
一个many2one字段要显示多列,要在对应的类中添加
返回这个recordset中所有record对应的名字
模型间的关系
关联字段
Many2one
publisher_id = fields.Many2one('res.partner',string='Publisher')
第一个位置是关联模型,第二个位置是字段标签
many2one模型字段在数据表中创建一个字段,并带有指向关联表的外键,其中为关联记录的数据库ID,以下是可用的关键字参数
- ondelete
- set null(默认值):关联字段删除时会置为空值
- restricted:抛出错误时阻止删除
- cascade:在关联记录删除时同时删除当前记录
- context 数据字典
- domain 域表达式,使用一个元祖列表过滤记录来作为关联记录选项
- auto_join=True允许ORM在使用关联进行搜索时使用SQL连接。
- delegate=True 创建一个关联记录的代理继承,必须先设置required=True和ondelete='cascade'
One2many 反向关联
列出引用该记录的关联模型记录
One2many字段接收三个位置参数
- 关联模型(comodel_name)
- 引用该记录的模型字段(inverse_name)
- 字段标签(string)
odoo中的base模型
- 信息仓库(Information Repository), ir.*模型
信息仓库用于存储odoo所需数据,如菜单,视图,模型,action- ir.actions.act_window用于窗口操作
- ir.ui.menu用于菜单项
- ir.ui.view用于视图
- ir.model用于模型
- ir.model.fields用于模型字段
- ir.model.data用于XML ID
- 资源(Resources),res.*模型
资源包含基本数据- res.partner用于业务伙伴,如客户、供应商和地址等等
- res.company用于公司数据
- res.currency用于货币
- res.country用于国家
- res.users用于应用用户
- res.groups用于应用安全组
继承
- 经典继承,添加的新功能会自动添加到已有模型中,而不会创建新模型
- 原型继承,在使用_inherit属性的同时还使用了与父模型不同的_name属性,创建新的模型,并带有自己的数据
- 代理继承,通过_inherits属性来使用,允许我们创建一个包含和继承已有模型的新模型,新模型创建新纪录时,在原模型也会被创建并使用many2one字段关联,查看新模型的人可以看到所有原模型和新模型中的字段,但在后台两个模型分别处理各自的数据
./odoo-bin shell -d aaa
执行self.env.cr.commit()时数据操作才会在数据库中生效
@api装饰器
Odoo自带的api装饰器主要有:model,multi,one,constrains,depends,onchange,returns 七个装饰器
一、one
one的用法主要用于self为单一记录的情况,意思是指:self仅代表当前正在操作的记录。
二、multi
multi则指self是多个记录的合集。因此,常使用for—in语句遍历self。
multi通常用于:在tree视图中点选多条记录,然后执行某方法,那么那个方法必须用@api.multi修饰,而参数中的self则代表选中的多条记录。
如果仅仅是在form视图下操作,那么self中通常只有当前正在操作的记录。
三、model
此时的self仅代表模型本身,不含任何记录信息。
四、contrains
对字段的约束
五、depends
depends 主要用于compute方法,depends就是用来标该方法依赖于哪些字段的。
六、onchange
onchange的使用方法非常简单,就是当字段发生改变时,触发绑定的函数。
七、returns
returns的用法主要是用来指定返回值的格式,它接受三个参数,第一个为返回值的model,第二个为向下兼容的method,第三个为向上兼容的method
注意事项
(1) odoo视图字段,如果是readonly,默认该数据是不会往后台传递的,因此,保存数据的时候,该字段的数据是不会存到数据库中的。(待确认,字段中增加默认值,保存)
(2)视图界面,注释的时候,一般情况,认为注释的代码,启动服务时,就不会被解释;其实不是,当视图中没有涉及变量的action视图ID引用指向的时候,这种情况下,确实没有影响;但是,如果被注释的该段代码,包含了引用另一个窗口视图的ID,即使注释掉,当你更新升级模块的时候,还会加载,出现视图外部extend id找不到的异常。其实原因是,odoo一旦模块被安装,相应视图的ID也会存在数据库中
(3) 改完权限文件ir.model.access.csv文件,需要重新启动服务,才会生效。
(4) odoo 模型函数装饰器,depends 和 onchange区别
onchange 前端视图变化触发,后台不触发;
depends 前台视图和后端字段值发生变化,都会触发。
(5) 当你需要从一票单据,复制生成多个单据的时候,默认单据名流水号也会被复制,为了避免这种情况,确保创建的几个单据,流水号不同,需要在后台模型中,流水号对应的字段,添加:copy=False属性。
(6) 代码中修改模型 py文件后,只需要重启odoo服务,重新加载页面,就会生效;当代码中修改视图view xml文件后,需要重启服务,并升级模块才能生效;修改static静态文件,图片,js,css样式文件,不需要重启odoo服务,也不需要升级模块,F5强制刷新相应界面,就会生效。
(7)扩展模型的write更新方法,def write(self, vals): self是上一次保存前的数据,vals是获取get了, 当前修改的字段数据。
1.model属性
每个对象(即class)一般由字段(变量)和函数组成,每个对象对应着数据库中的一张表,驼峰命名方式
-
models.Model
基础模块,会根据字段和模型名在后台数据库生成对应的表文件
-
models.TransientModel
临时模块,用于弹出信息的临时数据,会在后台生成对应的表,但是表的内容根据配置,一定时间后被清除,不会长时间保存,如向导时用
-
models.AbstractModel
抽象模块,类似于抽象类,常用于继承
2.model字段类型
odoo对象支持的字段类型有
基础:char,text,boolean,integer,float,date,datetime,binary
复杂:selection,function
关系:one2one,many2one,one2many,many2many
3.self
self是什么,目前旧版本中使用的self是对,游标cr,用户id,上下文context,模型,记录集,缓存的封装
可以通过self.xx获取到这些封装得东西,如self.cr,self.uid
在查出来某模型的记录后可以通过record.xx=value来直接修改记录的字段内容,同样在重写模型的write方法
中,也可以通过self.xx=value来指定新增记录中某字段得值
此时注意
a修改查出来的记录字段值来改变数据库内容,是通过改变缓存中的值触发数据库写记录来达到的
b重写write方法是,在write方法中每调用一次self.xx=value语句,都会触发数据库的写操作,因此一般用
for rec in self:
rec.xx = xx
- 操作缓存:
环境存储了模型的缓存记录,因此可以通过环境来获取增加,修改,删除记录,而触发数据库更改,从而达到操作数据库的目的
如 新增:
self.env['模型'].create(vals)
访问当前用户:
self.env.user
更新缓存,触发数据库操作:
self.env.invaludate_all()
- self常用接口
普通查询:返回记录集,后续通过修改记录值来触发数据库修改
search_count根据search的结果进行统计计数,如果要计数,可以直接在search的时候加上count=true属性,这样计算更快
self.search(domain)#从当前模型的self中查询
self.env['model'].search(domain)#获取某个model的环境,查询其中的记录集
只读查询:返回列表,只能提取内容,不能触发数据库修改
self.search_read(['要查询的字段'])
统计数量:返回符合条件的记录条数
self.search_count(domain)
浏览:通过一系列的id值,返回对应的记录集
self.browse([id])
删除:
self.unlink(domain)
- new ids
odoo在创建一个新纪录时,会使用models.ids虚拟一个记录id,可以通过如下来判断
if is instance(record.id,models.NewId)
记录集
model的数据是通过数据集的形式来使用的,定义在model里的函数执行时他们的self变量也是一个数据的集合
class AModel(models.Model):
_name = 'a.model'
def a_method(self): # self can be anywhere between 0 records and all records in the database
self.do_operation()
def do_operation(self):
print self # => a.model(1, 2, 3, 4, 5)
for record in self:
print record # => a.model(1), then a.model(2), then a.model(3), ...
获取有关联的字段(one2many,many2one,many2many)也是返回一个数据集合,如果字段为空则返回空的集合
每个赋值语句都会触发数据库字段更新,同时更新多个字段时可使用或者更新多条记录时使用write函数
# 3 * len(records) database updates
for record in records:
record.a = 1
record.b = 2
record.c = 3
# len(records) database updates
for record in records:
record.write({'a': 1, 'b': 2, 'c': 3})
# 1 database update
records.write({'a': 1, 'b': 2, 'c': 3})
数据缓存和预读取
odoo会为记录保留一份缓存,他有一种内置的预读取机制,通过缓存来提升性能集合运算符
record in set 返回record是否在set中,record须为单条记录,record not in set 反之
set1<=set2返回set1是否为set2的子集
set1>=set2返回set2是否为set1的子集
set1|set2返回set1和set2的并集
set1&set2返回set1和set2的交集
set1-set2返回在集合set1中但不在set2的记录
其他集合运算
- filtered()返回满足条件的数据集
# only keep records whose company is the current user's
records.filtered(lambda r: r.company_id == user.company_id)
# only keep records whose partner is a company
records.filtered("partner_id.is_company")
- sorted()返回根据提供的键排序之后的结果
# sort records by name
records.sorted(key=lambda r: r.name)
- mapped()返回应用了指定函数之后的结果集
#returns a list of summing two fields for each record in th set
records.mapped(lambda r:r.field1 + r.field2)
#函数也可以是字符串 对应记录的字段
#return a list of names
records.mapped('name')
#returns a recordset of partners
record.mapped('partner_id')
运行环境
运行环境保存了很多ORM相关的变量:数据库查询游标,当前用户,元数据,还有缓存,所有的model数据集都有不可改变的环境变量,可使用env来访问,如records.env.user,records.env.cr,records.env.context,运行环境还可用于为其他模型初始化一个空的集合并对该模型进行查询
self.env['res.partner'].search([['is_company','=',True],['customer','=',True]])
#res.partner(1,2,3,4,5,7,77)
- sudo()使用现有数据集创建一个新运行环境,得到一个基于新运行环境的数据集的拷贝
#create partner object as administrator
env['res.partner'].sudo().create({'name':"A Partner"})
#list partners visible by the "public" user
public = env.ref("base.public_user")
env['res.partner'].sudo(public).search([])
- with_context
一个参数时可用于替换当前运行环境的context,多个参数时通过keyword添加到当前运行环境context或单参数时设置context - with_env()
完整替换当前运行环境
常用ORM函数
- search(domain)
作用:搜索指定domain的记录集,接受domain表达式参数
参数:搜索domain[()]
返回值:符合搜索结果的对象列表,可以通过limit,offset参数返回一个子集,还可以通过order参数对数据排序
self.search([('is_company','=',True),('customer','=',True)])
#res.partner(1,2,3,4,5,7,77)
self.search([('is_company','=',True)],limit=1).name
#'Agrolait'
students = self.search[('name','=',self.name)]
#查所有与当前名字相同的学员
如果只需要知道满足条件的数据数量,可以使用search_count()函数
- create(val)
接收多个字段,值的组合,返回新创建的数据集
作用:创建对象
参数:要创建的对象字典
返回值:新创建的对象
student = self.create({
'name':'张三'
'age':26,
'sex':'man',
'sno':'1'
})
#创建了一个新学员
self.create({'name':'new name'})
- write(val)
接收多个字段,值组合,会对指定数据集的所有记录进行修改,不返回
作用:修改对象
参数:需要修改参数的字典
self.write({'name':'new name'})
browse(ids)根据数据的id或一组id来查找,返回符合条件的数据集合
作用:获取指定记录的对象
参数:记录值的ids
返回值:对象列表
browse方法其实在v7版本中应用的更多,因为v7版本的search等方法返回值是ids,而v8版本中的返回值已经是目标对象的列表了,因此就不需要再次调用browse方法unlink()
删除记录
self.unlink
- exists()得到某个数据集中保留在数据库中的那部分,或在对一个数据集进行处理后重新赋值
if not record.exists():
raise Exception('The record has been deleted')
records.may_remove_dome()
#only keep records which were not deleted
records = records.exists()
- ref()运行环境函数根据提供的external id返回对应得数据记录
env.ref('base.group_public')
res.groups(2)
- ensure_one()检验某个数据集是否只包含单条数据,若不是则报错
records.ensure_one()
#和下面语句效果相同
assert len(records) == 1,'Expected singleton'