第五章 API

Odoo的发展历史伴随着Python2到Python3的发展, 因此也不可避免了留下了历史的痕迹. 从最初的(cr,uid,ids,context) 到 api.one, api.mulit, api.model, 再到如今只剩5种装饰方法, API的历史演变体现了Python2到Python3的进程, 本章我们将了解Odoo中API的前世今生.

8.0之前的API

当年那是Odoo还叫OpenERP的时代,OpenObject对象的方法通常都需要带着几个固定的参数:cr,uid,ids,context等等,写起来很繁琐,比如下面的例子:

def btn_import(self,cr,uid,ids,context=None):
    ...

到了8.0,可能Odoo官方的开发人员也觉得这样写起来太繁琐了,于是乎,他们引入了新API,封装在api.py文件中,主要有一下几种类型:

  • model
  • multi
  • one
  • constrains
  • depends
  • onchange
  • returns

而从13.0开始, 官方再次对API动刀, 这次仅保留了5种装饰方法:

  • model
  • constrains
  • depends
  • onchange
  • returns

下面我们就这5种类型进行详细的介绍, 关于one和multi的介绍 我们放到附录中, 仅供需要维护旧版本Odoo的同学参考.

model

作用:api.model装饰的方法, 其self只代表模型, 不含有具体的记录值.

典型应用是对象的create方法:

@api.model
def create(self,vals):
    return super(Book,self).create(vals)

参数vals可以是一个字典, 也可以是一个字典组成的列表 . Odoo将会根据vals的类型返回单一或者多个记录集.*

create方法不会获取页面中标注为readonly的字段的值, 如果想要保存只读字段的值,请将其force_save属性设置为True。

contrains

作用:给某些字段添加限制条件, 如果是多个字段, 用逗号将其分开. 触发条件: 单声明的字段值发生变化时触发

constrains只接受简单的字段名称, 像对象属性obj.x形式的参数将会被忽略. 另外, 使用constrains限制的字段必须出现在视图中(如果没有出现,可以使用重载create的方法强制实现).

在我们book_store模块中, 我们可以给book对象设置一个名称限制, 限制其名称长度必须在10个字符以内,否则提示错误.

@api.constrains("name")
def _check_name(self):
    """检查名称长度"""
    if len(self.name) > 10:
        raise ValidationError("图书名称必须限制在10个字符以内")

当输入错误时,便会提示:

constrains的参数也可以是一个函数(当参数是函数时,只能接受一个参数), 该函数返回要限制的字段列表.

def _get_constrains_fields(self):
        return ['name', 'date']

@api.constrains(_get_constrains_fields)
def _check_name(self):
    """检查名称长度"""
    if len(self.name) > 10:
        raise ValidationError("图书名称必须限制在10个字符以内")
    if self.date < date(2000,1,1):
        raise ValidationError("只能添加2000年以后的图书")

另外一种constrains

odoo支持另外一种添加限制的方式,即通过sql约束的方式。方法是在odoo类对象中添加_sql_constraints属性,值是一个包含了元组的列表,元组的三个值分别是约束名,约束条件和警告信息,看一个例子:

_sql_constraints = [
    ('name_description_check',
     'CHECK(name != description)',
     "The title of the course should not be the description"),
    ('name_unique',
     'UNIQUE(name)',
     "The course title must be unique"),
]

这样做的好处是,在数据库层面就限制了数据的校验,而不是在代码层面的校验,显然效率会更高。缺点是在添加限制之前,数据库中不能存在违反约束的数据,否则约束会添加失败。

depends

depends主要用于compute方法,v8当中已经取消了function字段,对于任何fields都可以通过添加compute属性动态赋值。depends就是用来标该方法依赖于哪些字段的装饰。

@api.depends('date')
def _get_book_age(self):
    self.age = (datetime.now().date() - self.date).days

depends装饰器的参数可以是多个以逗号分割的字段, 也可以是一个返回字段列表的函数. depends装饰器不可用于id字段.

对于compute方法来说,加不加depends装饰的区别在于,加了depends的方法会在依赖的字段发生改变时重新计算本字段的值,而不加depends的方法只在触发的第一次调用,也就是说不会持续更新。

returns

returns的用法主要是用来指定返回值的格式,它接受三个参数,第一个为返回值的model,第二个为向下兼容的method,第三个为向上兼容的method。

第一个参数如果是对象本身,则写'self',如果是其他对象,则写其他对象名如:@api.returns('ir.ui.view')。

例如:

@api.multi    
@api.returns('mail.message', lambda value: value.id)
def message_post(self, **kwargs):
    if self.env.context.get('mark_so_as_sent'):
        self.filtered(lambda o: o.state == 'draft').with_context(tracking_disable=True).write({'state': 'sent'})
        self.env.user.company_id.set_onboarding_step_done('sale_onboarding_sample_quotation_state')
    return super(SaleOrder, self.with_context(mail_post_autofollow=True)).message_post(**kwargs)

returns主要用于确保新旧API返回值的一致,并不常用。

onchange

onchange的适用场景是当某个字段发生变化时被调用,用来处理需要动态联动的字段。

onchange的参数是发生变化的字段名,例如:

@api.onchange('amount', 'unit_price')
def _onchange_price(self):
   self.price = self.amount * self.unit_price
   return {
       'warning': {
           'title': "Something bad happened",
           'message': "It was very bad indeed",
       }
   }

onchange 可以有返回值可以没有返回值。返回值由一个字典组成,可选的值有 value和warning,value用来返回需要设置的字段值,warning用来返回一些警告信息。

@api.onchange('field_name')
def onchange_field_name(self):
    value = self.field_name if self.field_name else 'Nothing'
    return {'value': {'field_name': value}}

CUID方法介绍

前面介绍了API的多种装饰器及其作用,下面介绍odoo ORM框架的标准CUID方法。所谓CURD即Create\Update\Read\Delete等操作的简称。

Browse

在odoo中如果我们想要读取某个对象的信息,最常用的方法是browse方法,browse方法接收一个参数ID,然后返回该对象对应的对象。

order = self.env['sale.order'].browse(1)
print(order.name)

获取到对象以后,使用点号+属性名的方式即可获取该对象所有属性或者调用对应的方法。

Create

如果我们想要创建一个对象,那么我们就需要使用create方法。create方法接收一个参数vals,其值是包含需要创建的对象的各个字段的值。

order = self.env['sale.order'].create({
    "name":"SO0001",
    "user_id":1
})

Write

write方法给我们提供了一个修改对象值的方法,接收一个参数vals,其值是包含需要修改字段的值。

order = self.env['sale.order'].browse(1)
order.write({
    "name":"SO002"
})

如果只是修改其中的一个字段的值,那么可以直接使用赋值的方法:

order = self.env['sale.order'].browse(1)
order.name = "SO0002"

两种方法的作用是等效的,第二种方法跟面向对象一些,第一种方法的优势在于可以在一行代码中提交多个字段的修改。

odoo ORM的删除并没使用delete作为删除方法,而是使用了unlink关键字。

order = self.env['sale.order'].browse(1)
order.unlink()

这里只对CUID的各个方法做一个简单的示例, 关于其内部更多的详情,参见第五部分的模型一章.

results matching ""

    No results matching ""