Odoo中文网|Odoo实施培训

 找回密码
 立即注册
搜索
热搜: Odoo OpenERP 实施
查看: 7458|回复: 0
打印 上一主题 下一主题

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

[复制链接]

119

主题

119

帖子

566

积分

高级会员

Rank: 4

积分
566
跳转到指定楼层
楼主
发表于 2017-7-24 23:16:19 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 店小2 于 2017-7-24 23:33 编辑

记录集model的数据是通过数据集合的形式来使用的,定义在model里的函数执行时它们的self变量也是一个数据集合

  1. class AModel(models.Model):
  2.     _name = 'a.model'

  3.     def a_method(self):
  4.         # self can be anywhere between 0 records and all records in the database
  5.         self.do_operation()

  6.     def do_operation(self):
  7.     print self # => a.model(1, 2, 3, 4, 5)
  8.     for record in self:
  9.         print record # => a.model(1), then a.model(2), then a.model(3), ...

复制代码

获取有关联关系的字段(one2many,many2one,many2many)也是返回一个数据集合,如果字段为空则返回空的集合。
每个赋值语句都会触发数据库字段更新,同时更新多个字段时可使用或者更新多条记录时使用write函数
  1. # 3 * len(records) database updates
  2. for record in records:
  3.     record.a = 1
  4.     record.b = 2
  5.     record.c = 3

  6. # len(records) database updates
  7. for record in records:
  8.     record.write({'a': 1, 'b': 2, 'c': 3})

  9. # 1 database update
  10. records.write({'a': 1, 'b': 2, 'c': 3})
复制代码

  • 数据缓存和预读取
    odoo会为记录保留一份缓存,它有一种内置的预读取机制,通过缓存来提升性能。
  • 集合运算符
    • record in set返回record是否在set中,record须为单条记录,record not in set反之
    • set1 <= set2 返回set1是否为set2的子集
    • set1 >= set2 返回set2是否为set1的子集
    • set1 | set2 返回set1和set2的并集
    • set1 & set2 返回set1和set2的交集
    • set1 - set2 返回在集合set1中但不在set2中的记录
  • 其他集合运算
    • filtered() 返回满足条件的数据集,如
      1. # only keep records whose company is the current user's
      2. records.filtered(lambda r: r.company_id == user.company_id)

      3. # only keep records whose partner is a company
      4. records.filtered("partner_id.is_company")

      复制代码

    • sorted() 返回根据提供的键排序之后的结果# sort records by namerecords.sorted(key=lambda r: r.name)
    • mapped() 返回应用了指定函数之后的结果集
      1. #returns a list of summing two fields for each record in the set
      2. records.mapped(lambda r: r.field1 + r.field2)

      3. #函数也可以是字符串 对应记录的字段
      4. # returns a list of names
      5. records.mapped('name')

      6. # returns a recordset of partners
      7. record.mapped('partner_id')

      复制代码

    • 运行环境
    • 运行环境保存了很多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)
  • 更改运行环境
    可以基于一个运行环境量自定义,以得到拥有新运行环境的数据集
    • sudo() 使用现有数据集创建一个新运行环境,得到一个基于新运行环境的数据集的拷贝
# 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([])
  • with_context()
    一个参数时可用于替换当前运行环境的context,多个参数时通过keyword添加到当前运行环境context或单参数时设置的context
  • with_env() 完整替换当前运行环境
常用ORM函数
  • search() 接收domain表达式参数,返回符合条件的数据集合,可以通过limit,offset参数返回一个子集,还可通过order参数对数据排序
>>> 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来查找,返回符合条件的数据集合>>> 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 deletedrecords = records.exists()
  • ref() 运行环境函数根据提供的external id返回对应的数据记录>>> env.ref('base.group_public')res.groups(2)
  • ensure_one() 检验某数据集是否只包含单条数据,如果不是则报错records.ensure_one()# 和下面的语句效果相同assert len(records) == 1, "Expected singleton"
创建模型
模型字段就是定义的模型的属性,默认情况下字段名称就是属性名的大写,也可通过string参数指定
from odoo import models, fieldsclass 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)
  • 实时计算的字段
    字段也可以是通过实时计算得来的,指定compute参数,并且当用到其他字段时需要使用depends声明
  1. from odoo import api
  2. total = fields.Float(compute='_compute_total')

  3. @api.depends('value', 'tax')
  4. def _compute_total(self):
  5.     for record in self:
  6.         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的函数来设置相关的字段值
  1. document = fields.Char(compute='_get_document', inverse='_set_document')

  2. def _get_document(self):
  3.     for record in self:
  4.         with open(record.get_document_path) as f:
  5.             record.document = f.read()
  6. def _set_document(self):
  7.     for record in self:
  8.         if not record.document: continue
  9.         with open(record.get_document_path()) as f:
  10.             f.write(record.document)
