Odoo开发之记录集 – 使用模型数据
Posted dengl
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Odoo开发之记录集 – 使用模型数据相关的知识,希望对你有一定的参考价值。
Odoo 12开发之记录集 – 使用模型数据
一·启动服务链接数据库
# 1.启动服务,链接数据库
./odoo-bin shell -c debian/odoo.conf -d library_db
# 2. self 当前操作的对象
res.users(1,)
# 3. self._name # 当前模块名称 ,获得记录集模型名
‘res.users‘
# 4. self.login # 记录值,至于怎么用.未知
‘__system__‘
# 5. self.name # name 值为OdooBot
OdooBot
二·执行环境
环境属性
# 1. self.env , 获得当前环境
<odoo.api.Environment object at 0x7f78a26026a0>
# 2. self.env中的执行环境属性
# env.cr 正在使用的数据库游标(cursor)
<odoo.sql_db.Cursor object at 0x7f9df38efc50>
# env.user 是当前用户的记录
res.users(1,)
# env.uid 是会话用户的id
1
# env.context 是会话上下文的不可变字典
{‘lang‘: ‘en_US‘, ‘tz‘: ‘Europe/Brussels‘}
# 由于是 frozendict 由odoo 自行封装的字典,不可变
env.context[‘test‘]=‘test‘
错误: NotImplementedError: ‘__setitem__‘ not supported on frozendic
# search([(‘name‘,‘like‘,‘Ad‘)]) 搜索查询,search和domain表达式结合使用
self.env[‘res.partner‘].search([(‘name‘,‘like‘,‘Ad‘)])
# browse() 也是搜索查询,browse放id列表如[1,2,3,4]
self.env[‘res.partner‘].browse([1,2,3,4])
环境上下文
### 环境上下是带有会话数据的字典.
### 作用:用于客户端和服务端ORM和业务逻辑中. 可以将信息从一个视图中传递到另一个视图中.
# 比如:上一个视图中活跃的id,通过点击链接或者按钮.将默认值带入到下一个视图中.
# 形式:
{‘lang‘: ‘en_US‘, ‘tz‘: ‘Europe/Brussels‘, ‘uid‘: 2}
# 1. 查看上下文命令
self.context_get()
self.env.context
# lang :用户语言 tz:时区信息 uid:当前用户的id
修改记录集执行环境
### 记录集执行环境是不可变的.不可被修改,因此需要创建一个新的环境来修改
# 1. env.with_context({diciionary}) # 替换原上下文为新的上下文
# 2. env.with_context(key=value,...) # 修改当前上下文,为一些键设置值
# 3. env.sudo(user) # 传入一条用户记录并返回该用户的环境.如果未传用户,则使用 __system__超级用户. 可绕过安全规则执行指定操作
# env.ref()函数 , 传入一个外部标识符字符串并返回它的记录
self.env.ref(‘base.user_root‘)
res.users(1,)
三· 记录集和作用域domain查询数据
创建记录
### search方法 接收一个域表达式并返回符合条件的记录集. 空域[]将返回所有的记录
# 关键字参数:
# order是一个数据库查询语句种的 order by使用的字符串.通常是一个逗号分隔的字段名列表。每个字段都可接DESC关键字,用于表示倒序排列。
# limit 设置获取记录的最大条数
# offset 忽略钱n条记录,配合limit使用
# 用法:
self.env[‘res.partner‘].search([(‘name‘, ‘like‘, ‘Pac‘)])
# 结果
res.partner(42, 62)
### search_count()方法 返回记录条数. 先知道条数,节约内存
# 用法
self.env[‘res.partner‘].search_count([])
#结果
681
### browse()方法接收一个ID列表或单个ID并返回这些记录的记录集
# 用法
self.env[‘res.partner‘].browse([42, 62])
# 结果
res.partner(42, 62)
域表达式
### domain用于过滤数据记录. 使用的是特殊语法来供Odoo ORM解析
# 写法:
(‘字段名’, ‘运算符’, ‘值’) 组成的元组
# 字段名:是一个有待过滤的字段,可用点号标记,来表示关联模型种的字段
# 值: 分为两种运行上下文:
① 窗口操作或字段属性等客户端,使用原生字段值,不能使用点标记符
② 服务端可以是一个对象
# 运算符
# 比较: <,>,<=,>=,=,!=
# =like 和 =ilike
# like匹配 ‘%value%‘模式,ilike与其相似.
# not like 和 not ilike
# child of 支持层级关联的模型中 查找层级关系种的子级值
# in 和 not in 在和不在
# 多条件,即包含多个元祖 默认是 AND 运算符
# 例子: 此例子为编造
self.env[‘res.partner‘].search([(‘message_follower_ids‘, ‘in‘, [user.partner_id.id]),(‘user_id‘, ‘=‘, user.id)])
# 显式逻辑运算符 ‘&‘符号表示 AND 运算符(默认值),管道运算符’|‘表示OR运算符
# 例子:
[‘|‘,(‘message_follower_ids‘, ‘in‘, [user.partner_id.id]),‘|‘,(‘user_id‘, ‘=‘, user.id),(‘user_id‘, ‘=‘, False)]
四·记录集中访问数据
访问记录中数据
# 1.记录集仅有一条记录时,称为单例
self.name --> OdooBot
### 注意:
尝试访问有多条记录的记录集字段值会产生错误,所以在不确定操作的是否为单例数据集时就会产生问题。对于设计仅操作单例的方法,
可在开头处使用 self.ensure_one(),如果 self 不是单例时将抛出错误。
访问关联字段
### 包含:many-to-one, one-to-many和many-to-many
# 对于many-to-one,其值可以是单例或空记录集
self.company_id ---> res.company(1,)
self.company_id.name ---> ‘YourCompany‘
self.company_id.currency_id ---> res.currency(1,)
self.company_id.currency_id.name ---> ‘EUR‘
# 空记录可像单例一样操作,访问其字段值不会返回错误而是返回 False
self.company_id.parent_id ---> res.company()
self.company_id.parent_id.name ---> False
访问时间和日期值
# 1.查询上次admin用户登录日期
self.browse(2).login_date ---> datetime.datetime(2019, 1, 8, 9, 2, 54, 45546)
# 2. 可以利用python的datetime模块进行时间计算
Odoo 还在odoo.tools.date_utils模块中提供额外的便利函数
# start_of(value, granularity)是某个特定刻度时间区间的开始时间,这些刻度有year, quarter, month, week, day或hour
# end_of(value, granularity)是某个特定刻度时间区间的结束时间
# add(value, **kwargs)为指定值加上一个时间间隔。**kwargs参数由一个relativedelta对象来定义时间间隔。这些参数可以是years, months, weeks, days, hours, minutes等等
# subtract(value, **kwargs)为指定值减去一个时间间隔
from odoo.tools import date_utils
from datetime import datetime
# 范围时间当前时间和指定时间参数:week,本周周一那天.如今天是2020年5月27,一周前是2020年5月25
# 输入: date_utils.start_of(datetime.now(), ‘week‘)
datetime.datetime(2020, 5, 25, 0, 0)
# end_of 同理 末尾时间
# 输入:date_utils.end_of(datetime.now(), ‘week‘)
datetime.datetime(2020, 5, 31, 23, 59, 59, 999999)
# add 指定添加多长时间
# 输入: date_utils.add(datetime.today(), months=2)
datetime.datetime(2020, 7, 27, 2, 40, 55, 424214)
# subtract 指定减去多长时间
# 输入: date_utils.subtract(datetime.today(), months=2)
datetime.datetime(2020, 3, 27, 2, 42, 21, 390561)
odoo.fields.Date和the odoo.fields.Datetime
# 1. fields.Date.today() # 返回当前日期,类型是datetime
datetime.date(2020, 5, 27)
# 2. fields.Datetime.now() # 返回当前日期+时间,类型是datetime
datetime.datetime(2020, 5, 27, 2, 46, 10)
# 3. fields.Date.context_today(record, timestamp=None)
fields.Date.context_today(self, timestamp=None)
datetime.date(2020, 5, 27)
# 在会话上下文中返回带有当前日期的字符串。时间从记录上下文中获取。可选项timestamp参数是一个datetime对象,如果传入将不使用当前时间,而使用传入值
# 4. fields.Datetime.context_timestamp(record, timestamp)
# 将原生的datetime值(无时区)转换为具体时区的datetime。时区从记录上下文中提取,因此使了前述函数名。
转换文本形式的日期和时间
# fields.Date和fields.Datetime都提供了如下函数
# 1. to_date将字符串转换为date对象
# 2. to_datetime(value)将字符串转换为datetime对象
# 3. to_string(value)将 date和datetime对象转换成字符串格式
# 4. Odoo 预设文本格式默认值:
odoo.tools.DEFAULT_SERVER_DATE_FORMAT ---> %Y-%m-%d
odoo.tools.DEFAULT_SERVER_DATETIME_FORMAT ---> Y-%m-%d %H:%M:%S
from odoo import fields
# 输入: fields.Datetime.to_datetime(‘2019-01-12 13:48:50‘)
datetime.datetime(2019, 1, 12, 13, 48, 50)
# 注意:
其他格式时间字符串转换成日期类型需要使用 datetime.strptime单独转换
from datetime import datetime
# 输入: datetime.strptime(‘1/1/2019‘, ‘%d/%m/%Y‘)
datetime.datetime(2019, 1, 1, 0, 0)
五·记录中写入
# odoo写入分为两种模式:
# 1. 使用对象形式直接分配 . 简单但一次只能操作一条记录,效率较低
# 2. 使用write() 方法 . 写入关联字段时使用特殊语法,但每条命令可写入多个字段和记录,记录计算更为高效
对象形式分配值写入
# 查询一条数据
root = self.env[‘res.users‘].browse(1)
# 更改root对象name值
root.name = ‘Superuser‘
# root.name
Superuser
###
通过 write()方法写入
# 用write()方法来同时更新多条记录中的多个字段,仅需一条数据库命令
# write() 接收一个字典来进行字段和值的映射
# 得到一个空对象
Partner = self.env[‘res.partner‘]
# 查询值
recs = Partner.search( [(‘name‘, ‘ilike‘, ‘Azure‘)] )
# 修改值, 修改成功为True
recs.write({‘comment‘: ‘Hello!‘})
### 在写入many-to-one字段时,写入的值必须是关联记录的ID。
例如,我们不用self.write({‘user_id’: self.env.user}),而应使用self.write({‘user_id’: self.env.user.id})
### 写入to-many字段时,写入的值必须使用和 XML 数据文件相同的特殊语法
比如,我们设置图书作者列表为author1和author2,这是两条 Partner 记录。| 管道运算符可拼接记录来创建一个记录集,因此使用对象形式的分配可以这么写
publisher.child_ids = author1 | author2
# 使用write()方法
book.write( { ‘child_ids‘: [(6, 0, [author1.id, author2.id])] } )
### (4, id, _)添加一条记录
### (6, _, [ids])替换关联记录列表为所传入的列表
写入日期和时间值
### 可以使用文本形式值写入日期和时间:
# 如:
# 搜索到一个对象
demo = self.search([(‘login‘, ‘=‘, ‘demo‘)])
# 更改demo.login_date值
demo.login_date = ‘2019-01-01 09:00:00‘
# 查看
demo.login_date ---> datetime.datetime(2019, 1, 1, 9, 0)
创建和删除记录
#创建和删除记录 通过 create()和unlink()模型方法实现
### create方法
# 得到一个空的对象 ,这个对象仅被加载到了内存种,没有实际保存到数据库种
Partner = self.env[‘res.partner‘]
# 创建一个对象,此时就被保存到数据库种
new = Partner.create({‘name‘: ‘ACME‘, ‘is_company‘: True})
res.partner(64,)
# 查看cutomer属性值
print(new.customer) # customer标记默认为 True
### unlink()方法会删除记录
# 搜索到某条记录
rec = Partner.search([(‘name‘, ‘=‘, ‘ACME‘)])
rec.unlink() # 删除 partner 关联字段的串联删除
拷贝模型记录值
### copy() 方法,会自动创建一条记录.这条记录会被保存到数据库中
# 查询出一条记录
demo = self.env.ref(‘base.user_demo‘)
# 拷贝
new = demo.copy({‘name‘: ‘Daniel‘, ‘login‘: ‘daniel‘, ‘email‘: ‘‘})
六·重构记录集
# 1. recordset.ids 返回记录集元素的ID列表
# 2. recordset.ensure_one()检查是否为单条记录(单例);若不是,则抛出ValueError异常
# 3. recordset.filtered(func)返回一个过滤了的记录集,func可以是一个函数或一个点号分隔的表达式来表示字段路径,可参见下面的示例。
# 4. recordset.mapped(func)返回一个映射值列表。除函数外,还可使用文本字符串作为映射的字段名。
# 5. recordset.sorted(func)返回一个排好序的记录值。除函数外,文本字符串可用作排序的字段名。reverse=True是其可选参数。
>>> rs0 = self.env[‘res.partner‘].search([])
>>> len(rs0)
48
>>> starts_A = lambda r: r.name.startswith(‘A‘)
>>> rs1 = rs0.filtered(starts_A)
>>> print(rs1)
res.partner(63, 59, 14, 35)
>>> rs1.sorted(key=lambda r: r.id, reverse=True)
res.partner(63, 59, 35, 14)
>>> rs2 = rs1.filtered(‘is_company‘)
>>> print(rs2)
res.partner(14,)
>>> rs2.mapped(‘name‘)
[‘Azure Interior‘]
>>> rs2.mapped(lambda r: (r.id, r.name))
[(14, ‘Azure Interior‘)]
rs1 | rs2是一个集合的并运算,会生成一个包含两个记录集所有元素的记录集
rs1 + rs2是集合加法运算,会将两个记录集拼接为一个记录集,这可能会带来集合中有重复记录
rs1 & rs2是集合的交集运算,会生成一个仅在两个记录集中同时出现元素组成的数据集
rs1 – rs2是集合的差集运算,会生成在rs1中有但rs2中没有的元素组成的数据集
# 还可以使用分片标记,例如:
rs[0]和rs[-1]分别返回第一个和最后一个元素
rs[1:]返回除第一元素外的记录集拷贝。其结果和rs – rs[0]相同,但保留了排序
# 删除或添加元素来修改记录集
self.author_ids |= author1:如果不存在author1,它会将author1加入记录集
self.author_ids -= author1:如果author1存在于记录集中,会进行删除
self.author_ids = self.author_ids[:-1]删除最后一条记录
七·底层 SQL 和数据库事务
# 数据库引入运算在一个数据库事务上下文中执行
# 通过数据库游标self.env.cr
# 执行事务缓冲的写运算 ,提交数据
self.env.cr.commit()
# 取消上次 commit之后的写运算,如果尚未 commit,则回滚所有操作
self.env.cr.rollback()
# execute() 方法 执行SQL语法 self.env.cr.execute()
参数一: 运行的SQL 语句,
可选参数:一个用作 SQL 参数值的元组或列表。这些值会用在%s占位符之处。
# 用法:
self.env.cr.execute("SELECT id, login FROM res_users WHERE login=%s OR id=%s", (‘demo‘,1)
self.env.cr.execute("SELECT id, login FROM res_users WHERE login=%s OR id=%s", (‘demo‘,1))
### select 查询记录
# fetchall() 函数以元组列表的形式获取所有行
# 输入:self.env.cr.fetchall()
[(1, ‘__system__‘), (6, ‘demo‘)]
# dictfetchall()则以字典列表的形式获取
# 输入: self.env.cr.dictfetchall()
[{‘id‘: 1, ‘login‘: ‘__system__‘}, {‘id‘: 6, ‘login‘: ‘demo‘}]
### 修改DML操纵语言
self.env.cache.invalidate()
以上是关于Odoo开发之记录集 – 使用模型数据的主要内容,如果未能解决你的问题,请参考以下文章