OdooOdoo 10 Learnodoo

odoo V10中文参考手册(一:ORM API)

2017-04-10  本文已影响5760人  XiaoHaiYang

记录集

model的数据是通过数据集合的形式来使用的,定义在model里的函数执行时它们的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()
        
    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), ...

获取有关联关系的字段(one2many,many2one,many2many)也是返回一个数据集合,如果字段为空则返回空的集合。

每个赋值语句都会触发数据库字段更新,同时更新多个字段时可使用或者更新多条记录时使用write函数

# 3 * len(records) database updates
for record in records:
    record.a = 1
    record.b = 2
    record.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})

运行环境

运行环境保存了很多ORM相关的变量:数据库查询游标、当前用户、元数据,还存有缓存。所有的model数据集都有不可改变的环境变量,可使用env来访问,如records.env.user,records.env.cr,records.env.context,运行环境还可用于为其他模型初始化一个空的集合并对该模型进行查询

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 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([])

常用ORM函数

>>> 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()函数

 >>> self.create({'name': "New Name"})
res.partner(78)
self.write({'name': "Newer Name"})
>>> 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 deleted
records = records.exists()
>>> env.ref('base.group_public')
res.groups(2)
records.ensure_one()
# 和下面的语句效果相同
assert len(records) == 1, "Expected singleton"

创建模型

模型字段就是定义的模型的属性,默认情况下字段名称就是属性名的大写,也可通过string参数指定

from odoo import models, fields
class AModel(models.Model):
    _name = 'a.model.name'

    field1 = fields.Char()
    field2 = fields.Integer(string="an other field")

可以通过default参数设置字段的默认值,默认值可以是特定的值,也可以指向一个函数

a_field = fields.Char(default="a value")

def compute_default_value(self):
    return self.get_value()
a_field = fields.Char(default=compute_default_value)
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

1.依赖的字段如果是子集里的字段,可用.表示

@api.depends('line_ids.value')
def _compute_total(self):
    for record in self:
        record.total = sum(line.value for line in record.line_ids)

2.默认情况下实时计算的字段是不保存到数据库的,可以通过store=True参数来设置保存且可以搜索
3.可以为实时计算字段设置search参数来使其可搜索,参数的值须是一个返回domain表达式的函数

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)]

4.实时计算的字段也可以通过inverse参数来赋值,通过一个反转compute的函数来设置相关的字段值

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)

5.多个字段可以同时使用同一个方法计算而来

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

6.关联字段
关联字段是实时计算字段的一个特例,它会给出子集对应的值,通过related参数来定义,就像普通字段一样可以保存到数据库
nickname = fields.Char(related='user_id.partner_id.name', store=True)

@api.onchange('field1', 'field2') # 当这两个字段值改变时调用该函数
def check_change(self):
    if self.field1 < self.field2:
        self.field3 = True

实时计算字段和onchange方法会自动在客户端调用,不需要在视图里做特别声明,但可以在视图中通过on_change="0"参数来阻止onchange函数的自动调用
<field name="name" on_change="0"/>

模型使用

odoo的模型都是从class odoo.models.Model(pool, cr)继承而来
模型的属性结构:
1._name 业务对象的名称
2._rec_name 可选的name字段名称,供osv的name_get()方法使用,默认值name
3._inherit 如果设置了__name属性,它的取值是单个或多个父级的模型名称;没有设置__name属性时,只能是单个模型名称
4._order 在搜索的时候的默认排序,默认值是id
5._auto 指定该表是否需要创建,默认值是True,如果设置成False需要重写init方法来创建表
6._table 当_auto设置成false时,该值为创建的表名;默认情况下会自动生成一个
7._inherits 定义上级模型关联使用的外键

_inherits = {
    'a.model': 'a_field_id',
    'b.model': 'b_field_id'
}

8._sql_constraints 通过一个(name, sql_definition, message)的三元组列表定义表的sql级别约束
9._parent_store 与 parent_left , parent_right 一起使用,可得到一个嵌套的集合,默认是不启用的

CRUD

