[REM] core: remove optimization sharing fields across registries
The optimization will be reintroduced later in a different form.
This commit is contained in:
@@ -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
@@ -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
@@ -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:
|
||||
|
||||
@@ -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. """
|
||||
|
||||
Reference in New Issue
Block a user