记录集

版本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

试图在多个记录上读取或写入字段会引发错误。

访问一个关联字段 (Many2oneOne2manyMany2many) 总是返回一个记录集,如果字段没有设置则返回空集合。

危险

每次给字段赋值触发数据库更新,当在统一时间设置多个字段或设置多个记录上的字段(为相同的值),使用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. 可以使用单个位置参数,替换当前环境的上下文
  2. 可以通过键盘获取任意数量的参数,这些参数被添加到当前环境的上下文中或步骤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中使用CREATEUPDATE 或 DELETE但不是SELECT(简单读取数据库)时有必要清除缓存

清除缓存可以使用的环境(Environment)对象的invalidate_all()方法进行处理

ps:有翻译不当之处,欢迎留言指正。

原文地址:https://www.odoo.com/documentation/10.0/reference/orm.html

odoo10参考系列--ORM API 一(记录集、环境、通用方法和创建模型)相关推荐

  1. odoo10参考系列--ORM API 二(新旧API兼容性、模型参考和方法修饰符)

    新API与旧API的兼容性 现在的Odoo是从就的(不规律的)API过渡来的,它可能需要从一个手动桥接到另一个手动桥接: RPC层(XML-RPC和RPC)是在旧的API的形式表达,表达的纯粹的方法在 ...

  2. odoo10参考系列--ORM API 三(字段、继承与扩展、域和更新到新API)

    字段 基础字段 class odoo.fields.Field(string=<object object>, **kwargs) 字段描述符包含字段定义,并管理记录中相应字段的访问和分配 ...

  3. Odoo10参考系列--QWeb报表

    报表是写在HTML / QWeb中,像Odoo中的所有普通视图.你可以使用普通QWeb 流程控制工具.PDF的渲染是通过wkhtmltopdf执行的. 如果要在某个模型上创建报表,则需要定义该报表和它 ...

  4. Odoo10参考系列--Odoo指导方针

    本文介绍了新版Odoo编码指南.那些旨在提高代码的质量 (例如更好的可读性)和Odoo应用程序.实际上,适当的代码简化了维护.调试,降低了复杂性,提高了可靠性. 这些指导原则应适用于每一个新的模块和新 ...

  5. Odoo10参考系列--混合而有用的类

    Odoo实现了一些有用的类和混合,使您可以轻松地在对象上添加常用的行为.本指南将详细介绍其中的大部分内容,包括示例和用例. 消息特征 消息集成 基本消息系统 将消息功能集成到模型中非常容易.简单地继承 ...

  6. odoo10参考系列--Odoo中的安全机制

    除了手动管理使用自定义代码访问,Odoo提供了两种主要的数据驱动机制来管理或限制对数据的访问. 这两种机制是通过组与特定的用户实现:一个用户属于任何个数的组,和安全机制相关联的组,从而将安全机制应用与 ...

  7. odoo10参考系列--操作(Actions)

    操作定义系统响应用户操作的行为:登录.操作按钮.发票的选择,- 操作可以存储在数据库中,也可以直接作为字典返回,例如按钮方法.所有操作共享两个强制属性: type 当前操作的类别,决定可以使用哪些字段 ...

  8. odoo10参考系列--测试模块

    Odoo使用单元测试对测试模块进行支持. 要编写测试,只需在模块中定义一个tests子包,它将自动检查测试模.测试模块应该有一个以test_开始的名字并且应该从tests/__init__.py导入, ...

  9. odoo10参考系列--数据文件

    Odoo就是一个非常大的数据驱动, 因此,模块定义的一大部分就是对其管理的各种记录的定义: 用户界面(菜单.视图),安全(访问权限和访问规则),报表和普通数据都通过记录定义. 结构 在Odoo定义数据 ...

最新文章

  1. OpenCV java 图片处理,蒙太奇图片(13)
  2. javascript中window.event事件用法详解
  3. 【渝粤教育】国家开放大学2018年春季 0105-22T酒店营销实务 参考试题
  4. c语言自动计算时间,C语言 · 计算时间
  5. antd 获取table选中行数据_element-ui 组件el-table默认选中行setCurrentRow采坑记
  6. python变量类型字符串的内建函数使用
  7. 表哥首发送书100本,感谢老铁们支持!
  8. 常见的一些正则表达式!
  9. .NET(C#)生成随机姓名,随机汉字的产生
  10. 左手云通讯,右手AI,容联为何能成为云联络中心“风向标”?
  11. 按键精灵_提取文字、数字、字母、符号的通用Function
  12. 02-走马灯 动画实现图片无缝展示
  13. 各大平台免费接口,非常适用
  14. (精华)2020年8月22日 ABP vNext Web应用ABP
  15. win10系统ping服务器,win10怎么ping ip地址_win10怎样ping本机ip地址
  16. C语言随机刷新,C语言 刷新缓冲区
  17. 三国刘备十大名言:三分天下要靠“混
  18. win 二进制门安装mysql_MySQL5.7 windows二进制安装教程
  19. Linux编程定时执行某函数
  20. C++ 大整数运算 高精度除法

热门文章

  1. win2008r2 mysql 远程_SQL SERVER 2008 R2如何开启数据库的远程连接(转)
  2. android 浮动文字提示,怎么在Android中实现一个自由拖动并显示文字的悬浮框
  3. 微型计算机中AGP指,2011江苏省计算机等级考试二级理论考试试题及答案
  4. pcm转换在线工具_有木有好用的CAD格式转换工具可以推荐?在线等,挺急的
  5. C++ new和malloc区别
  6. Python No Module name cv2解决方案
  7. html页面代码重用-document writeln
  8. register关键字-1
  9. opencv jpg作为png背景_基于OpenCV与tensorflow实现实时手势识别
  10. hadoop概念介绍