(0, _, values) 为指定的value字典添加一条新记录
(1, id, values) 更新一条现有记录,条件是id为指定id且value在指定values中,不能在create方法里使用
(2, id, _) 将指定id的数据从数据集中删除并从数据库删除,不能在create里使用
(3, id, _) 将指定id的数据从数据集中删除但不从数据库删除,不能用在One2many关系及create里
(4, id, _) 将指定id的数据添加到数据集中,不能用在One2many关系上
(5, _, _) 将集合的所有数据删除,相当于当3作用于每条记录上
(6, _, ids) 使用ids列表里匹配的所有数据替换当前记录,相当于先执行5再循环执行4
domain 搜索条件的domain表达式列表 [['field_name', 'operator', 'value'], ...]
fields (list)  需要展示出来的字段列表
groupby (list) 用来分组的表达式列表,分组表达式可以是单个字段或者一个函数如:'field:groupby_function',目前函数只支持 'day', 'week', 'month', 'quarter' or 'year'并且只能作用在date和datetime字段上
offset (int) 可选参数,代表从哪个条记录开始取
limit (int)  可选参数,代表取出多少条记录
orderby (list) 可选参数,和search函数的orderby参数一样
lazy (bool) 值为true时,记录只会根据第一个groupby值进行分组,后面的groupby参数会被存在__context 中;值为false时会把所有分组条件一起执行

Searching

name (str) -- 用来匹配的name字符串
args (list) -- domain表达式列表
operator (str) --  用来匹配的操作符,如: 'like' , '='.
limit (int) -- 可选参数,最多返回的记录行数

记录集合操作

环境切换

# current context is {'key1': True}
r2 = records.with_context({}, key2=True)
# -> r2._context is {'key2': True}
r2 = records.with_context(key2=True)
# -> r2._context is {'key1': True, 'key2': True}

字段和视图查询

参数列表:
view_id 视图的id或None
view_type 当view_id参数为空时指定视图类型如form,tree等
toolbar 参数为true时将上下文动作包含在内

其他方法

内置字段

保留字段

一些字段名称是给model保留的,用来实现一些预定义的功能。当需要实现对应功能是需要对相应的保留字段进行定义

装饰器函数

模块提供了两种api形式,在传统形式中,所有参数明确地传给方法;还有一种记录行形式,提供了更加面向对象化的操作方式

#传统方式:
model = self.pool.get(MODEL)
ids = model.search(cr, uid, DOMAIN, context=context)
for rec in model.browse(cr, uid, ids, context=context):
    print rec.name
model.write(cr, uid, ids, VALUES, context=context)

#新的记录行方式
env = Environment(cr, uid, context) # cr, uid, context wrapped in env
model = env[MODEL]                  # retrieve an instance of MODEL
recs = model.search(DOMAIN)         # search returns a recordset
for rec in recs:                    # iterate over the records
    print rec.name
recs.write(VALUES)                  # update all records in recs

在传统方式下,通过某些参数自动应用了装饰方法

@api.multi
def method(self, args):
...

#传统方式下使用方式
# recs = model.browse(cr, uid, ids, context)
recs.method(args)

model.method(cr, uid, ids, args, context=context)
@api.model
def method(self, args):
    ...

#传统方式
# recs = model.browse(cr, uid, ids, context)
recs.method(args)

model.method(cr, uid, args, context=context)
name = fields.Char(compute='_compute_pname')

@api.one
@api.depends('partner_id.name', 'partner_id.is_company')
def _compute_pname(self):
    if self.partner_id.is_company:
        self.pname = (self.partner_id.name or "").upper()
    else:
        self.pname = self.partner_id.name
@api.one
@api.constrains('name', 'description')
def _check_description(self):
    if self.name == self.description:
        raise ValidationError("Fields name and description must be different")

在检验失败时抛出ValidationError错误,且不支持关联字段检验

@api.onchange('partner_id')
def _onchange_partner(self):
    self.message = "Dear %s" % (self.partner_id.name or "")

该函数可能会返回 以数据字典形式组装的当前更改字段的domain表达式和一个警告消息,不支持关联字段处理

return {
    'domain': {'other_id': [('partner_id', '=', partner_id)]},
    'warning': {'title': "Warning", 'message': "What is this?"},
}

