From 9bce04de7966346a75226d1ee6b8217deb266858 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Wed, 4 Mar 2015 14:44:14 +0100 Subject: [PATCH] [IMP] models: build a flat hierarchy of classes for models Instead of composing classes in a binary tree hierarchy, we make one class that inherits from all its base classes. This avoids keeping intermediate data structures for columns, inherits, constraints, etc. This saves about 600Kb per registry. --- openerp/models.py | 86 ++++++++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 46 deletions(-) diff --git a/openerp/models.py b/openerp/models.py index 3b93d2c4158..eb45259c6ba 100644 --- a/openerp/models.py +++ b/openerp/models.py @@ -569,11 +569,22 @@ class BaseModel(object): """ - # IMPORTANT: the registry contains an instance for each model. The class - # of each model carries inferred metadata that is shared among the - # model's instances for this registry, but not among registries. Hence - # we cannot use that "registry class" for combining model classes by - # inheritance, since it confuses the metadata inference process. + # The model's class inherits from cls and the classes of the inherited + # models. All those classes are combined in a flat hierarchy: + # + # Model the base class of all models + # / | \ + # cls c2 c1 the classes defined in modules + # \ | / + # ModelClass the final class of the model + # / | \ + # model recordset ... the class' instances + # + # The registry contains the instance `model`. Its class, `ModelClass`, + # carries inferred metadata that is shared between all the model's + # instances for this registry only. When we '_inherit' from another + # model, we do not inherit its `ModelClass`, but this class' parents. + # This is a limitation of the inheritance mechanism. # Keep links to non-inherited constraints in cls; this is useful for # instance when exporting translations @@ -590,65 +601,48 @@ class BaseModel(object): # determine the module that introduced the model original_module = pool[name]._original_module if name in parents else cls._module - # build the class hierarchy for the model + # determine all the classes the model should inherit from + bases = [cls] for parent in parents: if parent not in pool: raise TypeError('The model "%s" specifies an unexisting parent class "%s"\n' 'You may need to add a dependency on the parent class\' module.' % (name, parent)) - parent_model = pool[parent] + bases += type(pool[parent]).__bases__ - # do no use the class of parent_model, since that class contains - # inferred metadata; use its ancestor instead - parent_class = type(parent_model).__base__ + # determine the attributes of the model's class + inherits = {} + depends = {} + constraints = {} + sql_constraints = [] - inherits = dict(parent_class._inherits) - inherits.update(cls._inherits) + for base in reversed(bases): + inherits.update(base._inherits) - depends = dict(parent_class._depends) - for m, fs in cls._depends.iteritems(): - depends[m] = depends.get(m, []) + fs + for mname, fnames in base._depends.iteritems(): + depends[mname] = depends.get(mname, []) + fnames - old_constraints = parent_class._constraints - new_constraints = cls._constraints - # filter out from old_constraints the ones overridden by a - # constraint with the same function name in new_constraints - constraints = new_constraints + [oldc - for oldc in old_constraints - if not any(newc[2] == oldc[2] and same_name(newc[0], oldc[0]) - for newc in new_constraints) - ] + for cons in base._constraints: + # cons may override a constraint with the same function name + constraints[getattr(cons[0], '__name__', id(cons[0]))] = cons - sql_constraints = cls._sql_constraints + \ - parent_class._sql_constraints + sql_constraints += base._sql_constraints - attrs = { - '_name': name, - '_register': False, - '_inherits': inherits, - '_depends': depends, - '_constraints': constraints, - '_sql_constraints': sql_constraints, - } - cls = type(name, (cls, parent_class), attrs) - - # introduce the "registry class" of the model; - # duplicate some attributes so that the ORM can modify them - attrs = { + # build the actual class of the model + ModelClass = type(name, tuple(bases), { '_name': name, '_register': False, '_columns': None, # recomputed in _setup_fields() '_defaults': None, # recomputed in _setup_base() '_fields': frozendict(), # idem - '_inherits': dict(cls._inherits), - '_depends': dict(cls._depends), - '_constraints': list(cls._constraints), - '_sql_constraints': list(cls._sql_constraints), + '_inherits': inherits, + '_depends': depends, + '_constraints': constraints.values(), + '_sql_constraints': sql_constraints, '_original_module': original_module, - } - cls = type(cls._name, (cls,), attrs) + }) # instantiate the model, and initialize it - model = object.__new__(cls) + model = object.__new__(ModelClass) model.__init__(pool, cr) return model