本文共 8722 字,大约阅读时间需要 29 分钟。
版本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 # database self.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.nameExample Name>>> record.company_id.nameCompany Name>>> record.name = "Bob">>> field = "name">>> record[field]Bob
试图在多个记录上读取或写入字段会引发错误。
访问一个关联字段 (, , ) 总是返回一个记录集,如果字段没有设置则返回空集合。
危险
每次给字段赋值触发数据库更新,当在统一时间设置多个字段或设置多个记录上的字段(为相同的值),使用:
# 3 * len(records) database updatesfor record in records: record.a = 1 record.b = 2 record.c = 3# len(records) database updatesfor record in records: record.write({'a': 1, 'b': 2, 'c': 3})# 1 database updaterecords.write({'a': 1, 'b': 2, 'c': 3})
Odoo支持记录字段的缓存,因此不是每个字段访问都进行数据库请求,这对性能来说是可怕的。下面请求数据库查询的列子仅发生在第一条语句中:
record.name # first access reads value from databaserecord.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 partners countries.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工具(, , , ...)可提供转换只用,可是这些返回结果要么是一个list
,要么是一个除去在它们结果上调用方法能力的迭代器, 要么使用集合操作。
因此,记录集提供这些返回记录集本身的操作(如果可能的话):
返回一个仅包含满足提供谓词函数的记录的记录集。谓词也可以是由true或false字段过滤的字符串:
# only keep records whose company is the current user'srecords.filtered(lambda r: r.company_id == user.company_id)# only keep records whose partner is a companyrecords.filtered("partner_id.is_company")
返回一个提供key函数进行排序的记录集。如果没有提供key函数,使用模型默认的排序方式:
# sort records by namerecords.sorted(key=lambda r: r.name)
应用提供的函数在记录集中的每一个记录上,如果结果是记录集则返回这个记录集:
# returns a list of summing two fields for each record in the setrecords.mapped(lambda r: r.field1 + r.field2)
所提供的函数可以是一个获取字段值的字符串:
# returns a list of namesrecords.mapped('name')# returns a recordset of partnersrecord.mapped('partner_id')# returns the union of all partner banks, with duplicates removedrecord.mapped('partner_id.bank_ids')
环境(Environment)存储ORM使用的各种上下文数据: 数据库游标(用于数据库查询)、当前用户(用于访问权限检查)和当前上下文(存储任意元数据)。环境还存储缓存。
所有记录集都有一个不可变的环境,可以使用env访问它,也可以给当前用户(user
)访问,光标 (cr
) 或者上下文(context
):
>>> records.env>>> records.env.userres.user(3)>>> records.env.cr
当从另一个记录集创建新的记录集时,环境被继承下来。环境可以在一个其他模型上得到一个空记录集,并查询那个模型:
>>> 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)
环境可以从一个记录集定制而来。这将通过使用改变的环境返回一个新版本的记录集。
使用用户提供的设置创建一个新环境,如果没有提供使用系统管理员设置(绕过权限/规则的安全上下文),使用新的环境调用返回一个记录集副本:
# create partner object as administratorenv['res.partner'].sudo().create({'name': "A Partner"})# list partners visible by the "public" userpublic = env.ref('base.public_user')env['res.partner'].sudo(public).search([])
# look for partner, or create one with specified timezone if none is# foundenv['res.partner'].with_context(tz=a_tz).find_or_create(email_address)
完全替换现有环境
接受一个搜索域,返回一个匹配记录的记录集。能够返回一个匹配记录(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'
提示
仅仅检查是否有匹配的记录的域, 或者计数匹配记录的数量,使用
接受多个记录值,并返回一个包含创建的记录的记录集:
>>> self.create({'name': "New Name"})res.partner(78)
接受多个记录值,修改记录集中所有的记录。不返回任何值:
self.write({'name': "Newer Name"})
接手一个数据库id或者id的列表并返回一个记录集,当记录id是从Odoo(例如往返通过外部系统)外部获取或者当调用旧的API方法时是有用的:
>>> self.browse([7, 18, 12])res.partner(7, 18, 12)
返回一个包含仅存在数据库中的记录的新记录集。可以用例检查记录(例如,外部获取)是否仍然存在:
if not record.exists(): raise Exception("The record has been deleted")
或者在调用了删除某些记录的方法之后:
records.may_remove_some()# only keep records which were not deletedrecords = records.exists()
ref()
返回提供匹配外部id记录的环境方法:
>>> env.ref('base.group_public')res.groups(2)
检查记录集值有单个记录(仅仅包含一个记录),否则抛出错误信息:
records.ensure_one()# is equivalent to but clearer than:assert len(records) == 1, "Expected singleton"
模型字段作为属性被定义在模型内:
from odoo import models, fieldsclass 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
参数通过计算(而不是直接从数据库中读取)得到。它必须将计算值分配给字段。如果它使用了其他字段的值,则必须使用指明哪些字段:
from odoo import apitotal = 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: continue with 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 percentage discount = record.value * record.discount record.discount_value = discount record.total = record.value - discount
相关字段
计算字段的一个特殊情况是相关的(代理)字段, 它提供当前记录中子字段的值。它们通过设置related参数定义并像可以存储的普通计算字段一样:
nickname = fields.Char(related='user_id.partner_id.name', store=True)
当用户更改表单中的字段值(但尚未保存表单)时,根据该值自动更新其他字段是有用的,例如在更改税收或添加新的发票行时更新最终总金额。
onchange
对于非计算字段, 修饰符用例提供新字段值:
@api.onchange('field1', 'field2') # if these fields are changed, call methoddef check_change(self): if self.field1 < self.field2: self.field3 = True
在方法中执行的更改随后被发送到客户端程序并对用户可见
依靠在视图中添加on_change="0"可能一直来自特定字段的触发:
当字段被用户编辑,将不会触发任何界面更新,即使有函数字段或者依赖那个字段的明确变化
注
onchange
方法将这些记录上的虚拟记录分配工作不写入数据库,只用于知道要返回到客户端的值
环境中的cr属性是当前数据库事务的游标,允许直接执行SQL,或者使用ORM难以表达的查询(例如复杂连接)或出于性能原因:
self.env.cr.execute("some_sql", param1, param2, param3)
由于模型使用相同的游标,并且环境(Environment)拥有不同的缓存,当使用原始SQL语句修改数据库时,这些缓存必须失效,否则模型的进一步使用可能变得不连贯。当在SQL中使用CREATE
, UPDATE
或 DELETE
但不是SELECT(简单读取数据库)时有必要清除缓存
清除缓存可以使用的环境(Environment)对象的invalidate_all()方法进行处理
ps:有翻译不当之处,欢迎留言指正。
原文地址:
转载地址:http://vrwhj.baihongyu.com/