参数列表
model 模型名称,self代表当前模型
downgrade 一个将value值从记录形式转化为传统形式的方法:downgrade(self, value, *args, **kwargs)
upgrade 一个将value从传统形式转化为记录形式的方法:upgrade(self, value, *args, **kwargs)

self,args,*kwargs是在传统形式下需要传的参数

该装饰器将函数的输出变成api形式:传统形式下返回id/ids/false,记录形式下返回记录集合

@model
@returns('res.partner')
def find_partner(self, arg):
    ...     # return some record

# output depends on call style: traditional vs record style
partner_id = model.find_partner(cr, uid, arg, context=context)

# recs = model.browse(cr, uid, ids, context)
partner_record = recs.find_partner(arg)
@api.v8
def foo(self):
...

@api.v7
def foo(self, cr, uid, ids, context=None):
    ...

字段

基本字段

参数列表:
string(string) -- 用户能看到的字段的标签
help(string) -- 用户能看到的关于该字段的提示
readonly(boolean) -- 字段是否设置为只读,默认为False
required(boolean) -- 字段是否为必须,默认False
index(boolean) -- 字段是否作为索引保存在数据库中,默认False
default -- 字段的默认值,可以是一个特定的值或者一个有返回值的函数,可使用default=None来忽略字段的default设置
states -- 用数据字典封装视图里的属性-值对,如'readonly', 'required', 'invisible'
groups -- 用逗号分隔的xml id列表,可以限制用户对字段的访问
copy(boolean) -- 指定当数据行被复制时该字段是否被复制,默认是True,实时计算字段和one2many字段默认为False
oldname(string) -- 之前的字段名称,在做数据迁移的时候orm可以自动进行重命名

可定义一个字段,它的值通过指定函数实时计算得来,定义实时计算字段只需要指定compute属性即可,它有以下几种参数:

compute -- 用于计算的函数名称
inverse -- 逆向计算函数的函数名,可选
search -- 实现该字段search方法的函数名
store -- 是否在数据库存储该字段值,默认False
compute_sudo -- 是否需要使用超级管理员对该字段进行重新计算

例:

upper = fields.Char(compute='_compute_upper',
                    inverse='_inverse_upper',
                    search='_search_upper')

@api.depends('name')
def _compute_upper(self):
    for rec in self:
        rec.upper = rec.name.upper() if rec.name else False

def _inverse_upper(self):
    for rec in self:
        rec.name = rec.upper.lower() if rec.upper else False

def _search_upper(self, operator, value):
    if operator == 'like':
        operator = 'ilike'
    return [('name', operator, value)]

实时计算的方法必须用 odoo.api.depends()方法装饰以决定其依赖于哪些字段,且同一个计算方法可用于多个计算字段,只需要在方法里将对应的字段赋值就行,search方法的返回值须是一个与条件对应的domain表达式,在对模型执行搜索处理domain表达式时调用

例:第二个子类只为state字段添加提示

class First(models.Model):
    _name = 'foo'
    state = fields.Selection([...], required=True)

class Second(models.Model):
    _inherit = 'foo'
    state = fields.Selection(help="Blah blah blah")

常用字段

参数
size (int) -- 值的最大长度
translate -- 启用字段的翻译

参数
selection -- 指定该字段的取值列表,为(value,string)列表或一个模型的方法或方法名
selection_add -- 当该字段来自重定义时,它提供selection参数的扩展,为(value,string)列表

1.static context_today(record, timestamp=None)
返回客户端时区的当前日期,可以接收一个datetime格式的参数
2.static from_string(value) 将ORM的值转换为date的值
3.static to_string(value) 将date格式的值转换为ORM的值
4.static today(*args) 以ORM值的格式返回当前日期

关系模型字段

参数列表:
comodel_name(string) -- 目标模型名称,除非是关联字段否则该参数必选
domain -- 可选,用于在客户端筛选数据的domain表达式
context -- 可选,用于在客户端处理时使用
ondelete -- 当所引用的数据被删除时采取的操作,取值:'set null', 'restrict', 'cascade'
auto_join -- 在搜索该字段时是否自动生成JOIN条件,默认False
delegate -- 设置为True时可以通过当前model访问目标model的字段,与_inherits功能相同