复制代码


5.多个字段可以同时使用同一个方法计算而来
  1. discount_value = fields.Float(compute='_apply_discount')
  2. total = fields.Float(compute='_apply_discount')

  3. @depends('value', 'discount')
  4. def _apply_discount(self):
  5.     for record in self:
  6.         # compute actual discount from discount percentage
  7.         discount = record.value * record.discount
  8.         record.discount_value = discount
  9.         record.total = record.value - discount

复制代码


6.关联字段
关联字段是实时计算字段的一个特例,它会给出子集对应的值,通过related参数来定义,就像普通字段一样可以保存到数据库
nickname = fields.Char(related='user_id.partner_id.name', store=True)
  • onchange:实时更新用户界面
    当用户在表单中更改某个字段的值时,其他相关字段可以在不需保存的情况下实时更新
    @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"/>
  • 低级SQL
    运行环境的cr属性指向当前数据库查询的游标,在需要进行ORM没有提供的复杂查询或性能优化时,可以通过它直接执行查询
    self.env.cr.execute("some_sql", param1, param2, param3)
    但由于运行环境变量里同时存有大量缓存,所以在进行create/update,delete操作后需要使用invalidate_all()方法清除缓存: self.env.invalidate_all(),select方法是不需要更新缓存的,因为没有对数据进行更改
