Merge pull request #5321 from odoo-dev/8.0-speedup_loading-rco
[IMP] models: speedup loading of registry (-40%)
This commit is contained in:
commit
d8eb9dd347
|
@ -25,10 +25,9 @@ class mother(models.Model):
|
|||
# in the child object
|
||||
class daughter(models.Model):
|
||||
_name = 'test.inherit.daughter'
|
||||
_inherits = {'test.inherit.mother': 'template_id'}
|
||||
|
||||
template_id = fields.Many2one('test.inherit.mother', 'Template',
|
||||
required=True, ondelete='cascade')
|
||||
delegate=True, required=True, ondelete='cascade')
|
||||
field_in_daughter = fields.Char('Field1')
|
||||
|
||||
|
||||
|
|
|
@ -3,7 +3,13 @@ from openerp.tests import common
|
|||
|
||||
class test_inherits(common.TransactionCase):
|
||||
|
||||
def test_access_from_child_to_parent_model(self):
|
||||
def test_00_inherits(self):
|
||||
""" Check that a many2one field with delegate=True adds an entry in _inherits """
|
||||
daughter = self.env['test.inherit.daughter']
|
||||
|
||||
self.assertEqual(daughter._inherits, {'test.inherit.mother': 'template_id'})
|
||||
|
||||
def test_10_access_from_child_to_parent_model(self):
|
||||
""" check whether added field in model is accessible from children models (_inherits) """
|
||||
# This test checks if the new added column of a parent model
|
||||
# is accessible from the child model. This test has been written
|
||||
|
@ -15,7 +21,7 @@ class test_inherits(common.TransactionCase):
|
|||
self.assertIn('field_in_mother', mother._fields)
|
||||
self.assertIn('field_in_mother', daughter._fields)
|
||||
|
||||
def test_field_extension(self):
|
||||
def test_20_field_extension(self):
|
||||
""" check the extension of a field in an inherited model """
|
||||
mother = self.env['test.inherit.mother']
|
||||
daughter = self.env['test.inherit.daughter']
|
||||
|
@ -41,7 +47,7 @@ class test_inherits(common.TransactionCase):
|
|||
self.assertEqual(field.string, "Template")
|
||||
self.assertTrue(field.required)
|
||||
|
||||
def test_depends_extension(self):
|
||||
def test_30_depends_extension(self):
|
||||
""" check that @depends on overridden compute methods extends dependencies """
|
||||
mother = self.env['test.inherit.mother']
|
||||
field = mother._fields['surname']
|
||||
|
@ -49,7 +55,7 @@ class test_inherits(common.TransactionCase):
|
|||
# the field dependencies are added
|
||||
self.assertItemsEqual(field.depends, ['name', 'field_in_mother'])
|
||||
|
||||
def test_selection_extension(self):
|
||||
def test_40_selection_extension(self):
|
||||
""" check that attribute selection_add=... extends selection on fields. """
|
||||
mother = self.env['test.inherit.mother']
|
||||
|
||||
|
|
|
@ -2,5 +2,4 @@
|
|||
access_category,test_new_api_category,test_new_api.model_test_new_api_category,,1,1,1,1
|
||||
access_discussion,test_new_api_discussion,test_new_api.model_test_new_api_discussion,,1,1,1,1
|
||||
access_message,test_new_api_message,test_new_api.model_test_new_api_message,,1,1,1,1
|
||||
access_talk,test_new_api_talk,test_new_api.model_test_new_api_talk,,1,1,1,1
|
||||
access_mixed,test_new_api_mixed,test_new_api.model_test_new_api_mixed,,1,1,1,1
|
||||
|
|
|
|
@ -153,12 +153,6 @@ class Message(models.Model):
|
|||
self.double_size = self.double_size + size
|
||||
|
||||
|
||||
class Talk(models.Model):
|
||||
_name = 'test_new_api.talk'
|
||||
|
||||
parent = fields.Many2one('test_new_api.discussion', delegate=True, required=True)
|
||||
|
||||
|
||||
class MixedModel(models.Model):
|
||||
_name = 'test_new_api.mixed'
|
||||
|
||||
|
|
|
@ -375,20 +375,3 @@ class TestMagicFields(common.TransactionCase):
|
|||
record = self.env['test_new_api.discussion'].create({'name': 'Booba'})
|
||||
self.assertEqual(record.create_uid, self.env.user)
|
||||
self.assertEqual(record.write_uid, self.env.user)
|
||||
|
||||
|
||||
class TestInherits(common.TransactionCase):
|
||||
|
||||
def test_inherits(self):
|
||||
""" Check that a many2one field with delegate=True adds an entry in _inherits """
|
||||
Talk = self.env['test_new_api.talk']
|
||||
self.assertEqual(Talk._inherits, {'test_new_api.discussion': 'parent'})
|
||||
self.assertIn('name', Talk._fields)
|
||||
self.assertEqual(Talk._fields['name'].related, ('parent', 'name'))
|
||||
|
||||
talk = Talk.create({'name': 'Foo'})
|
||||
discussion = talk.parent
|
||||
self.assertTrue(discussion)
|
||||
self.assertEqual(talk._name, 'test_new_api.talk')
|
||||
self.assertEqual(discussion._name, 'test_new_api.discussion')
|
||||
self.assertEqual(talk.name, discussion.name)
|
||||
|
|
|
@ -297,6 +297,13 @@ class Field(object):
|
|||
self._attrs = {key: val for key, val in kwargs.iteritems() if val is not None}
|
||||
self._free_attrs = []
|
||||
|
||||
# self._triggers is a set of pairs (field, path) that represents the
|
||||
# computed fields that depend on `self`. When `self` is modified, it
|
||||
# invalidates the cache of each `field`, and registers the records to
|
||||
# recompute based on `path`. See method `modified` below for details.
|
||||
self._triggers = set()
|
||||
self.inverse_fields = []
|
||||
|
||||
def new(self, **kwargs):
|
||||
""" Return a field of the same type as `self`, with its own parameters. """
|
||||
return type(self)(**kwargs)
|
||||
|
@ -396,16 +403,6 @@ class Field(object):
|
|||
# Field setup
|
||||
#
|
||||
|
||||
def reset(self):
|
||||
""" Prepare `self` for a new setup. """
|
||||
self.setup_done = False
|
||||
# self._triggers is a set of pairs (field, path) that represents the
|
||||
# computed fields that depend on `self`. When `self` is modified, it
|
||||
# invalidates the cache of each `field`, and registers the records to
|
||||
# recompute based on `path`. See method `modified` below for details.
|
||||
self._triggers = set()
|
||||
self.inverse_fields = []
|
||||
|
||||
def setup(self, env):
|
||||
""" Make sure that `self` is set up, except for recomputation triggers. """
|
||||
if not self.setup_done:
|
||||
|
|
|
@ -636,8 +636,8 @@ class BaseModel(object):
|
|||
attrs = {
|
||||
'_name': name,
|
||||
'_register': False,
|
||||
'_columns': {}, # filled by _setup_fields()
|
||||
'_defaults': {}, # filled by Field._determine_default()
|
||||
'_columns': None, # recomputed in _setup_fields()
|
||||
'_defaults': None, # recomputed in _setup_base()
|
||||
'_inherits': dict(cls._inherits),
|
||||
'_depends': dict(cls._depends),
|
||||
'_constraints': list(cls._constraints),
|
||||
|
@ -745,15 +745,40 @@ class BaseModel(object):
|
|||
for (key, _, msg) in cls._sql_constraints:
|
||||
cls.pool._sql_error[cls._table + '_' + key] = msg
|
||||
|
||||
# collect constraint and onchange methods
|
||||
cls._constraint_methods = []
|
||||
cls._onchange_methods = defaultdict(list)
|
||||
for attr, func in getmembers(cls, callable):
|
||||
if hasattr(func, '_constrains'):
|
||||
cls._constraint_methods.append(func)
|
||||
if hasattr(func, '_onchange'):
|
||||
for name in func._onchange:
|
||||
cls._onchange_methods[name].append(func)
|
||||
@property
|
||||
def _constraint_methods(self):
|
||||
""" Return a list of methods implementing Python constraints. """
|
||||
def is_constraint(func):
|
||||
return callable(func) and hasattr(func, '_constrains')
|
||||
|
||||
cls = type(self)
|
||||
methods = []
|
||||
for attr, func in getmembers(cls, is_constraint):
|
||||
if not all(name in cls._fields for name in func._constrains):
|
||||
_logger.warning("@constrains%r parameters must be field names", func._constrains)
|
||||
methods.append(func)
|
||||
|
||||
# optimization: memoize result on cls, it will not be recomputed
|
||||
cls._constraint_methods = methods
|
||||
return methods
|
||||
|
||||
@property
|
||||
def _onchange_methods(self):
|
||||
""" Return a dictionary mapping field names to onchange methods. """
|
||||
def is_onchange(func):
|
||||
return callable(func) and hasattr(func, '_onchange')
|
||||
|
||||
cls = type(self)
|
||||
methods = defaultdict(list)
|
||||
for attr, func in getmembers(cls, is_onchange):
|
||||
for name in func._onchange:
|
||||
if name not in cls._fields:
|
||||
_logger.warning("@onchange%r parameters must be field names", func._onchange)
|
||||
methods[name].append(func)
|
||||
|
||||
# optimization: memoize result on cls, it will not be recomputed
|
||||
cls._onchange_methods = methods
|
||||
return methods
|
||||
|
||||
def __new__(cls):
|
||||
# In the past, this method was registering the model class in the server.
|
||||
|
@ -800,19 +825,6 @@ class BaseModel(object):
|
|||
"TransientModels must have log_access turned on, " \
|
||||
"in order to implement their access rights policy"
|
||||
|
||||
# retrieve new-style fields (from above registry class) and duplicate
|
||||
# them (to avoid clashes with inheritance between different models)
|
||||
cls._fields = {}
|
||||
above = cls.__bases__[0]
|
||||
for attr, field in getmembers(above, Field.__instancecheck__):
|
||||
cls._add_field(attr, field.new())
|
||||
|
||||
# introduce magic fields
|
||||
cls._add_magic_fields()
|
||||
|
||||
# register constraints and onchange methods
|
||||
cls._init_constraints_onchanges()
|
||||
|
||||
# prepare ormcache, which must be shared by all instances of the model
|
||||
cls._ormcache = {}
|
||||
|
||||
|
@ -2941,26 +2953,28 @@ class BaseModel(object):
|
|||
if cls._setup_done:
|
||||
return
|
||||
|
||||
# first make sure that parent models determine all their fields
|
||||
# 1. determine the proper fields of the model; duplicate them on cls to
|
||||
# avoid clashes with inheritance between different models
|
||||
for name in getattr(cls, '_fields', {}):
|
||||
delattr(cls, name)
|
||||
|
||||
# retrieve fields from parent classes
|
||||
cls._fields = {}
|
||||
cls._defaults = {}
|
||||
for attr, field in getmembers(cls, Field.__instancecheck__):
|
||||
cls._add_field(attr, field.new())
|
||||
|
||||
# add magic and custom fields
|
||||
cls._add_magic_fields()
|
||||
cls._init_manual_fields(self._cr, partial)
|
||||
|
||||
# 2. make sure that parent models determine their own fields, then add
|
||||
# inherited fields to cls
|
||||
cls._inherits_check()
|
||||
for parent in cls._inherits:
|
||||
self.env[parent]._setup_base(partial)
|
||||
|
||||
# remove inherited fields from cls._fields
|
||||
for name, field in cls._fields.items():
|
||||
if field.inherited:
|
||||
del cls._fields[name]
|
||||
|
||||
# retrieve custom fields
|
||||
cls._init_manual_fields(self._cr, partial)
|
||||
|
||||
# retrieve inherited fields
|
||||
cls._init_inherited_fields()
|
||||
|
||||
# prepare the setup of fields
|
||||
for field in cls._fields.itervalues():
|
||||
field.reset()
|
||||
|
||||
cls._setup_done = True
|
||||
|
||||
@api.model
|
||||
|
@ -2968,7 +2982,8 @@ class BaseModel(object):
|
|||
""" Setup the fields, except for recomputation triggers. """
|
||||
cls = type(self)
|
||||
|
||||
# set up fields, and update their corresponding columns
|
||||
# set up fields, and determine their corresponding column
|
||||
cls._columns = {}
|
||||
for name, field in cls._fields.iteritems():
|
||||
field.setup(self.env)
|
||||
if field.store or field.column:
|
||||
|
@ -3007,14 +3022,8 @@ class BaseModel(object):
|
|||
# register stuff about low-level function fields
|
||||
cls._init_function_fields(cls.pool, self._cr)
|
||||
|
||||
# check constraints
|
||||
for func in cls._constraint_methods:
|
||||
if not all(name in cls._fields for name in func._constrains):
|
||||
_logger.warning("@constrains%r parameters must be field names", func._constrains)
|
||||
for name in cls._onchange_methods:
|
||||
if name not in cls._fields:
|
||||
func = cls._onchange_methods[name]
|
||||
_logger.warning("@onchange%r parameters must be field names", func._onchange)
|
||||
# register constraints and onchange methods
|
||||
cls._init_constraints_onchanges()
|
||||
|
||||
# check defaults
|
||||
for name in cls._defaults:
|
||||
|
|
Loading…
Reference in New Issue