summaryrefslogtreecommitdiff
path: root/odoo/api.py
diff options
context:
space:
mode:
authorRaphael Collet <rco@odoo.com>2018-07-13 14:52:35 +0200
committerRaphael Collet <rco@odoo.com>2018-07-24 16:58:14 +0200
commitb1e83fd7b87fa874816553c2cc99f86b3c9658bf (patch)
tree36f76368e9d1d34ccceebaed5433828e5a9031c1 /odoo/api.py
parent79a2da0be9df7854aec43799ae63b8272a8f1c7e (diff)
[REF] models: make `create` work in batch
Add a decorator `model_create_multi` to indicate whether a `create` method is implemented to work in batch.
Diffstat (limited to 'odoo/api.py')
-rw-r--r--odoo/api.py89
1 files changed, 77 insertions, 12 deletions
diff --git a/odoo/api.py b/odoo/api.py
index 2d2bae74e1a..688b301cf02 100644
--- a/odoo/api.py
+++ b/odoo/api.py
@@ -52,7 +52,7 @@ from inspect import currentframe, getargspec
from pprint import pformat
from weakref import WeakSet
-from decorator import decorator
+from decorator import decorate, decorator
from werkzeug.local import Local, release_local
from odoo.tools import frozendict, classproperty, StackMap
@@ -316,6 +316,8 @@ def model(method):
Notice that no ``ids`` are passed to the method in the traditional style.
"""
+ if method.__name__ == 'create':
+ return model_create_single(method)
method._api = 'model'
return method
@@ -419,6 +421,50 @@ def model_cr_context(method):
return method
+_create_logger = logging.getLogger(__name__ + '.create')
+
+
+def _model_create_single(create, self, arg):
+ # 'create' expects a dict and returns a record
+ if isinstance(arg, Mapping):
+ return create(self, arg)
+ if len(arg) > 1:
+ _create_logger.debug("%s.create() called with %d dicts", self, len(arg))
+ return self.browse().concat(*(create(self, vals) for vals in arg))
+
+
+def model_create_single(method):
+ """ Decorate a method that takes a dictionary and creates a single record.
+ The method may be called with either a single dict or a list of dicts::
+
+ record = model.create(vals)
+ records = model.create([vals, ...])
+ """
+ wrapper = decorate(method, _model_create_single)
+ wrapper._api = 'model_create'
+ return wrapper
+
+
+def _model_create_multi(create, self, arg):
+ # 'create' expects a list of dicts and returns a recordset
+ if isinstance(arg, Mapping):
+ return create(self, [arg])
+ return create(self, arg)
+
+
+def model_create_multi(method):
+ """ Decorate a method that takes a list of dictionaries and creates multiple
+ records. The method may be called with either a single dict or a list of
+ dicts::
+
+ record = model.create(vals)
+ records = model.create([vals, ...])
+ """
+ wrapper = decorate(method, _model_create_multi)
+ wrapper._api = 'model_create'
+ return wrapper
+
+
def cr(method):
""" Decorate a traditional-style method that takes ``cr`` as a parameter.
Such a method may be called in both record and traditional styles, like::
@@ -665,15 +711,24 @@ def expected(decorator, func):
return decorator(func) if not hasattr(func, '_api') else func
-
-def call_kw_model(method, self, args, kwargs):
+def _call_kw_model(method, self, args, kwargs):
context, args, kwargs = split_context(method, args, kwargs)
recs = self.with_context(context or {})
_logger.debug("call %s.%s(%s)", recs, method.__name__, Params(args, kwargs))
result = method(recs, *args, **kwargs)
return downgrade(method, result, recs, args, kwargs)
-def call_kw_multi(method, self, args, kwargs):
+
+def _call_kw_model_create(method, self, args, kwargs):
+ # special case for method 'create'
+ context, args, kwargs = split_context(method, args, kwargs)
+ recs = self.with_context(context or {})
+ _logger.debug("call %s.%s(%s)", recs, method.__name__, Params(args, kwargs))
+ result = method(recs, *args, **kwargs)
+ return result.id if isinstance(args[0], Mapping) else result.ids
+
+
+def _call_kw_multi(method, self, args, kwargs):
ids, args = args[0], args[1:]
context, args, kwargs = split_context(method, args, kwargs)
recs = self.with_context(context or {}).browse(ids)
@@ -681,13 +736,17 @@ def call_kw_multi(method, self, args, kwargs):
result = method(recs, *args, **kwargs)
return downgrade(method, result, recs, args, kwargs)
+
def call_kw(model, name, args, kwargs):
""" Invoke the given method ``name`` on the recordset ``model``. """
method = getattr(type(model), name)
- if getattr(method, '_api', None) == 'model':
- return call_kw_model(method, model, args, kwargs)
+ api = getattr(method, '_api', None)
+ if api == 'model':
+ return _call_kw_model(method, model, args, kwargs)
+ elif api == 'model_create':
+ return _call_kw_model_create(method, model, args, kwargs)
else:
- return call_kw_multi(method, model, args, kwargs)
+ return _call_kw_multi(method, model, args, kwargs)
class Environment(Mapping):
@@ -860,14 +919,20 @@ class Environment(Mapping):
return self[field.model_name].browse(self._protected.get(field, ()))
@contextmanager
- def protecting(self, fields, records):
- """ Prevent the invalidation or recomputation of ``fields`` on ``records``. """
+ def protecting(self, what, records=None):
+ """ Prevent the invalidation or recomputation of fields on records.
+ The parameters are either:
+ - ``what`` a collection of fields and ``records`` a recordset, or
+ - ``what`` a collection of pairs ``(fields, records)``.
+ """
protected = self._protected
try:
protected.pushmap()
- for field in fields:
- ids = protected.get(field, frozenset())
- protected[field] = ids.union(records._ids)
+ what = what if records is None else [(what, records)]
+ for fields, records in what:
+ for field in fields:
+ ids = protected.get(field, frozenset())
+ protected[field] = ids.union(records._ids)
yield
finally:
protected.popmap()