第七章 Odoo 12开发之记录集 - 使用模型数据
在上一篇文章中,我们概览了模型创建以及如何从模型中载入和导出数据。现在我们已有数据模型和相关数据,是时候学习如何编程与其进行交互 了。模型的 ORM(Object-Relational Mapping)提供了一些交互数据的方法,称为 API(Application Programming Interface)。这包括基本的增删改查(CRUD)操作,也包括一些其它操作,如数据导入导出,以及改善用户界面和体验的工具方法。它还包含一些我们在前面文章中所看到的装饰器。这些都让我们可以通过添加新的方法来调用 ORM 进行相关操作。
本文主要内容有:
- 使用 shell 命令交互式地学习 ORM API
- 理解执行环境和上下文
- 使用记录集和作用域(domain)查询数据
- 在记录集中访问数据
- 在记录中写入
- 编写记录集
- 使用底层 SQL 和数据库事务
开发准备
本文代码使用交互式 shell 命令行执行,无需使用前面章节的代码。
使用 shell 命令行
Python带有命令行界面,是研究其语法一个很好的方式。Odoo 也有类似的功能,可以交互式的测试命令的执行效果,这就是 shell 命令行。在命令行中执行以下命令并指定数据库即可使用:
1
|
~/odoo-dev/odoo/odoo-bin shell -d dev12
|
在odoo的目录下可以直接用以下命令执行,odoo12是指数据库名
python odoo-bin shell -d odoo12
此时在终端上可以看到正常的服务启动信息,等到出现>>>Python提示符时即为完成,可以输入命令了。
ℹ️Odoo 9中的修改
shell 功能在9.0中才添加。Odoo 8.0可使用社区模块来添加这一功能。只需下载并放入 addons 路径即可使用,下载请见应用市场。
此处 self 表示管理员用户的记录,可通过如下命令进行确认:
1
2
3
4
5
6
|
>>> self
res.users(1,)
>>> self._name
'res.users'
>>> self.login
'__system__'
|
在以上 shell 会话中,我们检查了自己的环境:
- self命令表示res.users记录集,仅包含一条 id 为1的记录
- 查看self._name获得记录集模型名,你可能猜到了,是’res.users’
- 记录的 name 值为OdooBot
- 记录的 login 字段值为__system__
ℹ️Odoo 12中的修改
id 号为1的超级用户由原来的 admin 变成无法直接登录的内部系统用户。现在 admin 的 id 号为 2并且不是超级用户,但默认各应用会将其加入所有安全组。主要原因是避免用户使用超级用户账号来执行日常操作。这样的风险是该用户会跳过权限规则并导致数据的不一致,比如跨公司(cross-company)关联。现在超级用户仅用于检测问题或具体的跨公司操作。
和 Python 一样,可通过 Ctrl + D退出该命令行。此时会结束服务并返回到系统shell 命令行。
执行环境
Odoo shell 中包含一个 self 引用,类似于在res.users模型的方法中看到的那样。如我们所见,self 是一个记录集。记录集自带环境信息,包括浏览信息的用户以及其它上下文信息,如语言和时区。下面我们会学习执行环境中可用的属性、环境上下文的用处以及如何修改该上下文。
环境属性
我们可通过如下代码查看当前环境:
1
2
|
>>> self.env
<odoo.api.Environment object at 0x7f78a26026a0>
|
self.env 中的执行环境中有以下属性:
- env.cr是正在使用的数据库游标(cursor)
- env.user是当前用户的记录
- env.uid是会话用户 id,与env.user.id相同
- env.context是会话上下文的不可变字典
环境还提供对带有所有已安装模型注册表的访问,如self.env[‘res.partner’]返回一条对 partner 模型的引用。然后我们还可以对其使用search()或browse()方法来获取记录集:
1
2
|
>>> self.env['res.partner'].search([('name', 'like', 'Ad')])
res.partner(10, 35, 3)
|
上例中返回的res.partner模型记录集包含三条记录,id 分别为10, 35和3。记录集并没有按 id 排序,因为使用了相应模型的默认排序。就 partner 模型而言,默认的_order为display_name。
环境上下文
环境上下文是一个带有会话数据的字典,可用于客户端用户界面以及服务端 ORM 和业务逻辑中。在客户端中,它可以把信息从一个视图带到另一个视图中,比如前一个视图中活跃的记录 id,通过点击链接或按钮,可将默认值带入到下一个视图中。在服务端中,一些记录集的值会依赖于上下文提供的本地化设置。具体的例子有lang键影响可翻译字段的值。上下文还可为服务端代码提供信号。比如active_test键在设为 False 时,会改变ORM中search()方法的行为,它会忽略记录中的active标记,inactive(假删除)的记录也会被返回。
客户端的初始上下文长这样:
1
|
{'lang': 'en_US', 'tz': 'Europe/Brussels', 'uid': 2}
|
补充:服务端查看上下文命令为self.context_get()或self.env.context
其中 lang 键为用户语言,tz 为时区信息,uid 为当前用户 id。记录中的内容随当前依赖的上下文可能会不同:
- translated字段根据活跃的 lang 语言不同值也会不同
- datetimep字段根据活跃的的 tz 时区不同时间会不同
在上一个视图中点击链接或按钮打开表单时,一个active_id键会被加入上下文,它带有原表单我们所在位置记录的 id。以列表视图为例,active_ids上下文键中包含上一个列表中所选择的记录 id 列表。
在客户端中,上下文可用于使用default_或default_search_前缀在目录视图上设置默认值或启动默认过滤器。举例如下:
- 设置当前用户为user_id字段默认值,使用{‘default_user_id’: uid}
- 在目标视图上默认启动filter_my_books过滤器,使用{‘default_search_filter_my_tasks’: 1}
修改记录集执行环境
记录集执行环境是不可变的,因此不能被修改,但我们可以创建一个变更环境并使用它来执行操作。我们通过如下方法来实现:
- env.sudo(user)中传入一条用户记录并返回该用户的环境。如未传入用户,则使用__system__超级用户root,这时可绕过安全规则执行指定操作。
- env.with_context(<dictionary>) 替换原上下文为新的上下文
- env.with_context(key=value,…)修改当前上下文,为一些键设置值
此外还有一个env.ref()函数,传入一个外部标识符字符串并返回它的记录,请参见:
1
2
|
>>> self.env.ref('base.user_root')
res.users(1,)
|
使用记录集和作用域(domain)查询数据
在方法或 shell 会话中,self表示当前模型,并且我们仅能访问该模型的记录。要访问其它模型就需要使用self.env。例如self.env[‘res.partner’]返回一条对 Partner 模型的引用(也是一个空记录集)。我们可以使用search()或browse()来获取记录集,其中search()方法使用域表达式来定义记录选择范围。
创建记录集
search()方法接收一个域表达式并返回符合条件记录的记录集。空域[] 将返回所有记录。
ℹ️如果模型有特殊字段 active,默认只有active=True的记录才在选择范围内
还可以使用以下关键字参数:
- order是一个数据库查询语句中ORDER BY使用的字符串,通常是一个逗号分隔的字段名列表。每个字段都可接DESC关键字,用于表示倒序排列。
- limit设置获取记录的最大条数
- offset忽略前 n 前记录,可配合limit使用来一次查询指定范围记录
有时我们只要知道满足某一条件的记录条数,这时可使用search_count()来返回记录条数而非记录集。这节约了先获取记录列表再记数的开销,在还没有获取记录集且仅想知道记录条数时这样会更高效。
browse()方法接收一个 ID 列表或单个ID并返回这些记录的记录集。在我们知道 ID 并想要获取记录时这就非常方便了。
一些使用示例如下:
1
2
3
4
|
>>> self.env['res.partner'].search([('name', 'like', 'Pac')])
res.partner(42, 62)
>>> self.env['res.partner'].browse([42, 62])
res.partner(42, 62)
|
域表达式
域(domain)用于过滤数据记录。它使用一个特殊语法来供 Odoo ORM解析,生成数据库查询中的 WHERE 表达式。域表达式是一组条件组成的列表,每个条件都是一个(‘字段名’, ‘运算符’, ‘值’)组成的元组,例如,[(‘is_done’,’=’,False)]是仅带有一个条件的有效域表达式。以下是对各个元素的说明:
- 字段名:是一个待过滤字段,可使用点号标记来表示关联模型中的字段
- 值:在 Python 表达式中运行。可使用字面值,如数字、布尔值、字符串和列表,也可使用运行上下文中的字段和标识符。针对域其实有两种运行上下文:
- 在窗口操作或字段属性等客户端中使用时,可使用原生字段值来渲染当前可用视图,但不能对其使用点标记符
- 在服务端使用时,如安全记录规则或服务端 Python 代码中,可以对字段使用点标记符,因为当前记录是一个对象
- 运算符:可以是以下中的一个
- 常用比较运算符有<, >, <= , >=, =和!=。
- ‘=like’和’=ilike’匹配某一模式,这里下划线_匹配单个字符,百分号%匹配任意一组字符。
- ‘like’匹配’%value%’模式,’ilike’与其相似但忽略大小写。还可以使用’not like’和’not ilike’运算符。
- ‘child of’在配置支持层级关联的模型中查找层级关系中的子级值。
- ‘in’ 和’not in’用于查看给定列表的包含,所以其值为一个列表。用于to-many关联字段时,in运算符和contains运算符一样。
- ‘not in’是in的反向运算,用于查看不在列表中的值。
域表达式是一个列表并且包含多个条件元组。默认这些条件使用AND逻辑运算符连接,也就是说它仅返回满足所有条件的记录。也可以使用显式逻辑运算符 – ‘&‘符号表示 AND 运算符(默认值),管道运算符’|‘表示OR运算符。这两个运算符会作用于接下来的两项,递归执行。后面我们会一起来详细了解。
ℹ️域表达式使用了更为正式的定义方式:前缀标记法,也称波兰表达式(Polish notation):运算符放在运算项之前。AND和OR是二元运算符,而NOT是一元运算符。
感叹号’!’表示NOT运算符,可用于下一项的运算,因此要放执行的否定项之前。例如[‘!’, (‘is_done’,’=’,True)]将过滤出所有未完成(not-don e)的记录。
下一项本身也可以是一个作用其后续项的运算符,形成一个嵌套条件。下例可以有助于我们进行理解。在服务端记录规则中,可以找到类似下面这样的域表达式:
1
2
3
4
5
6
|
['|',
('message_follower_ids', 'in', [user.partner_id.id]),
'|',
('user_id', '=', user.id),
('user_id', '=', False)
]
|
这个域过滤出当前用户在follower列表中并且是负责人用户,或者没有负责人用户的用户集。第一个’|’或运算符作用于 follower 条件以及下一个条件的结果。下一个条件是后面两个条件的并集:用户ID是当前会话用户或未进行设置。下图是上例域表达式的抽象语法树表示:
在记录集中访问数据
一旦获取了数据集,就可以查看其中包含的数据了。下面的几个部分中我们就来看看如何访问记录集中的数据。我们可以获取单条记录的字段值,称为单例(singleton)。关联字段带有特殊属性,我们可通过点号标记来查看关联记录。最后我们一起思考处理日期和时间记录并进行格式转换。
访问记录中数据
记录集的一个特例是仅有一条记录,称为单例。单例仍是记录集,在需要记录集的地方均可使用。与多元素记录集不同,单例可使用点号标记访问它的字段,如:
1
2
|
>>> print(self.name)
OdooBot
|
下个例子中我们看看同一个 self 单例和记录集相同的行为,我们可对其进行遍历。它只有一条记录,所以只会打印出一个名称:
1
2
3
4
|
>>> for rec in self:
... print(rec.name)
...
OdooBot
|
尝试访问有多条记录的记录集字段值会产生错误,所以在不确定操作的是否为单例数据集时就会产生问题。对于设计仅操作单例的方法,可在开头处使用self.ensure_one(),如果 self 不是单例时将抛出错误。
ℹ️空记录也是单例。这样很方便,因为访问字段会返回 None 而非抛出错误。对于关联字段同样如此,使用点号标记访问关联记录也不会抛出错误。
访问关联字段
如前面所见,模型可包含关联字段:many-to-one, one-to-many和many-to-many。这些字段类型的值为记录集。
对于many-to-one,其值可以是单例或空记录集。两种情况下都可以直接访问字段值。如下例中的命令是正确并安全的:
1
2
3
4
5
6
7
8
|
>>> 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。所以我们可以使用点号标记来遍历字段,而无需担心因其值为空而报错,如:
1
2
3
4
|
>>> self.company_id.parent_id
res.company()
>>> self.company_id.parent_id.name
False
|
访问时间和日期值
在记录集中,日期和日期时间值以原生 Python 对象展示,例如,在查询上次 admin 用户登录日期时:
1
2
|
>>> self.browse(2).login_date
datetime.datetime(2019, 1, 8, 9, 2, 54, 45546)
|
因为日期和日期时间是 Python 对象,它们可使用这些对象的所有功能。
ℹ️Odoo 12中的修改
date和datetime字段值以 Python 对象表示,而此前 Odoo 版本中它们以文本字符串表示。这些字段类型值仍可像此前 Odoo 版本中那样使用文本表示。
日期和时间在数据库中以原生的世界标准时间(UTC) 格式存储,不受时区影响。 在记录集中看到的datetime值也是 UTC格式,在客户端中向用户展示时,datetime值会根据当前会话的时间设置来转换成用户的时区。这一设置存储在上下文的tz键中,如{‘tz’: ‘Europe/Brussels’}。这一转换由客户端负责,而不是由服务端完成。
例如在布鲁塞尔(UTC+1)的用户输入12:00 AM数据库中会存储为10:00 AM UTC,而在纽约(UTC-4) 的用户查看时则为06:00 AM。
补充:请不要怀疑作者的数学是不是体育老师教的
第七章 Odoo 12开发之记录集 - 使用模型数据的更多相关文章
- 第九章 Odoo 12开发之外部 API - 集成第三方系统
Odoo 服务器端带有外部 API,可供网页客户端和其它客户端应用使用.本文中我们将学习如何在我们的客户端程序中使用 Odoo 的外部 API.为避免引入大家所不熟悉的编程语言,此处我们将使用基于 P ...
- 第十三章 Odoo 12开发之创建网站前端功能
Odoo 起初是一个后台系统,但很快就有了前端界面的需求.早期基于后台界面的门户界面不够灵活并且对移动端不友好.为解决这一问题,Odoo 引入了新的网站功能,为系统添加了 CMS(Content Ma ...
- 第十二章 Odoo 12开发之报表和服务端 QWeb
报表是业务应用非常有价值的功能,内置的 QWeb 引擎是报表的默认引擎.使用 QWeb 模板设计的报表可生成 HTML 文件并被转化成 PDF.也就是说我们可以很便捷地利用已学习的 QWeb 知识,应 ...
- 第十一章 Odoo 12开发之看板视图和用户端 QWeb
QWeb 是 Odoo 使用的模板引擎,它基于 XML 来生成 HTML 片断和页面.通过 QWeb可生成内容丰富的看板(Kankan)视图.报表和 CMS 网页.本文中我们将学习QWeb 语法以及如 ...
- 第六章 Odoo 12开发之模型 - 结构化应用数据
在本系列文章第三篇Odoo 12 开发之创建第一个 Odoo 应用中,我们概览了创建 Odoo 应用所需的所有组件.本文及接下来的一篇我们将深入到组成应用的每一层:模型层.视图层和业务逻辑层. 本文中 ...
- 第五章 Odoo 12开发之导入、导出以及模块数据
大多数Odoo 模块的定义,如用户界面和安全规则,实际是存储在对应数据表中的数据记录.模块中的 XML 和 CSV 文件不是 Odoo 应用运行时使用,而是载入数据表的手段.正是因为这个原因,Odoo ...
- 第四章 Odoo 12 开发之模块继承
Odoo 的一个强大功能是无需直接修改底层对象就可以添加功能.这是通过其继承机制来实现的,采取在已有对象之上修改层来完成.这种修改可以在不同层上进行-模型层.视图层和业务逻辑层.我们创建新的模块来做出 ...
- 第三章 Odoo 12 开发之创建第一个 Odoo 应用
Odoo 开发通常都需要创建自己的插件模块.本文中我们将通过创建第一个应用来一步步学习如何在 Odoo 中开启和安装这个插件.我们将从基础的开发流学起,即创建和安装新插件,然后在开发迭代中更新代码来进 ...
- 第二章 Odoo 12开发之开发环境准备
在更深入了解 Odoo 开发之前,我们应配置好开发环境并学习相关的基础管理任务.本文中,我们将学习创建 Odoo 应用所需用到的工具和环境配置.这里采用 Ubuntu 系统来作为开发服务器实例的主机, ...
随机推荐
- access数据库调用
1.工程环境配置(vs+qt) 点击项目,右键,选择项目设置,选择SQL 2.获取自己数据库的驱动版本有哪些,如下代码所示: QStringList drivers = QSqlDatabase::d ...
- 【idea】设置console控制台显示内容大小
Settings→Editor→General→Console 参考文章:https://blog.csdn.net/weixin_34363171/article/details/93444586
- java实现数字转中文大写
package cn.aikang.ChineseC; import java.util.Scanner; /** * @Description: TODO(这里用一句话描述这个类的作用) * @Au ...
- arm-linux-objdump 的使用
1. 查看静态库或.o 文件的组成文件 [arm@localhost gcc]$ armlinuxobjdump a libhello.a 2. 查看静态库或.o 文件的络组成部分的头部分 [a ...
- MySQL中查询所有数据库占用磁盘空间大小
查询所有数据库占用磁盘空间大小的SQL语句: 复制代码 代码如下:select TABLE_SCHEMA, concat(truncate(sum(data_length)/1024/1024,2), ...
- 数据库MySQL--修改数据表
创建数据库::create database 数据库名: 如果数据不存在则创建,存在不创建:Create database if not exists 数据库名 ; 删除数据库::drop datab ...
- Unable to resolve dependency for ':app@debug/compileClasspath': Could not resolve com.android.support:appcompat-v7:26.1.0
android studio 3.0 出现此问题可能是因为 你的android studio 时脱机状态 无法下载资源 这时候你可以点击左上角分File->Other Settings-> ...
- python中与time模块和datetime模块相关操作
使用time模块计时程序运行时长 import time time_start = time.time() #程序主体部分 time_end = time.time() print('总耗时: ', ...
- Windows dir
显示目录中的文件和子目录列表. DIR [drive:][path][filename] [/A[[:]attributes]] [/B] [/C] [/D] [/L] [/N] [/O[[:]so ...
- P2528 [SHOI2001]排序工作量之新任务
P2528 [SHOI2001]排序工作量之新任务 题目描述 假设我们将序列中第i件物品的参数定义为Ai,那么排序就是指将A1,…,An从小到大排序.若i<j且Ai>Aj,则<i ...