在上一篇文章中,我们概览了模型创建以及如何从模型中载入和导出数据。现在我们已有数据模型和相关数据,是时候学习如何编程与其进行交互 了。模型的 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开发之记录集 - 使用模型数据的更多相关文章

  1. 第九章 Odoo 12开发之外部 API - 集成第三方系统

    Odoo 服务器端带有外部 API,可供网页客户端和其它客户端应用使用.本文中我们将学习如何在我们的客户端程序中使用 Odoo 的外部 API.为避免引入大家所不熟悉的编程语言,此处我们将使用基于 P ...

  2. 第十三章 Odoo 12开发之创建网站前端功能

    Odoo 起初是一个后台系统,但很快就有了前端界面的需求.早期基于后台界面的门户界面不够灵活并且对移动端不友好.为解决这一问题,Odoo 引入了新的网站功能,为系统添加了 CMS(Content Ma ...

  3. 第十二章 Odoo 12开发之报表和服务端 QWeb

    报表是业务应用非常有价值的功能,内置的 QWeb 引擎是报表的默认引擎.使用 QWeb 模板设计的报表可生成 HTML 文件并被转化成 PDF.也就是说我们可以很便捷地利用已学习的 QWeb 知识,应 ...

  4. 第十一章 Odoo 12开发之看板视图和用户端 QWeb

    QWeb 是 Odoo 使用的模板引擎,它基于 XML 来生成 HTML 片断和页面.通过 QWeb可生成内容丰富的看板(Kankan)视图.报表和 CMS 网页.本文中我们将学习QWeb 语法以及如 ...

  5. 第六章 Odoo 12开发之模型 - 结构化应用数据

    在本系列文章第三篇Odoo 12 开发之创建第一个 Odoo 应用中,我们概览了创建 Odoo 应用所需的所有组件.本文及接下来的一篇我们将深入到组成应用的每一层:模型层.视图层和业务逻辑层. 本文中 ...

  6. 第五章 Odoo 12开发之导入、导出以及模块数据

    大多数Odoo 模块的定义,如用户界面和安全规则,实际是存储在对应数据表中的数据记录.模块中的 XML 和 CSV 文件不是 Odoo 应用运行时使用,而是载入数据表的手段.正是因为这个原因,Odoo ...

  7. 第四章 Odoo 12 开发之模块继承

    Odoo 的一个强大功能是无需直接修改底层对象就可以添加功能.这是通过其继承机制来实现的,采取在已有对象之上修改层来完成.这种修改可以在不同层上进行-模型层.视图层和业务逻辑层.我们创建新的模块来做出 ...

  8. 第三章 Odoo 12 开发之创建第一个 Odoo 应用

    Odoo 开发通常都需要创建自己的插件模块.本文中我们将通过创建第一个应用来一步步学习如何在 Odoo 中开启和安装这个插件.我们将从基础的开发流学起,即创建和安装新插件,然后在开发迭代中更新代码来进 ...

  9. 第二章 Odoo 12开发之开发环境准备

    在更深入了解 Odoo 开发之前,我们应配置好开发环境并学习相关的基础管理任务.本文中,我们将学习创建 Odoo 应用所需用到的工具和环境配置.这里采用 Ubuntu 系统来作为开发服务器实例的主机, ...

随机推荐

  1. FIR和IIR设计指标

  2. python初探爬虫

    python爬虫初探 爬取前50名豆瓣电影: 废话少说,直接上代码! import re​import requestsfrom bs4 import BeautifulSoup​def get_co ...

  3. linux使用nmon监控、分析系统性能

    linux使用nmon监控.分析系统性能   一.概述 nmon是一种在AIX与各种Linux操作系统上广泛使用的监控与分析工具,相对于其它一些系统资源监控工具来说,nmon所记录的信息是比较全面的, ...

  4. .Net串口通讯中的若干问题(C#多串口硬件识别、热插拔、Close方法报错问题、IsOpen的可靠性问题)

    一.需求场景 最近有时间静下心来研究SDK,串口通讯的.要求实现识别cp210x和cp2303驱动的两款硬件,并且2303的优先级高,即有2303识别之,没有再识别210x:要求实现热插拔,拔掉自动断 ...

  5. exe4j 打包(多个jar打包)

    一,自行下载exe4j 注册码: 用户名和公司名可随便填A-XVK258563F-1p4lv7mg7savA-XVK209982F-1y0i3h4ywx2h1A-XVK267351F-dpurrhny ...

  6. django 项目分析

    项目要点 一.功能制定 1.用户功能 #.登陆 #.权限组功能 2.数据展示功能 #.列表展示 #.详细信息展示 #.图标展示 3.资源管理功能 #远程管理 #对远程服务器上的进程具有 #开启 #关闭 ...

  7. linux 服务器安装mysql5.6

    1.移除CentOS默认的mysql-libs: whereis mysql 2.为了避免冲突,先移除CenttOS上默认的mysql-libs: yum remove mysql-libs 3.然后 ...

  8. javascript 的学习笔记(第一天)

    1.==与=== ==   先转换类型,再比较 ===  直接比较 2.parseInt  把字符串转成整数 parsefloat  把字符串转成小数 3. 变量的作用域:变量起作用的范围 局部变量: ...

  9. Python代码中func(*args, **kwargs)

    这是Python函数可变参数 args及kwargs *args表示任何多个无名参数,它是一个tuple **kwargs表示关键字参数,它是一个dict 测试代码如下: def foo(*args, ...

  10. Delphi 查找标题已知的窗口句柄,遍历窗口控件句柄

    有了回调函数的概念及上面的例子,我们可以继续了.其实想要找到一个标题已知的窗口句柄,用一个API函数就可以了:FindWindow.其函数原形是:function FindWindow(lpClass ...