参数列表:
comodel_name -- 目标模型名称,
inverse_name -- 在comodel_name 中对应的Many2one字段
domain -- 可选,用于在客户端筛选数据的domain表达式
context -- 可选,用于在客户端处理时使用
auto_join -- 在搜索该字段时是否自动生成JOIN条件,默认False
limit(integer) -- 可选,在读取时限制数量

注:除非是关联字段,否则comodel_name和inverse_name是必选参数

参数:
comodel_name -- 目标模型名称,除非是关联字段否则该参数必选
relation -- 可选,关联的model在数据库存储的表名,默认采用comodel_name获取数据
column1 -- 可选,与relation表记录相关联的列名
column2 -- 可选,与relation表记录相关联的列名
domain -- 可选,用于在客户端筛选数据的domain表达式
context -- 可选,用于在客户端处理时使用
limit(integer) -- 可选,在读取时限制数量

继承和扩展

odoo有三种模块化的模型继承机制:

传统继承

当_inherit和_name属性一起使用时,odoo基于原有模型创建一个新模型,新的模型会自动继承原模型的字段、方法等

class Inheritance0(models.Model):
    _name = 'inheritance.0'

    name = fields.Char()

    def call(self):
        return self.check("model 0")

    def check(self, s):
        return "This is {} record {}".format(s, self.name)

class Inheritance1(models.Model):
    _name = 'inheritance.1'
    _inherit = 'inheritance.0'

    def call(self):
        return self.check("model 1")

扩展

当只使用_inherit属性时,新的模型会替代已存在的模型,当需要给模型添加字段、方法、重置属性时比较有用

class Extension0(models.Model):
    _name = 'extension.0'

    name = fields.Char(default="A")

class Extension1(models.Model):
    _inherit = 'extension.0'

    description = fields.Char(default="Extended")

代理

代理模式使用_inherits属性来指定一个模型当找不到指定字段时直接去对应的子模型查找

class Child0(models.Model):
    _name = 'delegation.child0'

    field_0 = fields.Integer()

class Child1(models.Model):
    _name = 'delegation.child1'

    field_1 = fields.Integer()

class Delegating(models.Model):
    _name = 'delegation.parent'

    _inherits = {
        'delegation.child0': 'child0_id',
        'delegation.child1': 'child1_id',
    }

    child0_id = fields.Many2one('delegation.child0', required=True, ondelete='cascade')
    child1_id = fields.Many2one('delegation.child1', required=True, ondelete='cascade')
    
#use
env = self.env
record = env['delegation.parent'].create({
    'child0_id': env['delegation.child0'].create({'field_0': 0}).id,

Domain表达式

domain表达式是由多个(field_name, operator, value)元组构成的列表或数组

field_name -- 字段名或者用.号分隔的Many2one关系的字段如:'street' , 'partner_id.country'
operator(str) -- 用于对字段值和给定值进行比较的运算符:

=,!=,>,>=,<,<=,
=?(值为false或none时返回true,否则与=效果一致)
=like()将字段数据与value进行匹配,_代表匹配单个字符、%匹配0或多个字符
like() 将字段数据与%value% 进行匹配,
not like 不与%value%匹配
ilike 忽略大小写的like函数
not ilike 忽略大小写的not like
=ilike 忽略大小写的=like
in 与value的任意值相等,value须为值列表
not in 与value的任意值都不相等
child_of 是否由value派生而来

value 对应值,必须与相应条件对应

多个domain表达式可用运算符进行连接,运算符写在两个表达式之前。

& 逻辑与 ,| 逻辑或,!逻辑非

#例:
[('name','=','ABC'),
 ('language.code','!=','en_US'),
 '|',('country_id.code','=','be'),
     ('country_id.code','=','de')]

Porting from the old API to the new API

这个基本不需要用


译自odoo官方文档:http://www.odoo.com/documentation/10.0/reference/orm.html,不当之处欢迎批评指正。


内容发布自http://www.jianshu.com/u/6fdae8ec06bc,转载请注明出处

上一篇下一篇

猜你喜欢

热点阅读