diff options
| author | Raphael Collet <rco@odoo.com> | 2018-07-13 14:52:35 +0200 |
|---|---|---|
| committer | Raphael Collet <rco@odoo.com> | 2018-07-24 16:58:14 +0200 |
| commit | b1e83fd7b87fa874816553c2cc99f86b3c9658bf (patch) | |
| tree | 36f76368e9d1d34ccceebaed5433828e5a9031c1 /odoo/api.py | |
| parent | 79a2da0be9df7854aec43799ae63b8272a8f1c7e (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.py | 89 |
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() |
