[REM] core: remove optimization sharing fields across registries

The optimization will be reintroduced later in a different form.
This commit is contained in:
Raphael Collet
2021-05-03 12:33:29 +00:00
parent 960cd72774
commit 81ff2717cf
6 changed files with 42 additions and 83 deletions
+2 -2
View File
@@ -510,8 +510,8 @@ class TestCustomFields(common.TransactionCase):
})
query_count = self.cr.sql_log_count - query_count
# create the related field, and assert it only takes 1 extra query
with self.assertQueryCount(query_count + 1):
# create the related field, and assert it only takes 3 extra queries
with self.assertQueryCount(query_count + 3):
self.env['ir.model.fields'].create({
'model_id': self.env['ir.model']._get_id('res.partner'),
'name': 'x_oh_boy',
@@ -228,6 +228,7 @@ class Multi(models.Model):
partner = fields.Many2one('res.partner')
lines = fields.One2many('test_new_api.multi.line', 'multi')
partners = fields.One2many(related='partner.child_ids')
tags = fields.Many2many('test_new_api.multi.tag', domain=[('name', 'ilike', 'a')])
@api.onchange('name')
def _onchange_name(self):
@@ -1824,28 +1824,23 @@ class TestFields(TransactionCaseWithUserDemo):
def test_60_many2many_domain(self):
""" test the cache consistency of a many2many field with a domain """
discussion = self.env.ref('test_new_api.discussion_0')
category = self.env['test_new_api.category'].create({'name': "Foo"})
discussion.categories = category
discussion.flush()
discussion.invalidate_cache()
tag = self.env['test_new_api.multi.tag'].create({'name': 'bar'})
record = self.env['test_new_api.multi'].create({'tags': tag.ids})
record.flush()
record.invalidate_cache()
# patch the many2many field to give it a domain (this simply avoids
# adding yet another test model)
field = discussion._fields['categories']
self.patch(field, 'domain', [('color', '!=', 42)])
self.registry.setup_models(self.cr)
self.assertEqual(type(record).tags.domain, [('name', 'ilike', 'a')])
# the category is in the many2many
self.assertIn(category, discussion.categories)
# the tag is in the many2many
self.assertIn(tag, record.tags)
# modify the category; it should not longer be in the many2many
category.color = 42
self.assertNotIn(category, discussion.categories)
# modify the tag; it should not longer be in the many2many
tag.name = 'foo'
self.assertNotIn(tag, record.tags)
# modify again the category; it should be back in the many2many
category.color = 69
self.assertIn(category, discussion.categories)
# modify again the tag; it should be back in the many2many
tag.name = 'baz'
self.assertIn(tag, record.tags)
def test_70_x2many_write(self):
discussion = self.env.ref('test_new_api.discussion_0')
@@ -2676,7 +2671,6 @@ class TestMagicFields(common.TransactionCase):
# check setup of models in alphanumeric order
self.patch(registry, 'models', OrderedDict(sorted(models.items())))
registry.model_cache.clear()
registry.setup_models(self.cr)
field = registry['test_new_api.display'].display_name
self.assertFalse(field.automatic)
@@ -2684,7 +2678,6 @@ class TestMagicFields(common.TransactionCase):
# check setup of models in reverse alphanumeric order
self.patch(registry, 'models', OrderedDict(sorted(models.items(), reverse=True)))
registry.model_cache.clear()
registry.setup_models(self.cr)
field = registry['test_new_api.display'].display_name
self.assertFalse(field.automatic)
@@ -3094,7 +3087,6 @@ class TestSelectionOndeleteAdvanced(common.TransactionCase):
for model_name in (self.MODEL_BASE, self.MODEL_REQUIRED):
Model = self.registry[model_name]
self.addCleanup(setattr, Model, '__bases__', Model.__bases__)
self.addCleanup(self.registry.model_cache.clear)
def test_ondelete_unexisting_policy(self):
class Foo(models.Model):
@@ -3155,7 +3147,6 @@ class TestSelectionOndeleteAdvanced(common.TransactionCase):
class TestFieldParametersValidation(common.TransactionCase):
def test_invalid_parameter(self):
self.addCleanup(self.registry.model_cache.clear)
class Foo(models.Model):
_module = None
+12 -10
View File
@@ -54,17 +54,18 @@ def first(records):
def resolve_mro(model, name, predicate):
""" Return the list of successively overridden values of attribute ``name``
in mro order on ``model`` that satisfy ``predicate``. Model classes
(the ones that appear in the registry) are ignored.
in mro order on ``model`` that satisfy ``predicate``. Model registry
classes are ignored.
"""
result = []
for cls in model._model_classes:
value = cls.__dict__.get(name, Default)
if value is Default:
continue
if not predicate(value):
break
result.append(value)
for cls in type(model).mro():
if not is_registry_class(cls):
value = cls.__dict__.get(name, Default)
if value is Default:
continue
if not predicate(value):
break
result.append(value)
return result
@@ -4035,5 +4036,6 @@ def apply_required(model, field_name):
# pylint: disable=wrong-import-position
from .exceptions import AccessError, MissingError, UserError
from .models import (
check_pg_name, expand_ids, is_definition_class, BaseModel, IdType, NewId, PREFETCH_MAX,
check_pg_name, expand_ids, is_definition_class, is_registry_class,
BaseModel, IdType, NewId, PREFETCH_MAX,
)
+14 -45
View File
@@ -2850,9 +2850,6 @@ class BaseModel(metaclass=MetaModel):
cls = type(self)
cls._setup_done = False
# the classes that define this model's base fields and methods
cls._model_classes = tuple(c for c in cls.mro() if not is_registry_class(c))
# reset those attributes on the model's class for _setup_fields() below
for attr in ('_rec_name', '_active_name'):
try:
@@ -2869,51 +2866,23 @@ class BaseModel(metaclass=MetaModel):
# 1. determine the proper fields of the model: the fields defined on the
# class and magic fields, not the inherited or custom ones
cls0 = cls.pool.model_cache.get(cls._model_classes)
if cls0 and cls0._model_classes == cls._model_classes:
# cls0 is either a model class from another registry, or cls itself.
# The point is that it has the same base classes. We retrieve stuff
# from cls0 to optimize the setup of cls. cls0 is guaranteed to be
# properly set up: registries are loaded under a global lock,
# therefore two registries are never set up at the same time.
# retrieve fields from parent classes, and duplicate them on cls to
# avoid clashes with inheritance between different models
for name in cls._fields:
delattr(cls, name)
cls._fields.clear()
# remove fields that are not proper to cls
for name in set(cls._fields).difference(cls0._model_fields):
delattr(cls, name)
del cls._fields[name]
# collect the definitions of each field (base definition + overrides)
definitions = defaultdict(list)
for klass in reversed(cls.mro()):
if is_definition_class(klass):
for field in klass._field_definitions:
definitions[field.name].append(field)
for name, fields_ in definitions.items():
self._add_field(name, fields_[-1].new(_base_fields=fields_))
if cls0 is not cls:
# collect proper fields on cls0, and add them on cls
for name in cls0._model_fields:
field = cls0._fields[name]
# regular fields are shared, while related fields are setup from scratch
if not field.related:
self._add_field(name, field)
else:
self._add_field(name, field.new(**field.args))
cls._model_fields = list(cls._fields)
else:
# retrieve fields from parent classes, and duplicate them on cls to
# avoid clashes with inheritance between different models
for name in cls._fields:
delattr(cls, name)
cls._fields.clear()
# collect the definitions of each field (base definition + overrides)
definitions = defaultdict(list)
for klass in reversed(cls.mro()):
if is_definition_class(klass):
for field in klass._field_definitions:
definitions[field.name].append(field)
for name, fields_ in definitions.items():
self._add_field(name, fields_[-1].new(_base_fields=fields_))
self._add_magic_fields()
cls._model_fields = list(cls._fields)
cls.pool.model_cache[cls._model_classes] = cls
self._add_magic_fields()
# 2. add manual fields
if self.pool._init_modules:
-4
View File
@@ -9,7 +9,6 @@ from collections.abc import Mapping
from contextlib import closing, contextmanager
from functools import partial
from operator import attrgetter
from weakref import WeakValueDictionary
import logging
import os
import threading
@@ -38,9 +37,6 @@ class Registry(Mapping):
_lock = threading.RLock()
_saved_lock = None
# a cache for model classes, indexed by their base classes
model_cache = WeakValueDictionary()
@lazy_classproperty
def registries(cls):
""" A mapping from database names to registries. """