Odoo新式api心得
Table of Contents
1 一些odoo8.0中新式api的使用心得
@api.multi和@api.one的差别在于(参见odoo/openerp/api.py两个装饰器的使用):
当在recordset(s)调用@api.one装饰的方法method时,会迭代此 recordset(s), 对应于迭代后的每个record调用方法method, 这时候方法method的self就指代这个record。 最后方法method返回的结果组装成“列表”返回(当需要使用@api.one装饰的方法返回值时这点要特别注意)
当在recordset(s)调用@api.multi装饰的方法时, 会直接在recordsets(s)上调用method, 这时候方法的self就指代这个 recordset(s), 所以在具体场景下,有可能需要迭代这个self进行处理(因为此时self为一个recordsets)。
@api.model和@api.multi很像,除了在旧式方法调用下(传入cr, uid 等的情况), @api.multi会把传入的第4个参数(self,cr,uid,ids)当成ids, 搜索数据库得到recordsets,然后调用方法,所以self即recordsets。 而@api.model不会,第4个参数会被当成普通的位置参数传入方法。
考虑到@api.one容易错误使用,odoo9.0后不建议使用。可以使用@api.mult配合self.ensureone()来确保self指代单条记录,或者遍历self来进行相关操作(对于需要返回值的情况,需要根据情况相应设计)
odoo客户端并不总是支持@api.one装饰的方法,正如官方文档的提醒:
@api.one is not always supported by the web client, e.g. on button action methods. In that case, you should use @api.multi to decorate your method, and probably call self.ensureone() in the method definition."
相关代码:
def make_wrapper(decorator, method, old_api, new_api): """ Return a wrapper method for ``method``. """ def wrapper(self, *args, **kwargs): # avoid hasattr(self, '_ids') because __getattr__() is overridden if '_ids' in self.__dict__: return new_api(self, *args, **kwargs) else: return old_api(self, *args, **kwargs) # propagate specific openerp attributes from method to wrapper for attr in WRAPPED_ATTRS: if hasattr(method, attr): setattr(wrapper, attr, getattr(method, attr)) wrapper._api = decorator wrapper._orig = method return wrapper def multi(method): """ Decorate a record-style method where ``self`` is a recordset. The method typically defines an operation on records. Such a method:: @api.multi def method(self, args): ... may be called in both record and traditional styles, like:: # recs = model.browse(cr, uid, ids, context) recs.method(args) model.method(cr, uid, ids, args, context=context) """ split = get_context_split(method) downgrade = get_downgrade(method) def old_api(self, cr, uid, ids, *args, **kwargs): context, args, kwargs = split(args, kwargs) recs = self.browse(cr, uid, ids, context) result = method(recs, *args, **kwargs) return downgrade(recs, result, *args, **kwargs) return make_wrapper(multi, method, old_api, method) def one(method): """ Decorate a record-style method where ``self`` is expected to be a singleton instance. The decorated method automatically loops on records, and makes a list with the results. In case the method is decorated with :func:`returns`, it concatenates the resulting instances. Such a method:: @api.one def method(self, args): return self.name may be called in both record and traditional styles, like:: # recs = model.browse(cr, uid, ids, context) names = recs.method(args) names = model.method(cr, uid, ids, args, context=context) .. deprecated:: 9.0 :func:`~.one` often makes the code less clear and behaves in ways developers and readers may not expect. It is strongly recommended to use :func:`~.multi` and either iterate on the ``self`` recordset or ensure that the recordset is a single record with :meth:`~openerp.models.Model.ensure_one`. """ split = get_context_split(method) downgrade = get_downgrade(method) aggregate = get_aggregate(method) def old_api(self, cr, uid, ids, *args, **kwargs): context, args, kwargs = split(args, kwargs) recs = self.browse(cr, uid, ids, context) result = new_api(recs, *args, **kwargs) return downgrade(recs, result, *args, **kwargs) def new_api(self, *args, **kwargs): result = [method(rec, *args, **kwargs) for rec in self] return aggregate(self, result) return make_wrapper(one, method, old_api, new_api) def ensure_one(self): """ Verifies that the current recorset holds a single record. Raises an exception otherwise. """ if len(self) == 1: return self raise except_orm("ValueError", "Expected singleton: %s" % self)