[IMP] core: better API for flush() and invalidate()
This provides a new API for those operations, in order to make the
distinction between the use cases more explicit. The former API was
using obscure parameter combinations to correspond to various cases.
In the summary below, `fnames` is an iterable of field names. If the
parameter is not given, it means "all fields" in the given context.
Note that method recompute() is now mostly private, as it should not be
used in business code.
# process pending computations and updates
records.env.flush_all() # all fields of all models
records.flush_model(fnames) # the fields of all records of the model
records.flush_recordset(fnames) # the fields of the given records
# process pending computations, became non-public methods
records.env._recompute_all() # all fields of all models
records._recompute_model(fnames) # the fields of all records of the model
records._recompute_recordset(fnames) # the fields of the given records
# invalidate the cache of fields
records.env.invalidate_all() # all fields of all models
records.invalidate_model(fnames) # the fields of all records of the model
records.invalidate_recordset(fnames) # the fields of the given records
Part-of: odoo/odoo#87527
This commit is contained in:
+18
-3
@@ -459,7 +459,7 @@ def call_kw(model, name, args, kwargs):
|
||||
result = _call_kw_model_create(method, model, args, kwargs)
|
||||
else:
|
||||
result = _call_kw_multi(method, model, args, kwargs)
|
||||
model.flush()
|
||||
model.env.flush_all()
|
||||
return result
|
||||
|
||||
|
||||
@@ -701,6 +701,21 @@ class Environment(Mapping):
|
||||
)
|
||||
return self.cr.savepoint()
|
||||
|
||||
def invalidate_all(self):
|
||||
""" Invalidate the cache of all records. """
|
||||
self.cache.invalidate()
|
||||
|
||||
def _recompute_all(self):
|
||||
""" Process all pending computations. """
|
||||
for field in list(self.fields_to_compute()):
|
||||
self[field.model_name]._recompute_field(field)
|
||||
|
||||
def flush_all(self):
|
||||
""" Flush all pending computations and updates to the database. """
|
||||
self._recompute_all()
|
||||
for model_name in list(self.all.towrite):
|
||||
self[model_name].flush_model()
|
||||
|
||||
def is_protected(self, field, record):
|
||||
""" Return whether `record` is protected against invalidation or
|
||||
recomputation for `field`.
|
||||
@@ -834,7 +849,7 @@ class Transaction:
|
||||
if env.uid is not None:
|
||||
break
|
||||
if env_to_flush is not None:
|
||||
env_to_flush['base'].flush()
|
||||
env_to_flush.flush_all()
|
||||
|
||||
def clear(self):
|
||||
""" Clear the caches and pending computations and updates in the translations. """
|
||||
@@ -983,7 +998,7 @@ class Cache(object):
|
||||
def check(self, env):
|
||||
""" Check the consistency of the cache for the given environment. """
|
||||
# flush fields to be recomputed before evaluating the cache
|
||||
env['res.partner'].recompute()
|
||||
env._recompute_all()
|
||||
|
||||
# make a copy of the cache, and invalidate it
|
||||
dump = dict(self._data)
|
||||
|
||||
@@ -80,7 +80,7 @@ class Populate(Command):
|
||||
|
||||
# force the flush to make sure population time still
|
||||
# considers flushing all values to database
|
||||
env['base'].flush()
|
||||
env.flush_all()
|
||||
|
||||
if commit:
|
||||
env.cr.commit()
|
||||
|
||||
+2
-2
@@ -1024,7 +1024,7 @@ class Field(MetaField('DummyField', (object,), {})):
|
||||
@model.pool.post_init
|
||||
def add_not_null():
|
||||
# flush values before adding NOT NULL constraint
|
||||
model.flush([self.name])
|
||||
model.flush_model([self.name])
|
||||
model.pool.post_constraint(apply_required, model, self.name)
|
||||
|
||||
elif not self.required and has_notnull:
|
||||
@@ -3472,7 +3472,7 @@ class One2many(_RelationalMulti):
|
||||
# cache value of self is consistent
|
||||
inverse_field = records.pool[self.comodel_name]._fields[self.inverse_name]
|
||||
if inverse_field.compute:
|
||||
records.env[self.comodel_name].recompute([self.inverse_name])
|
||||
records.env[self.comodel_name]._recompute_model([self.inverse_name])
|
||||
return super().__get__(records, owner)
|
||||
|
||||
def read(self, records):
|
||||
|
||||
+195
-102
@@ -908,7 +908,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
)
|
||||
finally:
|
||||
psycopg2.extensions.set_wait_callback(callback)
|
||||
self.env['ir.model.data'].invalidate_cache(fnames=fields)
|
||||
self.env['ir.model.data'].invalidate_model(fields)
|
||||
|
||||
return (
|
||||
(record, to_xid(record.id))
|
||||
@@ -935,7 +935,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
sub = rs[idx:idx+1000]
|
||||
for rec in sub:
|
||||
yield rec
|
||||
rs.invalidate_cache(ids=sub.ids)
|
||||
sub.invalidate_recordset()
|
||||
if not _is_toplevel_call:
|
||||
splittor = lambda rs: rs
|
||||
|
||||
@@ -1060,7 +1060,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
:type data: list(list(str))
|
||||
:returns: {ids: list(int)|False, messages: [Message][, lastrow: int]}
|
||||
"""
|
||||
self.flush()
|
||||
self.env.flush_all()
|
||||
|
||||
# determine values of mode, current_module and noupdate
|
||||
mode = self._context.get('mode', 'init')
|
||||
@@ -1171,15 +1171,15 @@ class BaseModel(metaclass=MetaModel):
|
||||
|
||||
# make 'flush' available to the methods below, in the case where XMLID
|
||||
# resolution fails, for instance
|
||||
flush_self = self.with_context(import_flush=flush, import_cache=LRU(1024))
|
||||
flush_recordset = self.with_context(import_flush=flush, import_cache=LRU(1024))
|
||||
|
||||
# TODO: break load's API instead of smuggling via context?
|
||||
limit = self._context.get('_import_limit')
|
||||
if limit is None:
|
||||
limit = float('inf')
|
||||
extracted = flush_self._extract_records(fields, data, log=messages.append, limit=limit)
|
||||
extracted = flush_recordset._extract_records(fields, data, log=messages.append, limit=limit)
|
||||
|
||||
converted = flush_self._convert_records(extracted, log=messages.append)
|
||||
converted = flush_recordset._convert_records(extracted, log=messages.append)
|
||||
|
||||
info = {'rows': {'to': -1}}
|
||||
for id, xid, record, info in converted:
|
||||
@@ -2837,7 +2837,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
WHERE row.id = comp.id
|
||||
""".format(table=self._table, parent=self._parent_name)
|
||||
self.env.cr.execute(query)
|
||||
self.invalidate_cache(['parent_path'])
|
||||
self.invalidate_model(['parent_path'])
|
||||
return True
|
||||
|
||||
def _check_removed_columns(self, log=False):
|
||||
@@ -3419,7 +3419,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
|
||||
# if a read() follows a write(), we must flush updates, as read() will
|
||||
# fetch from database and overwrites the cache (`test_update_with_id`)
|
||||
self.flush(fields, self)
|
||||
self.flush_recordset(fields)
|
||||
|
||||
field_names = []
|
||||
inherited_field_names = []
|
||||
@@ -3761,7 +3761,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
|
||||
# mark fields that depend on 'self' to recompute them after 'self' has
|
||||
# been deleted (like updating a sum of lines after deleting one line)
|
||||
self.flush()
|
||||
self.env.flush_all()
|
||||
self.modified(self._fields, before=True)
|
||||
|
||||
with self.env.norecompute():
|
||||
@@ -3819,14 +3819,14 @@ class BaseModel(metaclass=MetaModel):
|
||||
|
||||
# invalidate the *whole* cache, since the orm does not handle all
|
||||
# changes made in the database, like cascading delete!
|
||||
self.invalidate_cache()
|
||||
self.env.invalidate_all()
|
||||
if ir_model_data_unlink:
|
||||
ir_model_data_unlink.unlink()
|
||||
if ir_attachment_unlink:
|
||||
ir_attachment_unlink.unlink()
|
||||
# DLE P93: flush after the unlink, for recompute fields depending on
|
||||
# the modified of the unlink
|
||||
self.flush()
|
||||
self.env.flush_all()
|
||||
|
||||
# auditing: deletions are infrequent and leave no trace in the database
|
||||
_unlink.info('User #%s deleted %s records with IDs: %r', self._uid, self._name, self.ids)
|
||||
@@ -3943,7 +3943,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
for field in protected
|
||||
if field.compute and field.name not in vals]
|
||||
if to_compute:
|
||||
self.recompute(to_compute, self)
|
||||
self._recompute_recordset(to_compute)
|
||||
|
||||
# protect fields being written against recomputation
|
||||
with env.protecting(protected, self):
|
||||
@@ -3992,7 +3992,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
self.modified(vals)
|
||||
|
||||
if self._parent_store and self._parent_name in vals:
|
||||
self.flush([self._parent_name])
|
||||
self.flush_model([self._parent_name])
|
||||
|
||||
# validate non-inversed fields first
|
||||
inverse_fields = [f.name for fs in determine_inverses.values() for f in fs]
|
||||
@@ -4840,7 +4840,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
models.append(self.env[model_name])
|
||||
|
||||
for model_name, field_names in to_flush.items():
|
||||
self.env[model_name].flush(field_names)
|
||||
self.env[model_name].flush_model(field_names)
|
||||
|
||||
@api.model
|
||||
def _search(self, domain, offset=0, limit=None, order=None, count=False, access_rights_uid=None):
|
||||
@@ -5067,7 +5067,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
|
||||
# must ignore 'active' flag, ir.rules, etc. => direct SQL query
|
||||
cr = self._cr
|
||||
self.flush([parent])
|
||||
self.flush_model([parent])
|
||||
query = 'SELECT "%s" FROM "%s" WHERE id = %%s' % (parent, self._table)
|
||||
for id in self.ids:
|
||||
current_id = id
|
||||
@@ -5093,7 +5093,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
# field must be a many2many on itself
|
||||
raise ValueError('invalid field_name: %r' % (field_name,))
|
||||
|
||||
self.flush([field_name])
|
||||
self.flush_model([field_name])
|
||||
|
||||
cr = self._cr
|
||||
query = 'SELECT "%s", "%s" FROM "%s" WHERE "%s" IN %%s AND "%s" IS NOT NULL' % \
|
||||
@@ -5775,63 +5775,92 @@ class BaseModel(metaclass=MetaModel):
|
||||
:param Model records: if given (together with ``fnames``), limit the
|
||||
processing to the given records.
|
||||
"""
|
||||
warnings.warn(
|
||||
"Deprecated method flush(), use flush_model(), flush_recordset() or env.flush_all() instead",
|
||||
DeprecationWarning,
|
||||
)
|
||||
if fnames is None:
|
||||
self.env.flush_all()
|
||||
elif records is None:
|
||||
self.flush_model(fnames)
|
||||
else:
|
||||
records.flush_recordset(fnames)
|
||||
|
||||
def flush_model(self, fnames=None):
|
||||
""" Process the pending computations and database updates on ``self``'s
|
||||
model. When the parameter is given, the method guarantees that at least
|
||||
the given fields are flushed to the database. More fields can be
|
||||
flushed, though.
|
||||
|
||||
:param fnames: optional iterable of field names to flush
|
||||
"""
|
||||
self._recompute_model(fnames)
|
||||
self._flush(fnames)
|
||||
|
||||
def flush_recordset(self, fnames=None):
|
||||
""" Process the pending computations and database updates on the records
|
||||
``self``. When the parameter is given, the method guarantees that at
|
||||
least the given fields on records ``self`` are flushed to the database.
|
||||
More fields and records can be flushed, though.
|
||||
|
||||
:param fnames: optional iterable of field names to flush
|
||||
"""
|
||||
self._recompute_recordset(fnames)
|
||||
towrite = self.env.all.towrite.get(self._name)
|
||||
if fnames is None:
|
||||
must_flush = towrite and any(id_ in towrite for id_ in self._ids)
|
||||
else:
|
||||
must_flush = towrite and any(
|
||||
fname in vals
|
||||
for id_ in self._ids
|
||||
for vals in towrite.get(id_, ())
|
||||
for fname in fnames
|
||||
)
|
||||
if must_flush:
|
||||
self._flush(fnames)
|
||||
|
||||
def _flush(self, fnames=None):
|
||||
def process(model, id_vals):
|
||||
# group record ids by vals, to update in batch when possible
|
||||
updates = defaultdict(list)
|
||||
for rid, vals in id_vals.items():
|
||||
updates[frozendict(vals)].append(rid)
|
||||
for id_, vals in id_vals.items():
|
||||
updates[frozendict(vals)].append(id_)
|
||||
|
||||
for vals, ids in updates.items():
|
||||
model.browse(ids)._write(vals)
|
||||
|
||||
# DLE P76: test_onchange_one2many_with_domain_on_related_field
|
||||
# ```
|
||||
# email.important = True
|
||||
# self.assertIn(email, discussion.important_emails)
|
||||
# ```
|
||||
# When a search on a field coming from a related occurs (the domain
|
||||
# on discussion.important_emails field), make sure the related field
|
||||
# is flushed
|
||||
if fnames is None:
|
||||
# flush everything
|
||||
self.recompute()
|
||||
while self.env.all.towrite:
|
||||
model_name, id_vals = self.env.all.towrite.popitem()
|
||||
process(self.env[model_name], id_vals)
|
||||
fields = self._fields.values()
|
||||
else:
|
||||
# flush self's model if any of the fields must be flushed
|
||||
self.recompute(fnames, records=records)
|
||||
fields = [self._fields[fname] for fname in fnames]
|
||||
|
||||
# check whether any of 'records' must be flushed
|
||||
if records is not None:
|
||||
fnames = set(fnames)
|
||||
towrite = self.env.all.towrite.get(self._name)
|
||||
if not towrite or all(
|
||||
fnames.isdisjoint(towrite.get(record.id, ()))
|
||||
for record in records
|
||||
):
|
||||
return
|
||||
model_fields = defaultdict(list)
|
||||
for field in fields:
|
||||
model_fields[field.model_name].append(field)
|
||||
if field.related_field:
|
||||
model_fields[field.related_field.model_name].append(field.related_field)
|
||||
|
||||
# DLE P76: test_onchange_one2many_with_domain_on_related_field
|
||||
# ```
|
||||
# email.important = True
|
||||
# self.assertIn(email, discussion.important_emails)
|
||||
# ```
|
||||
# When a search on a field coming from a related occurs (the domain
|
||||
# on discussion.important_emails field), make sure the related field
|
||||
# is flushed
|
||||
model_fields = {}
|
||||
for fname in fnames:
|
||||
field = self._fields[fname]
|
||||
model_fields.setdefault(field.model_name, []).append(field)
|
||||
if field.related_field:
|
||||
model_fields.setdefault(field.related_field.model_name, []).append(field.related_field)
|
||||
for model_name, fields in model_fields.items():
|
||||
if any(
|
||||
field.name in vals
|
||||
for vals in self.env.all.towrite.get(model_name, {}).values()
|
||||
for field in fields
|
||||
):
|
||||
id_vals = self.env.all.towrite.pop(model_name)
|
||||
process(self.env[model_name], id_vals)
|
||||
for model_name, fields_ in model_fields.items():
|
||||
if any(
|
||||
field.name in vals
|
||||
for vals in self.env.all.towrite.get(model_name, {}).values()
|
||||
for field in fields_
|
||||
):
|
||||
id_vals = self.env.all.towrite.pop(model_name)
|
||||
process(self.env[model_name], id_vals)
|
||||
|
||||
# missing for one2many fields, flush their inverse
|
||||
for fname in fnames:
|
||||
field = self._fields[fname]
|
||||
if field.type == 'one2many' and field.inverse_name:
|
||||
self.env[field.comodel_name].flush([field.inverse_name])
|
||||
# flush the inverse of one2many fields, too
|
||||
for field in fields:
|
||||
if field.type == 'one2many' and field.inverse_name:
|
||||
self.env[field.comodel_name].flush_model([field.inverse_name])
|
||||
|
||||
#
|
||||
# New records - represent records that do not exist in the database yet;
|
||||
@@ -6099,7 +6128,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
"""
|
||||
warnings.warn('refresh() is deprecated method, use invalidate_cache() instead',
|
||||
DeprecationWarning)
|
||||
self.invalidate_cache()
|
||||
self.env.invalidate_all()
|
||||
|
||||
@api.model
|
||||
def invalidate_cache(self, fnames=None, ids=None):
|
||||
@@ -6109,16 +6138,45 @@ class BaseModel(metaclass=MetaModel):
|
||||
:param fnames: the list of modified fields, or ``None`` for all fields
|
||||
:param ids: the list of modified record ids, or ``None`` for all
|
||||
"""
|
||||
if fnames is None:
|
||||
if ids is None:
|
||||
return self.env.cache.invalidate()
|
||||
fields = list(self._fields.values())
|
||||
warnings.warn(
|
||||
"Deprecated method invalidate_cache(), use invalidate_model(), invalidate_recordset() or env.invalidate_all() instead",
|
||||
DeprecationWarning,
|
||||
)
|
||||
if ids is not None:
|
||||
self.browse(ids).invalidate_recordset(fnames)
|
||||
elif fnames is not None:
|
||||
self.invalidate_model(fnames)
|
||||
else:
|
||||
fields = [self._fields[n] for n in fnames]
|
||||
self.env.invalidate_all()
|
||||
|
||||
# invalidate fields and inverse fields, too
|
||||
spec = [(f, ids) for f in fields] + \
|
||||
[(invf, None) for f in fields for invf in self.pool.field_inverses[f]]
|
||||
def invalidate_model(self, fnames=None):
|
||||
""" Invalidate the cache of all records of ``self``'s model, when the
|
||||
cached values no longer correspond to the database values. If the
|
||||
parameter is given, only the given fields are invalidated from cache.
|
||||
|
||||
:param fnames: optional iterable of field names to invalidate
|
||||
"""
|
||||
self._invalidate_cache(fnames)
|
||||
|
||||
def invalidate_recordset(self, fnames=None):
|
||||
""" Invalidate the cache of the records in ``self``, when the cached
|
||||
values no longer correspond to the database values. If the parameter
|
||||
is given, only the given fields on ``self`` are invalidated from cache.
|
||||
|
||||
:param fnames: optional iterable of field names to invalidate
|
||||
"""
|
||||
self._invalidate_cache(fnames, self._ids)
|
||||
|
||||
def _invalidate_cache(self, fnames=None, ids=None):
|
||||
if fnames is None:
|
||||
fields = self._fields.values()
|
||||
else:
|
||||
fields = [self._fields[fname] for fname in fnames]
|
||||
spec = []
|
||||
for field in fields:
|
||||
spec.append((field, ids))
|
||||
for invf in self.pool.field_inverses[field]:
|
||||
spec.append((invf, None))
|
||||
self.env.cache.invalidate(spec)
|
||||
|
||||
def modified(self, fnames, create=False, before=False):
|
||||
@@ -6260,38 +6318,73 @@ class BaseModel(metaclass=MetaModel):
|
||||
The fields and records to recompute have been determined by method
|
||||
:meth:`modified`.
|
||||
"""
|
||||
def process(field):
|
||||
recs = self.env.records_to_compute(field)
|
||||
if (not recs) or (records is not None and not (records & recs)):
|
||||
return
|
||||
if field.store:
|
||||
# do not force recomputation on new records; those will be
|
||||
# recomputed by accessing the field on the records
|
||||
recs = recs.filtered('id')
|
||||
try:
|
||||
field.recompute(recs)
|
||||
except MissingError:
|
||||
existing = recs.exists()
|
||||
field.recompute(existing)
|
||||
# mark the field as computed on missing records, otherwise
|
||||
# they remain forever in the todo list, and lead to an
|
||||
# infinite loop...
|
||||
for f in recs.pool.field_computed[field]:
|
||||
self.env.remove_to_compute(f, recs - existing)
|
||||
else:
|
||||
self.env.cache.invalidate([(field, recs._ids)])
|
||||
self.env.remove_to_compute(field, recs)
|
||||
|
||||
warnings.warn(
|
||||
"Deprecated method recompute(), use flush_model(), flush_recordset() or env.flush_all() instead",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
if fnames is None:
|
||||
# recompute everything
|
||||
for field in list(self.env.fields_to_compute()):
|
||||
process(field)
|
||||
self.env._recompute()
|
||||
elif records is None:
|
||||
self._recompute_model(fnames)
|
||||
else:
|
||||
# recompute the given fields on self's model
|
||||
for fname in fnames:
|
||||
field = self._fields[fname]
|
||||
if field.compute:
|
||||
process(field)
|
||||
records._recompute_recordset(fnames)
|
||||
|
||||
def _recompute_model(self, fnames=None):
|
||||
""" Process the pending computations of the fields of ``self``'s model.
|
||||
|
||||
:param fnames: optional iterable of field names to compute
|
||||
"""
|
||||
if fnames is None:
|
||||
fields = self._fields.values()
|
||||
else:
|
||||
fields = [self._fields[fname] for fname in fnames]
|
||||
|
||||
for field in fields:
|
||||
if field.compute:
|
||||
self._recompute_field(field)
|
||||
|
||||
def _recompute_recordset(self, fnames=None):
|
||||
""" Process the pending computations of the fields of the records in ``self``.
|
||||
|
||||
:param fnames: optional iterable of field names to compute
|
||||
"""
|
||||
if fnames is None:
|
||||
fields = self._fields.values()
|
||||
else:
|
||||
fields = [self._fields[fname] for fname in fnames]
|
||||
|
||||
for field in fields:
|
||||
if field.compute:
|
||||
self._recompute_field(field, self._ids)
|
||||
|
||||
def _recompute_field(self, field, ids=None):
|
||||
ids_to_compute = self.env.all.tocompute.get(field, ())
|
||||
if ids is None:
|
||||
ids = ids_to_compute
|
||||
else:
|
||||
ids = [id_ for id_ in ids if id_ in ids_to_compute]
|
||||
if not ids:
|
||||
return
|
||||
|
||||
records = self.browse(ids)
|
||||
if field.store:
|
||||
# do not force recomputation on new records; those will be
|
||||
# recomputed by accessing the field on the records
|
||||
records = records.filtered('id')
|
||||
try:
|
||||
field.recompute(records)
|
||||
except MissingError:
|
||||
existing = records.exists()
|
||||
field.recompute(existing)
|
||||
# mark the field as computed on missing records, otherwise
|
||||
# they remain forever in the todo list, and lead to an
|
||||
# infinite loop...
|
||||
for f in records.pool.field_computed[field]:
|
||||
self.env.remove_to_compute(f, records - existing)
|
||||
else:
|
||||
self.env.cache.invalidate([(field, records._ids)])
|
||||
self.env.remove_to_compute(field, records)
|
||||
|
||||
#
|
||||
# Generic onchange method
|
||||
@@ -6388,7 +6481,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
methods to them, and return all the fields in ``field_onchange``.
|
||||
"""
|
||||
# this is for tests using `Form`
|
||||
self.flush()
|
||||
self.env.flush_all()
|
||||
|
||||
env = self.env
|
||||
if isinstance(field_name, list):
|
||||
@@ -6653,7 +6746,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
snapshot1 = Snapshot(record, nametree)
|
||||
|
||||
# determine values that have changed by comparing snapshots
|
||||
self.invalidate_cache()
|
||||
self.env.invalidate_all()
|
||||
result['value'] = snapshot1.diff(snapshot0, force=first_call)
|
||||
|
||||
# format warnings
|
||||
|
||||
@@ -119,7 +119,7 @@ def force_demo(cr):
|
||||
load_demo(cr, package, {}, 'init')
|
||||
|
||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||
env['ir.module.module'].invalidate_cache(['demo'])
|
||||
env['ir.module.module'].invalidate_model(['demo'])
|
||||
env['res.groups']._update_user_groups_view()
|
||||
|
||||
|
||||
@@ -183,7 +183,7 @@ def load_module_graph(cr, graph, status=None, perform_checks=True,
|
||||
migrations.migrate_module(package, 'pre')
|
||||
if package.name != 'base':
|
||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||
env['base'].flush()
|
||||
env.flush_all()
|
||||
|
||||
load_openerp_module(package.name)
|
||||
|
||||
@@ -230,7 +230,7 @@ def load_module_graph(cr, graph, status=None, perform_checks=True,
|
||||
load_data(cr, idref, mode, kind='data', package=package)
|
||||
demo_loaded = package.dbdemo = load_demo(cr, package, idref, mode)
|
||||
cr.execute('update ir_module_module set demo=%s where id=%s', (demo_loaded, module_id))
|
||||
module.invalidate_cache(['demo'])
|
||||
module.invalidate_model(['demo'])
|
||||
|
||||
migrations.migrate_module(package, 'post')
|
||||
|
||||
@@ -308,7 +308,7 @@ def load_module_graph(cr, graph, status=None, perform_checks=True,
|
||||
for kind in ('init', 'demo', 'update'):
|
||||
if hasattr(package, kind):
|
||||
delattr(package, kind)
|
||||
module.flush()
|
||||
module.env.flush_all()
|
||||
|
||||
extra_queries = odoo.sql_db.sql_counter - module_extra_query_count - test_queries
|
||||
extras = []
|
||||
@@ -454,9 +454,9 @@ def load_modules(registry, force_demo=False, status=None, update_module=False):
|
||||
if modules:
|
||||
modules.button_upgrade()
|
||||
|
||||
env.flush_all()
|
||||
cr.execute("update ir_module_module set state=%s where name=%s", ('installed', 'base'))
|
||||
Module.invalidate_cache(['state'])
|
||||
Module.flush()
|
||||
Module.invalidate_model(['state'])
|
||||
|
||||
# STEP 3: Load marked modules (skipping base which was done in STEP 1)
|
||||
# IMPORTANT: this is done in two parts, first loading all installed or
|
||||
@@ -519,7 +519,7 @@ def load_modules(registry, force_demo=False, status=None, update_module=False):
|
||||
|
||||
# Cleanup orphan records
|
||||
env['ir.model.data']._process_end(processed_modules)
|
||||
env['base'].flush()
|
||||
env.flush_all()
|
||||
|
||||
for kind in ('init', 'demo', 'update'):
|
||||
tools.config[kind] = {}
|
||||
@@ -587,7 +587,7 @@ def load_modules(registry, force_demo=False, status=None, update_module=False):
|
||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||
for model in env.values():
|
||||
model._register_hook()
|
||||
env['base'].flush()
|
||||
env.flush_all()
|
||||
|
||||
# STEP 9: save installed/updated modules for post-install tests
|
||||
registry.updated_modules += processed_modules
|
||||
|
||||
@@ -306,7 +306,7 @@ class Registry(Mapping):
|
||||
if self.ready:
|
||||
for model in env.values():
|
||||
model._register_hook()
|
||||
env['base'].flush()
|
||||
env.flush_all()
|
||||
|
||||
@lazy_property
|
||||
def field_computed(self):
|
||||
@@ -445,7 +445,7 @@ class Registry(Mapping):
|
||||
self.check_indexes(cr, model_names)
|
||||
self.check_foreign_keys(cr)
|
||||
|
||||
env['base'].flush()
|
||||
env.flush_all()
|
||||
|
||||
# make sure all tables are present
|
||||
self.check_tables_exist(cr)
|
||||
@@ -561,7 +561,7 @@ class Registry(Mapping):
|
||||
for name in missing:
|
||||
_logger.info("Recreate table of model %s.", name)
|
||||
env[name].init()
|
||||
env['base'].flush()
|
||||
env.flush_all()
|
||||
# check again, and log errors if tables are still missing
|
||||
missing_tables = set(table2model).difference(existing_tables(cr, table2model))
|
||||
for table in missing_tables:
|
||||
|
||||
+15
-15
@@ -496,14 +496,14 @@ class BaseCase(unittest.TestCase, metaclass=MetaCase):
|
||||
return lambda x: x
|
||||
|
||||
if flush:
|
||||
self.env.user.flush()
|
||||
self.env.flush_all()
|
||||
self.env.cr.flush()
|
||||
|
||||
with patch('odoo.sql_db.Cursor.execute', execute):
|
||||
with patch('odoo.osv.expression.get_unaccent_wrapper', get_unaccent_wrapper):
|
||||
yield actual_queries
|
||||
if flush:
|
||||
self.env.user.flush()
|
||||
self.env.flush_all()
|
||||
self.env.cr.flush()
|
||||
|
||||
self.assertEqual(
|
||||
@@ -538,12 +538,12 @@ class BaseCase(unittest.TestCase, metaclass=MetaCase):
|
||||
login = self.env.user.login
|
||||
expected = counters.get(login, default)
|
||||
if flush:
|
||||
self.env.user.flush()
|
||||
self.env.flush_all()
|
||||
self.env.cr.flush()
|
||||
count0 = self.cr.sql_log_count
|
||||
yield
|
||||
if flush:
|
||||
self.env.user.flush()
|
||||
self.env.flush_all()
|
||||
self.env.cr.flush()
|
||||
count = self.cr.sql_log_count - count0
|
||||
if count != expected:
|
||||
@@ -562,11 +562,11 @@ class BaseCase(unittest.TestCase, metaclass=MetaCase):
|
||||
# flush before and after during warmup, in order to reproduce the
|
||||
# same operations, otherwise the caches might not be ready!
|
||||
if flush:
|
||||
self.env.user.flush()
|
||||
self.env.flush_all()
|
||||
self.env.cr.flush()
|
||||
yield
|
||||
if flush:
|
||||
self.env.user.flush()
|
||||
self.env.flush_all()
|
||||
self.env.cr.flush()
|
||||
|
||||
def assertRecordValues(self, records, expected_values):
|
||||
@@ -772,7 +772,7 @@ class TransactionCase(BaseCase):
|
||||
self.addCleanup(self.env.clear)
|
||||
|
||||
# flush everything in setUpClass before introducing a savepoint
|
||||
self.env['base'].flush()
|
||||
self.env.flush_all()
|
||||
|
||||
self._savepoint_id = next(savepoint_seq)
|
||||
self.cr.execute('SAVEPOINT test_%d' % self._savepoint_id)
|
||||
@@ -816,7 +816,7 @@ class SingleTransactionCase(BaseCase):
|
||||
|
||||
def setUp(self):
|
||||
super(SingleTransactionCase, self).setUp()
|
||||
self.env.user.flush()
|
||||
self.env.flush_all()
|
||||
|
||||
|
||||
class ChromeBrowserException(Exception):
|
||||
@@ -1509,7 +1509,7 @@ class HttpCase(TransactionCase):
|
||||
|
||||
ICP = cls.env['ir.config_parameter']
|
||||
ICP.set_param('web.base.url', cls.base_url())
|
||||
ICP.flush()
|
||||
ICP.env.flush_all()
|
||||
# v8 api with correct xmlrpc exception handling.
|
||||
cls.xmlrpc_url = f'http://{HOST}:{odoo.tools.config["http_port"]:d}/xmlrpc/2/'
|
||||
cls._logger = logging.getLogger('%s.%s' % (cls.__module__, cls.__name__))
|
||||
@@ -1737,16 +1737,16 @@ def warmup(func, *args, **kwargs):
|
||||
effects of the warmup phase are rolled back thanks to a savepoint.
|
||||
"""
|
||||
self = args[0]
|
||||
self.env['base'].flush()
|
||||
self.env.cache.invalidate()
|
||||
self.env.flush_all()
|
||||
self.env.invalidate_all()
|
||||
# run once to warm up the caches
|
||||
self.warm = False
|
||||
self.cr.execute('SAVEPOINT test_warmup')
|
||||
func(*args, **kwargs)
|
||||
self.env['base'].flush()
|
||||
self.env.flush_all()
|
||||
# run once for real
|
||||
self.cr.execute('ROLLBACK TO SAVEPOINT test_warmup')
|
||||
self.env.cache.invalidate()
|
||||
self.env.invalidate_all()
|
||||
self.warm = True
|
||||
func(*args, **kwargs)
|
||||
|
||||
@@ -2127,7 +2127,7 @@ class Form(object):
|
||||
record_to_values(self._view['fields'], r)
|
||||
)
|
||||
self._changed.clear()
|
||||
self._model.flush()
|
||||
self._model.env.flush_all()
|
||||
self._model.env.clear() # discard cache and pending recomputations
|
||||
return r
|
||||
|
||||
@@ -2244,7 +2244,7 @@ class Form(object):
|
||||
|
||||
record = self._model.browse(self._values.get('id'))
|
||||
result = record.onchange(self._onchange_values(), fields, spec)
|
||||
self._model.flush()
|
||||
self._model.env.flush_all()
|
||||
self._model.env.clear() # discard cache and pending recomputations
|
||||
if result.get('warning'):
|
||||
_logger.getChild('onchange').warning("%(title)s %(message)s" % result.get('warning'))
|
||||
|
||||
Reference in New Issue
Block a user