ORM API(二)

创建模型

在模型上字段作为属性被定义

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

    field1 = fields.Char()

警告
意味着字段和方法不能有相同的名字,否则会冲突

默认情况下,显示给用户的是首字母大写的字段名,但是可以被参数"string"覆盖

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

字段的默认值通过参数定义,如下

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

或者调用一个函数,获取函数的返回值

a_field = fields.Char(default=compute_default_value)
def compute_default_value(self):
    return self.get_value()
计算字段

字段的值可以通过compute参数来计算,代替直接从数据库中获取。必须将计算的值赋给字段,如果需要用到其它字段时,应该用depends()修饰符

from openerp 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"可以存储到数据库中并且启用自动搜索

搜索参数也可以被计算来启用计算字段上的搜索,这个方法返回一个domains

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字段

计算字段的特殊字段是related字段,它提供当前记录上的子字段的值。它们是通过设置相关参数来定义的,像常规的计算字段一样,它们可以存储:

nickname = fields.Char(related='user_id.partner_id.name', store=True)

onchange:动态更新UI

当用户更新了字段的值(但并没有保存),它能够自动的更新其它字段的值

  • 计算字段能够自动的检查和重新计算,不需要onchange
  • 对于非计算字段,onchang()修饰符可以提供新的字段值
	# 如果这些字段值发生改变,调用check_change方法
	@api.onchange('field1', 'field2') 
	def check_change(self):
	    if self.field1 < self.field2:
	        self.field3 = True

方法执行过程中的更改对用户可见

  • 客户端自动调用计算字段和onchange()修饰符,不需要在视图中添加
  • 可以在视图中添加on_change="0"在特定字段中抑制触发器。
   <field name="name" on_change="0"/>

当用户编辑时,即使字段拥有onchange属性,也不会触发更新。

Note
onchange只是对记录上的值进行计算,然后返回客户端,并不会保存到数据库中

Low-level SQL

环境上的cr属性是当前数据库事务的指针,允许直接执行SQL,对于用ORM难以表示的查询或者性能原因,则可以使用sql:

self.env.cr.execute("some_sql", param1, param2, param3)

由于模型使用相同的游标,并且环境中保存着各种缓存,所以当使用sql修改数据库时,这些缓存必须失效,否则模型的进一步使用可能会变得不连贯。在sql中使用创建、更新或者删除时,有必要清楚缓存,而不是SELECT(它只是读取数据库)

新旧API之间的兼容

odoo正在从一个老的(不太常规的)API过渡,需要手动连接到另一个API:

  • RPC层(XML-RPC和JSON-RPC)是用旧API表示的,纯在新API中表示的方法在RPC上不可用
  • 可重写的方法可以从仍然使用旧API样式编写的旧代码片段中调用

新旧API之间最大的区别是:

  • 环境的值(游标、用户id和上下文)被显式地传递给方法
  • 记录数据(ids)显式地传递给方法,可能根本就不传递
  • 记录数据(ids)显式地传递给方法,可能根本就不传递

默认情况下,假定方法使用新的API样式,并且不能从旧的API样式调用

Tip
从新API到旧API的调用被桥接
当使用新的API样式时,对使用旧API定义的方法的调用将自动动态转换,不需要做任何特别的事情

	>>> # 旧API定义的方法
	>>> def old_method(self, cr, uid, ids, context=None):
	...    print ids

	>>> # 新API定义的方法
	>>> def new_method(self):
	...     # 系统自动推断如何调用旧的样式
	...     self.old_method()

	>>> env[model].browse([1, 2, 3, 4]).new_method()
	[1, 2, 3, 4]

两个decorator可以向旧API公开一个新风格的方法:

model()
该方法公开为不使用ids,其记录集通常为空。它的“旧API”签名是cr、uid、*arguments、context:

@api.model
def some_method(self, a_value):
    pass
# 等于
old_style_model.some_method(cr, uid, a_value, context=context)

multi()
该方法公开为一个id列表(可能为空),其“旧API”签名为cr、uid、ids、*arguments、context:

@api.multi
def some_method(self, a_value):
    pass
# 等于
old_style_model.some_method(cr, uid, [id1, id2], a_value, context=context)

因为新风格的api倾向于返回记录集,而旧风格的api倾向于返回id列表,所以还有一个装饰器来管理这一点:

returns()
假设函数返回一个记录集,第一个参数应该是记录集的模型或self(对于当前模型)的名称。
如果以新的API样式调用该方法,但从旧的API样式调用时将记录集转换为id列表,则不会产生任何影响:

	>>> @api.multi
	... @api.returns('self')
	... def some_method(self):
	...     return self
	>>> new_style_model = env['a.model'].browse(1, 2, 3)
	>>> new_style_model.some_method()
	a.model(1, 2, 3)
	>>> old_style_model = pool['a.model']
	>>> old_style_model.some_method(cr, uid, [1, 2, 3], context=context)
	[1, 2, 3]

Model Reference

class openerp.models.Model(pool, cr)

常规数据库的主要超类——持久化OpenERP模型。
OpenERP模型是从这个类继承而来的:

class user(Model):
    ...

系统稍后将在每个数据库(安装了类模块的数据库)上实例化该类一次。

结构属性

_name
业务对象名称,用点符号表示(在模块名称空间中)

_rec_name
用作名称的替代字段,由osv的name_get()(默认:‘name’)使用

_inherit

  • 如果设置了_name,则继承该模型以_name命名,并在其上进行扩展
  • 如果不设置_name,则继承该模型,在原模型上进行扩展

_order
在没有指定排序的情况下进行搜索时的排序字段(默认值:‘id’)

_auto
是否应该创建数据库表(默认:True)如果设置为False,则重写init()以创建数据库表

_table
是否应该创建数据库表(默认:True)如果设置为False,则重写init()以创建数据库表

_inherits
字典将父业务对象的_name映射到要使用的相应外键字段的名称

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

实现基于复合的继承:新模型公开了_inherits-ed模型的所有字段,但没有存储任何字段:值本身仍然存储在链接的记录上。

_constraints
定义python约束的列表(constraint_function, message, fields),字段列表是指示性的。

_sql_constraints
在生成备份表时,定义SQL约束以执行的元组(name,sql_definition,message)

_parent_store
在parent_left和parent_right旁边,设置一个嵌套集,以支持对当前模型记录进行快速分层查询(默认:False)

CRUD

create(vals)->record
在模型中创建一个新的记录
新记录使用vals中和default_get()中的值初始化

Parameters: vals(dict) –
模型字段的值,如字典

{'field_name': field_value, ...}

Returns:新创建的记录
Raises: AccessError –

  • 如果用户对请求的对象没有创建权限
  • 如果用户试图绕过在请求对象上创建的访问规则
  • ValidateError——如果用户试图为未选择的字段输入无效值
  • UserError——如果在对象的层次结构中创建了一个循环,则是操作的结果(例如将对象设置为其父对象)

browse([ids]) → records
返回当前环境中作为参数提供的id的记录集。
值可以为空,单个id或者一个列表

unlink()
删除当前集合的记录

Raises: AccessError –

  • 如果用户在请求的对象上没有unlink权限
  • 如果用户试图绕过请求对象上取消链接的访问规则
  • UserError——如果记录是其他记录的默认属性

write(vals)
使用提供的值更新当前集合中的所有记录。

Parameters: vals(dict) –
要更新的字段和要设置的值,例如:

{'foo': 1, 'bar': "Qux"}

将字段foo设为1,字段bar设为“Qux”(否则会触发错误)
Raises: AccessError –

  • 如果用户对请求的对象没有写权限
  • 如果用户试图绕过对请求对象进行写操作的访问规则
  • ValidateError——如果用户试图为未选择的字段输入无效值
  • UserError——如果在对象的层次结构中创建了一个循环,则是操作的结果(例>- 如将对象设置为其父对象)
  • 对于数值字段(整数、浮点数),值应该是相应的类型
  • 对于布尔值,值应该是bool
  • 对于选择,该值应该匹配选择值(通常为str,有时为int)
  • 对于Many2one,该值应该是要设置的记录的数据库标识符
  • 其它非关系字段使用字符串作为值

Danger
由于历史和兼容性的原因,日期和日期时间字段使用字符串作为值(写入和读> 取),而不是日期或日期时间。这些日期字符串是utc格式的,并且根据openerp.tools.misc.DEFAULT_SERVER_DATE_FORMAT和openerp.tools.misc.DEFAULT_SERVER_DATETIME_FORMAT进行格式化。

  • One2many和Many2many使用一种特殊的“命令”格式来操作存储在/与字段关联的记录集。

这种格式是顺序执行的三元组列表,其中每个三元组是要在记录集上执行的命令。并不是所有命令都适用于所有情况。可能的命令是:

(0, _, values)
添加从提供的值字典创建的新记录。

(1, id, values)
使用值中的值更新现有的id id记录。无法在create()中使用。

(2, id, _)
从集合中删除id id记录,然后(从数据库中)删除它。无法在create()中使用。

(3, id, _)
从集合中删除id id记录,但不删除它。不能在One2many上使用。无法在create()中使用。

(4, id, _)
将现有的id id记录添加到集合中。不能在One2many上使用。

(5, _, _)
从集合中删除所有记录,相当于显式地对每个记录使用命令3。不能在One2many上使用。无法在create()中使用。

(6, _, ids)
替换ids列表中设置的所有现有记录,相当于对ids中的每个id使用命令5和命令4。不能在One2many上使用。

Note
上面列表中标记为_的值被忽略,可以是任何值,通常为0或False。

read([fields])
在self、low-level/RPC方法中为记录读取请求字段。在Python代码中,首选browse()。

Parameters: fields – 要返回的字段名称列表(默认为所有字段)
Returns: 将字段名称映射到其值的字典列表,每个记录有一个字典
Raises: AccessError – 如果用户对某些给定记录没有读权限

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值