From 45a37b22fd9914da7fa38c56c25173583c59abf7 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Tue, 17 Feb 2015 12:07:50 +0100 Subject: [PATCH] [IMP] models: speedup loading of registry (-20%) Idea: look up for the model's fields in method `_setup_base()` instead of method `__init__()`. This does not make a significant difference when installing or upgrading modules, but when simply loading a registry, the (expensive) field lookup is done once per model instead of once per class. --- openerp/fields.py | 17 +++++------- openerp/models.py | 66 +++++++++++++++++++---------------------------- 2 files changed, 34 insertions(+), 49 deletions(-) diff --git a/openerp/fields.py b/openerp/fields.py index 7a58bef7641..5c38d5bb4bd 100644 --- a/openerp/fields.py +++ b/openerp/fields.py @@ -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: diff --git a/openerp/models.py b/openerp/models.py index a9f32c72588..f1aa6ebf8c0 100644 --- a/openerp/models.py +++ b/openerp/models.py @@ -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), @@ -750,9 +750,13 @@ class BaseModel(object): cls._onchange_methods = defaultdict(list) for attr, func in getmembers(cls, callable): if hasattr(func, '_constrains'): + if not all(name in cls._fields for name in func._constrains): + _logger.warning("@constrains%r parameters must be field names", func._constrains) cls._constraint_methods.append(func) if hasattr(func, '_onchange'): for name in func._onchange: + if name not in cls._fields: + _logger.warning("@onchange%r parameters must be field names", func._onchange) cls._onchange_methods[name].append(func) def __new__(cls): @@ -800,19 +804,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 +2932,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 +2961,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 +3001,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: