odoo10参考系列--ORM API 一(记录集、环境、通用方法和创建模型)
记录集
版本8.0中新东西: 这个在Odoo8.0中新加的API的页面文档应该是不断向前发展的主要开发API。同时它还提供了关于移植或桥接版本7和更早版本的“旧API”的信息,但没有明确地记录API。请查看旧文档。
模型和记录相关交互是通过记录集执行的,一个相同模型的有序记录集执行的。
警告
和记录集名字暗示的情况相反,包含副本的记录集当前是可以接收的。这在将来可能有所改变。
模型上定义的方法在一个记录集上执行,并且方法本身(self
)也是一个记录集:
class AModel(models.Model):_name = 'a.model'def a_method(self):# self can be anywhere between 0 records and all records in the# databaseself.do_operation()
迭代记录集将产生一个新的记录集(“单身”),就像一个Python字符串迭代产生一个单字符的字符串:
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), ...
字段访问
记录集提供一个“活动记录”接口:模型的字段可以直接读取和写入的记录属性,但只有单个记录(记录集中单个记录)。字段值也可以想字典项一样访问,这比通过方法getattr()动态获取字段名称更加简练和安全。设置字段的值会触发数据库更新操作:
>>> record.name
Example Name
>>> record.company_id.name
Company Name
>>> record.name = "Bob"
>>> field = "name"
>>> record[field]
Bob
试图在多个记录上读取或写入字段会引发错误。
访问一个关联字段 (Many2one
, One2many
, Many2many
) 总是返回一个记录集,如果字段没有设置则返回空集合。
危险
每次给字段赋值触发数据库更新,当在统一时间设置多个字段或设置多个记录上的字段(为相同的值),使用write()
:
# 3 * len(records) database updates
for record in records:record.a = 1record.b = 2record.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.name # first access reads value from database
record.name # second access gets value from cache
为了避免统一时间读取一条记录的一个字段,Odoo通过一些探索法预取记录和字段来获得更好的性能。在更定的记录上必须一次读取一个字段,事实上ORM在一个更大的记录集上读取字段,并把返回的值放入缓存以方便之后使用。预取得到记录集通常是来自每个记录通过迭代获取的记录集。此外,所有存储的简单字段(boolean, integer, float, char, text, date, datetime, selection, many2one)都是一起预取的; 它们对应模型表的列并在同一查询中有效的预取。
考虑下面的例子, partners
是一个有1000条记录的记录集。在没有预取的情况下,循环将进行2000次数据库查询。在有预取的情况下,仅进行一次数据库查询:
for partner in partners:print partner.name # first pass prefetches 'name' and 'lang'# (and other fields) on all 'partners'print partner.lang
预取对二次记录也起作用:当关系字段被读取时,它们的值(它们是记录)被订阅,以便将来进行预取。访问哪些二次记录中的一个,它预取自同一模型的所有二次记录。这使得下面的示例只生成两个查询,一个用于partners ,另一个用于countries:
countries = set()
for partner in partners:country = partner.country_id # first pass prefetches all partnerscountries.add(country.name) # first pass prefetches all countries
集合操作
记录集是不可变的,但同样模型的集合可以组合使用不同的设置操作,返回新的记录集。集合操作不保留顺序:
record in set
返回record
(必须是有一个元素的记录集) 是否在set
中。record not in set
是相反的操作(即不在集合中)set1 <= set2
和set1 < set2
返回set1
是否是set2的子集合 (分别的 严格的)set1 >= set2
和set1 > set2
返回set1
是否是set2的父集合 (分别的 严格的)set1 | set2
返回两个记录集的并集,一个包含了原来记录集所有记录的新记录集set1 & set2
返回两个记录集的交集,一个包含原来记录集共同拥有的记录的新记录集set1 - set2
返回一个仅在set1
中而不在set2中的新的记录集
其他记录集操作
记录集是可以迭代的,一次通常的Python工具(map()
, sorted()
, ifilter()
, ...)可提供转换只用,可是这些返回结果要么是一个list
,要么是一个除去在它们结果上调用方法能力的迭代器, 要么使用集合操作。
因此,记录集提供这些返回记录集本身的操作(如果可能的话):
filtered()
返回一个仅包含满足提供谓词函数的记录的记录集。谓词也可以是由true或false字段过滤的字符串:
# 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()
返回一个提供key函数进行排序的记录集。如果没有提供key函数,使用模型默认的排序方式:
# sort records by name
records.sorted(key=lambda r: r.name)
mapped()
应用提供的函数在记录集中的每一个记录上,如果结果是记录集则返回这个记录集:
# returns a list of summing two fields for each record in the set
records.mapped(lambda r: r.field1 + r.field2)
所提供的函数可以是一个获取字段值的字符串:
# returns a list of names
records.mapped('name')# returns a recordset of partners
record.mapped('partner_id')# returns the union of all partner banks, with duplicates removed
record.mapped('partner_id.bank_ids')
环境
环境(Environment)存储ORM使用的各种上下文数据: 数据库游标(用于数据库查询)、当前用户(用于访问权限检查)和当前上下文(存储任意元数据)。环境还存储缓存。
所有记录集都有一个不可变的环境,可以使用env访问它,也可以给当前用户(user
)访问,光标 (cr
) 或者上下文(context
):
>>> records.env
<Environment object ...>
>>> records.env.user
res.user(3)
>>> records.env.cr
<Cursor object ...)
当从另一个记录集创建新的记录集时,环境被继承下来。环境可以在一个其他模型上得到一个空记录集,并查询那个模型:
>>> self.env['res.partner']
res.partner
>>> self.env['res.partner'].search([['is_company', '=', True], ['customer', '=', True]])
res.partner(7, 18, 12, 14, 17, 19, 8, 31, 26, 16, 13, 20, 30, 22, 29, 15, 23, 28, 74)
改变环境
环境可以从一个记录集定制而来。这将通过使用改变的环境返回一个新版本的记录集。
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()
- 可以使用单个位置参数,替换当前环境的上下文
- 可以通过键盘获取任意数量的参数,这些参数被添加到当前环境的上下文中或步骤1中的上下文设置中
# look for partner, or create one with specified timezone if none is
# found
env['res.partner'].with_context(tz=a_tz).find_or_create(email_address)
with_env()
完全替换现有环境
通用的ORM方法
search()
接受一个搜索域,返回一个匹配记录的记录集。能够返回一个匹配记录(offset
和limit
参数)的子集合并按顺序(order
参数)返回:
>>> # searches the current model
>>> self.search([('is_company', '=', True), ('customer', '=', True)])
res.partner(7, 18, 12, 14, 17, 19, 8, 31, 26, 16, 13, 20, 30, 22, 29, 15, 23, 28, 74)
>>> self.search([('is_company', '=', True)], limit=1).name
'Agrolait'
提示
仅仅检查是否有匹配的记录的域, 或者计数匹配记录的数量,使用search_count()
create()
接受多个记录值,并返回一个包含创建的记录的记录集:
>>> self.create({'name': "New Name"})
res.partner(78)
write()
接受多个记录值,修改记录集中所有的记录。不返回任何值:
self.write({'name': "Newer Name"})
browse()
接手一个数据库id或者id的列表并返回一个记录集,当记录id是从Odoo(例如往返通过外部系统)外部获取或者当调用旧的API方法时是有用的:
>>> self.browse([7, 18, 12])
res.partner(7, 18, 12)
exists()
返回一个包含仅存在数据库中的记录的新记录集。可以用例检查记录(例如,外部获取)是否仍然存在:
if not record.exists():raise Exception("The record has been deleted")
或者在调用了删除某些记录的方法之后:
records.may_remove_some()
# only keep records which were not deleted
records = records.exists()
ref()
返回提供匹配外部id记录的环境方法:
>>> env.ref('base.group_public')
res.groups(2)
ensure_one()
检查记录集值有单个记录(仅仅包含一个记录),否则抛出错误信息:
records.ensure_one()
# is equivalent to but clearer than:
assert len(records) == 1, "Expected singleton"
创建模型
模型字段作为属性被定义在模型内:
from odoo import models, fields
class AModel(models.Model):_name = 'a.model.name'field1 = fields.Char()
警告
这意味着你不能定义相同名称的字段和方法,它们是冲突的
默认情况下,字段的标签(用户可见的名字)是字段名称的大写版本,它可以通过string参数重写:
field2 = fields.Integer(string="an other field")
对于各种字段类型和参数, 详见字段参考(在odoo10系列--ORM API 三中)
默认值被定义为字段上的参数,或者是值:
a_field = fields.Char(default="a value")
或者调用一个函数来计算默认值,它应该返回这个值:
def compute_default_value(self):return self.get_value()
a_field = fields.Char(default=compute_default_value)
计算的字段
字段可以使用compute
参数通过计算(而不是直接从数据库中读取)得到。它必须将计算值分配给字段。如果它使用了其他字段的值,则必须使用depends()
指明哪些字段:
from odoo import api
total = fields.Float(compute='_compute_total')@api.depends('value', 'tax')
def _compute_total(self):for record in self:record.total = record.value + record.value * record.tax
当使用子字段时依赖点路径:
@api.depends('line_ids.value') def _compute_total(self):for record in self:record.total = sum(line.value for line in record.line_ids)
- 计算字段默认情况下是不存储的,它们在被请求时计算并返回。设置
store=True
将在数据库中存储并启动自动搜索 搜索计算字段也需要通过设置
search
参数来启动。值是返回域的方法名:upper_name = field.Char(compute='_compute_upper', search='_search_upper')def _search_upper(self, operator, value):if operator == 'like':operator = 'ilike'return [('name', operator, value)]
允许使用inverse参数为计算字段设置值。 它是反转计算和设置相关字段的函数名称:
document = fields.Char(compute='_get_document', inverse='_set_document')def _get_document(self):for record in self:with open(record.get_document_path) as f:record.document = f.read() def _set_document(self):for record in self:if not record.document: continuewith open(record.get_document_path()) as f:f.write(record.document)
多个字段可以通过相同的方法在同一时间计算,仅仅在所有字段上使用相同的方法并设置所有字段:
discount_value = fields.Float(compute='_apply_discount') total = fields.Float(compute='_apply_discount')@depends('value', 'discount') def _apply_discount(self):for record in self:# compute actual discount from discount percentagediscount = record.value * record.discountrecord.discount_value = discountrecord.total = record.value - discount
相关字段
计算字段的一个特殊情况是相关的(代理)字段, 它提供当前记录中子字段的值。它们通过设置related参数定义并像可以存储的普通计算字段一样:
nickname = fields.Char(related='user_id.partner_id.name', store=True)
变化:即时更新的用户界面
当用户更改表单中的字段值(但尚未保存表单)时,根据该值自动更新其他字段是有用的,例如在更改税收或添加新的发票行时更新最终总金额。
- 计算字段是自动检查和重新计算,他们不需要
onchange
对于非计算字段,
onchange()
修饰符用例提供新字段值:@api.onchange('field1', 'field2') # if these fields are changed, call method def check_change(self):if self.field1 < self.field2:self.field3 = True
在方法中执行的更改随后被发送到客户端程序并对用户可见
- 计算字段和新API的变化被客户端自动调用而无需添加到视图中(改变的内容)
依靠在视图中添加on_change="0"可能一直来自特定字段的触发:
<field name="name" on_change="0"/>
当字段被用户编辑,将不会触发任何界面更新,即使有函数字段或者依赖那个字段的明确变化
注
onchange
方法将这些记录上的虚拟记录分配工作不写入数据库,只用于知道要返回到客户端的值
低水平的SQL
环境中的cr属性是当前数据库事务的游标,允许直接执行SQL,或者使用ORM难以表达的查询(例如复杂连接)或出于性能原因:
self.env.cr.execute("some_sql", param1, param2, param3)
由于模型使用相同的游标,并且环境(Environment)拥有不同的缓存,当使用原始SQL语句修改数据库时,这些缓存必须失效,否则模型的进一步使用可能变得不连贯。当在SQL中使用CREATE
, UPDATE
或 DELETE
但不是SELECT(简单读取数据库)时有必要清除缓存
清除缓存可以使用的环境(Environment)对象的invalidate_all()方法进行处理
ps:有翻译不当之处,欢迎留言指正。
原文地址:https://www.odoo.com/documentation/10.0/reference/orm.html
odoo10参考系列--ORM API 一(记录集、环境、通用方法和创建模型)相关推荐
- odoo10参考系列--ORM API 二(新旧API兼容性、模型参考和方法修饰符)
新API与旧API的兼容性 现在的Odoo是从就的(不规律的)API过渡来的,它可能需要从一个手动桥接到另一个手动桥接: RPC层(XML-RPC和RPC)是在旧的API的形式表达,表达的纯粹的方法在 ...
- odoo10参考系列--ORM API 三(字段、继承与扩展、域和更新到新API)
字段 基础字段 class odoo.fields.Field(string=<object object>, **kwargs) 字段描述符包含字段定义,并管理记录中相应字段的访问和分配 ...
- Odoo10参考系列--QWeb报表
报表是写在HTML / QWeb中,像Odoo中的所有普通视图.你可以使用普通QWeb 流程控制工具.PDF的渲染是通过wkhtmltopdf执行的. 如果要在某个模型上创建报表,则需要定义该报表和它 ...
- Odoo10参考系列--Odoo指导方针
本文介绍了新版Odoo编码指南.那些旨在提高代码的质量 (例如更好的可读性)和Odoo应用程序.实际上,适当的代码简化了维护.调试,降低了复杂性,提高了可靠性. 这些指导原则应适用于每一个新的模块和新 ...
- Odoo10参考系列--混合而有用的类
Odoo实现了一些有用的类和混合,使您可以轻松地在对象上添加常用的行为.本指南将详细介绍其中的大部分内容,包括示例和用例. 消息特征 消息集成 基本消息系统 将消息功能集成到模型中非常容易.简单地继承 ...
- odoo10参考系列--Odoo中的安全机制
除了手动管理使用自定义代码访问,Odoo提供了两种主要的数据驱动机制来管理或限制对数据的访问. 这两种机制是通过组与特定的用户实现:一个用户属于任何个数的组,和安全机制相关联的组,从而将安全机制应用与 ...
- odoo10参考系列--操作(Actions)
操作定义系统响应用户操作的行为:登录.操作按钮.发票的选择,- 操作可以存储在数据库中,也可以直接作为字典返回,例如按钮方法.所有操作共享两个强制属性: type 当前操作的类别,决定可以使用哪些字段 ...
- odoo10参考系列--测试模块
Odoo使用单元测试对测试模块进行支持. 要编写测试,只需在模块中定义一个tests子包,它将自动检查测试模.测试模块应该有一个以test_开始的名字并且应该从tests/__init__.py导入, ...
- odoo10参考系列--数据文件
Odoo就是一个非常大的数据驱动, 因此,模块定义的一大部分就是对其管理的各种记录的定义: 用户界面(菜单.视图),安全(访问权限和访问规则),报表和普通数据都通过记录定义. 结构 在Odoo定义数据 ...
最新文章
- OpenCV java 图片处理,蒙太奇图片(13)
- javascript中window.event事件用法详解
- 【渝粤教育】国家开放大学2018年春季 0105-22T酒店营销实务 参考试题
- c语言自动计算时间,C语言 · 计算时间
- antd 获取table选中行数据_element-ui 组件el-table默认选中行setCurrentRow采坑记
- python变量类型字符串的内建函数使用
- 表哥首发送书100本,感谢老铁们支持!
- 常见的一些正则表达式!
- .NET(C#)生成随机姓名,随机汉字的产生
- 左手云通讯,右手AI,容联为何能成为云联络中心“风向标”?
- 按键精灵_提取文字、数字、字母、符号的通用Function
- 02-走马灯 动画实现图片无缝展示
- 各大平台免费接口,非常适用
- (精华)2020年8月22日 ABP vNext Web应用ABP
- win10系统ping服务器,win10怎么ping ip地址_win10怎样ping本机ip地址
- C语言随机刷新,C语言 刷新缓冲区
- 三国刘备十大名言:三分天下要靠“混
- win 二进制门安装mysql_MySQL5.7 windows二进制安装教程
- Linux编程定时执行某函数
- C++ 大整数运算 高精度除法
热门文章
- win2008r2 mysql 远程_SQL SERVER 2008 R2如何开启数据库的远程连接(转)
- android 浮动文字提示,怎么在Android中实现一个自由拖动并显示文字的悬浮框
- 微型计算机中AGP指,2011江苏省计算机等级考试二级理论考试试题及答案
- pcm转换在线工具_有木有好用的CAD格式转换工具可以推荐?在线等,挺急的
- C++ new和malloc区别
- Python No Module name cv2解决方案
- html页面代码重用-document writeln
- register关键字-1
- opencv jpg作为png背景_基于OpenCV与tensorflow实现实时手势识别
- hadoop概念介绍