Skip to content

Commit 5a0538a

Browse files
committed
[IMP] orm: iter_browse.create() accept generator or query as values
Done to be able to create millions of records memory-efficiently.
1 parent fafb5a8 commit 5a0538a

File tree

1 file changed

+30
-6
lines changed

1 file changed

+30
-6
lines changed

src/util/orm.py

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,19 @@ def get_ids():
444444

445445
self._ids = get_ids()
446446

447+
def _values_query(self, query):
448+
cr = self._model.env.cr
449+
cr.execute(format_query(cr, "WITH query AS ({}) SELECT count(*) FROM query", SQLStr(query)))
450+
size = cr.fetchone()[0]
451+
452+
def get_values():
453+
with named_cursor(cr, itersize=self._chunk_size) as ncr:
454+
ncr.execute(SQLStr(query))
455+
for row in ncr.iterdict():
456+
yield row
457+
458+
return size, get_values()
459+
447460
def _browse(self, ids):
448461
next(self._end(), None)
449462
args = self._cr_uid + (list(ids),)
@@ -494,35 +507,46 @@ def caller(*args, **kwargs):
494507
self._it = None
495508
return caller
496509

497-
def create(self, values, **kw):
510+
def create(self, values=None, query=None, **kw):
498511
"""
499512
Create records.
500513
501514
An alternative to the default `create` method of the ORM that is safe to use to
502515
create millions of records.
503516
504-
:param list(dict) values: list of values of the records to create
517+
:param iterable(dict) values: iterable of values of the records to create
518+
:param int size: the no. of elements produced by values, required if values is a generator
519+
:param str query: alternative to values, SQL query that can produce them
505520
:param bool multi: whether to use the multi version of `create`, by default is
506521
`True` from Odoo 12 and above
507522
"""
508523
multi = kw.pop("multi", version_gte("saas~11.5"))
524+
size = kw.pop("size", None)
509525
if kw:
510526
raise TypeError("Unknown arguments: %s" % ", ".join(kw))
511527

512-
if not values:
513-
raise ValueError("`create` cannot be called with an empty `values` argument")
528+
if not bool(values) ^ bool(query):
529+
raise ValueError("`create` needs to be called using exactly one of `values` or `query` arguments")
514530

515531
if self._size:
516532
raise ValueError("`create` can only called on empty `browse_record` objects.")
517533

518-
ids = []
519-
size = len(values)
534+
if query:
535+
size, values = self._values_query(query)
536+
537+
if size is None:
538+
try:
539+
size = len(values)
540+
except TypeError:
541+
raise ValueError("When passing a generator of values, the size kwarg is mandatory")
542+
520543
it = chunks(values, self._chunk_size, fmt=list)
521544
if self._logger:
522545
sz = (size + self._chunk_size - 1) // self._chunk_size
523546
qualifier = "env[%r].create([:%d])" % (self._model._name, self._chunk_size)
524547
it = log_progress(it, self._logger, qualifier=qualifier, size=sz)
525548

549+
ids = []
526550
self._patch = no_selection_cache_validation()
527551
for sub_values in it:
528552
self._patch.start()

0 commit comments

Comments
 (0)