模型使用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
  • create(vals)
    使用传递的vals参数为模型生成一条新记录,参数示例:{'field_name': field_value, ...},该方法返回该新创建的记录
  • browse([ids])
    返回满足条件的记录集合,参数可以为空或单个id或一系列id
  • unlink() 删除当前集合的数据
  • write(vals) 使用提供的数据更新当前集合里的所有记录,参数示例:{'foo': 1, 'bar': "Qux"}
    • int或float型字段,给定的值须为对应的整型或浮点型
    • 布尔型字段,对应的值须为bool型
    • Selection字段,给定的值须符合条件
    • Many2one字段,给定的值须与对应的数据库记录相符
    • 其他无关联关系的字段使用字符串作值
    • One2many和Many2many字段通过一个特殊的格式命令来操纵对应字段值,通过一系列三元组按顺序来对数据进行操作,下面是一些常用的:(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
  • read([fields]) 从self里读取指定的字段,专供rpc使用
  • read_group(domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True) 得到一个通过groupby参数分组后的记录的列表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
  • search(args[, offset=0][, limit=None][, order=None][, count=False])
    根据args参数里的domain表达式来搜索所有记录,参数列表:
    1.args domain表达式,为空时返回所有记录
    2.offset (int) 从第几条记录开始取
    3.limit (int) 返回记录行数的最大值
    4.order (str)  排序的字段
    5.count (bool) 当值为True的时候只返回匹配记录的条数
  • search_count(args)
    返回根据给定domain表达式参数查询所得到的记录条数
  • name_search(name='', args=None, operator='ilike', limit=100)
    返回根据name条件来查询,并满足args指定的domain表达式的记录集合
    name (str) -- 用来匹配的name字符串args (list) -- domain表达式列表operator (str) --  用来匹配的操作符,如: 'like' , '='.limit (int) -- 可选参数,最多返回的记录行数
记录集合操作
  • ids 得到当前记录集合的id列表
  • ensure_one() 验证一个记录集合是否只包含一条记录
  • exists() 返回当前记录集中真正存在的子集,并把缓存中未删除的部分做标记,可用于判断if record.exists():
  • filtered(func) 返回满足func参数内条件的记录集合,参数可以是一个函数或者用.分隔的字段列表
  • sorted(key=None, reverse=False) 返回按key排序之后的记录集,key参数可以是一个返回单个key的函数或字段名称或为空,reverse参数为True时即为倒序
  • mapped(func) 将func函数应用到所有记录上,并返回记录列表或集合
环境切换
  • sudo([user=SUPERUSER])  返回通过指定用户得到的新记录集,默认会返回SUPERUSER的记录集(前提是权限没有问题)
  • with_context([context][, **overrides])
    返回当前记录集在扩展环境下的新记录集,扩展环境可以由指定环境和overrides参数合并而成、或由当前环境和overrides参数合并而成# 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}
  • with_env(env) 返回在指定环境下的新版记录集合
字段和视图查询
  • fields_get([fields][, attributes])
    以数据字典的形式返回字段的定义,通过继承得来的字段也会在其中,string/help/selection属性会自动被翻译
    • fields参数是字段列表、为空或不传返回所有字段
    • attributes 可指定字段的属性、为空或不传时返回全部的
  • fields_view_get([view_id | view_type='form'])
    返回指定视图的具体组成如:字段,模型,视图结构
    参数列表:
    view_id 视图的id或None
    view_type 当view_id参数为空时指定视图类型如form,tree等
    toolbar  参数为true时将上下文动作包含在内

其他方法
  • default_get(fields) 获取指定字段的默认值
  • name_get() 以列表形式返回每条记录的描述,默认是display_name字段
  • name_create(name) 相当于调用create方法创建一条新记录而只设置一个display_name
内置字段
  • id 数据识别字段
  • _log_access 决定记log的字段(created_date,write_uid..)是否创建,默认值True
  • create_date 记录创建的时间
  • create_uid 创建人的id,关联到res.users
  • write_date 记录最近的修改时间
  • write_uid 最近修改记录的用户id,关联到res.users
保留字段一些字段名称是给model保留的,用来实现一些预定义的功能。当需要实现对应功能是需要对相应的保留字段进行定义
  • name(Char)  -- _rec_name的默认值,在需要用来展示的时候使用
  • active(Boolean) -- 设置记录的全局可见性,当值为False时通过search和list是获取不到的
  • sequence(Integer) -- 可修改的排序,可以在列表视图里通过拖拽进行排序
  • state(Selection) -- 对象的生命周期阶段,通过fileds的states属性使用
  • parent_id(Many2one) -- 用来对树形结构的记录排序,并激活domain表达式的child_of运算符
  • parent_left,parent_right -- 与 _parent_store结合使用,提供更好的树形结构数据读取
装饰器函数
模块提供了两种api形式,在传统形式中,所有参数明确地传给方法;还有一种记录行形式,提供了更加面向对象化的操作方式


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

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

复制代码


在传统方式下,通过某些参数自动应用了装饰方法
  • odoo.api.multi(method)
    在记录行方式下装饰一个对记录进行操作的方法
    1. @api.multi
    2. def method(self, args):
    3. ...

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

    7. model.method(cr, uid, ids, args, c
    复制代码

  • odoo.api.model(method)
    在记录行方式下装饰一个内容不明确、但模型明确的方法
  1. @api.model
  2. def method(self, args):
  3.     ...

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

  7. model.method(cr, uid, args, context=context)
复制代码

  • odoo.api.depends(*args)
    返回为compute方法指定依赖字段的装饰器,每个参数必须是字符串
  1. name = fields.Char(compute='_compute_pname')

  2. @api.one
  3. @api.depends('partner_id.name', 'partner_id.is_company')
  4. def _compute_pname(self):
  5.     if self.partner_id.is_company:
  6.         self.pname = (self.partner_id.name or "").upper()
  7.     else:
  8.         self.pname = self.partner_id.name
复制代码

  • odoo.api.constrains(*args)
    装饰一个约束检查方法,每个参数必须是需要检查的字段
@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错误,且不支持关联字段检验
  • odoo.api.onchange(*args)
    返回一个监控指定字段的onchange方法的装饰器,每个参数必须是字段名称
    @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?"},}
  • odoo.api.returns(model, downgrade=None, upgrade=None)
    返回一个获取model实例的方法的装饰器
    参数列表
    model 模型名称,self代表当前模型
    downgrade  一个将value值从记录形式转化为传统形式的方法:downgrade(self, value, args, *kwargs)
    upgrade 一个将value从传统形式转化为记录形式的方法:upgrade(self, value, args, *kwargs)

self,args,*kwargs是在传统形式下需要传的参数
该装饰器将函数的输出变成api形式:传统形式下返回id/ids/false,记录形式下返回记录集合
  1. @model
  2. @returns('res.partner')
  3. def find_partner(self, arg):
  4.     ...     # return some record

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

  7. # recs = model.browse(cr, uid, ids, context)
  8. partner_record = recs.find_partner(arg)
复制代码

  • odoo.api.one(method)
    装饰一个需要将self作为单例模式使用的记录形式方法,该方法自动对记录进行循环并将结果组织成列表,当记录行被returns方法装饰过时,该方法会将对应的实例组织起来,从9.0版本开始不用了
  • odoo.api.v7(method_v7) 用于装饰支持老版api的方法
  • odoo.api.v8(method_v8) 用于装饰支持新版api的方法
@api.v8def foo(self):...@api.v7def foo(self, cr, uid, ids, context=None):    ...






回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|技术支持|开发手册|Odoo中文网-远鼎旗下odoo培训网站 ( 苏ICP备15039516号 )

GMT+8, 2024-4-25 13:36 , Processed in 0.019032 second(s), 10 queries , Xcache On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表