[IMP] fields: reuse column objects when possible, instead of recreating them
This is a memory optimization: it reduces the memory footprint of each registry. We have observed a reduction of 10Mb on a database with modules crm, sale, purchase, stock.
This commit is contained in:
parent
575ea15518
commit
8e6d5beb35
|
@ -629,8 +629,7 @@ class Field(object):
|
||||||
""" return a low-level field object corresponding to `self` """
|
""" return a low-level field object corresponding to `self` """
|
||||||
assert self.store
|
assert self.store
|
||||||
|
|
||||||
# some columns are registry-dependent, like float fields (digits);
|
# determine column parameters
|
||||||
# duplicate them to avoid sharing between registries
|
|
||||||
_logger.debug("Create fields._column for Field %s", self)
|
_logger.debug("Create fields._column for Field %s", self)
|
||||||
args = {}
|
args = {}
|
||||||
for attr, prop in self.column_attrs:
|
for attr, prop in self.column_attrs:
|
||||||
|
@ -644,10 +643,9 @@ class Field(object):
|
||||||
args['relation'] = self.comodel_name
|
args['relation'] = self.comodel_name
|
||||||
return fields.property(**args)
|
return fields.property(**args)
|
||||||
|
|
||||||
if isinstance(self.column, fields.function):
|
if self.column:
|
||||||
# it is too tricky to recreate a function field, so for that case,
|
# let the column provide a valid column for the given parameters
|
||||||
# we make a stupid (and possibly incorrect) copy of the column
|
return self.column.new(**args)
|
||||||
return copy(self.column)
|
|
||||||
|
|
||||||
return getattr(fields, self.type)(**args)
|
return getattr(fields, self.type)(**args)
|
||||||
|
|
||||||
|
@ -1303,14 +1301,14 @@ class Selection(Field):
|
||||||
|
|
||||||
class Reference(Selection):
|
class Reference(Selection):
|
||||||
type = 'reference'
|
type = 'reference'
|
||||||
size = 128
|
size = None
|
||||||
|
|
||||||
def __init__(self, selection=None, string=None, **kwargs):
|
def __init__(self, selection=None, string=None, **kwargs):
|
||||||
super(Reference, self).__init__(selection=selection, string=string, **kwargs)
|
super(Reference, self).__init__(selection=selection, string=string, **kwargs)
|
||||||
|
|
||||||
def _setup(self, env):
|
def _setup(self, env):
|
||||||
super(Reference, self)._setup(env)
|
super(Reference, self)._setup(env)
|
||||||
assert isinstance(self.size, int), \
|
assert isinstance(self.size, (NoneType, int)), \
|
||||||
"Reference field %s with non-integer size %r" % (self, self.size)
|
"Reference field %s with non-integer size %r" % (self, self.size)
|
||||||
|
|
||||||
_related_size = property(attrgetter('size'))
|
_related_size = property(attrgetter('size'))
|
||||||
|
@ -1725,10 +1723,6 @@ class Id(Field):
|
||||||
super(Id, self).__init__(type='integer', string=string, **kwargs)
|
super(Id, self).__init__(type='integer', string=string, **kwargs)
|
||||||
|
|
||||||
def to_column(self):
|
def to_column(self):
|
||||||
""" to_column() -> fields._column
|
|
||||||
|
|
||||||
Whatever
|
|
||||||
"""
|
|
||||||
return fields.integer('ID')
|
return fields.integer('ID')
|
||||||
|
|
||||||
def __get__(self, record, owner):
|
def __get__(self, record, owner):
|
||||||
|
|
|
@ -104,7 +104,7 @@ class _column(object):
|
||||||
self.help = args.get('help', '')
|
self.help = args.get('help', '')
|
||||||
self.priority = priority
|
self.priority = priority
|
||||||
self.change_default = change_default
|
self.change_default = change_default
|
||||||
self.ondelete = ondelete.lower() if ondelete else None # defaults to 'set null' in ORM
|
self.ondelete = ondelete.lower() if ondelete else 'set null'
|
||||||
self.translate = translate
|
self.translate = translate
|
||||||
self._domain = domain
|
self._domain = domain
|
||||||
self._context = context
|
self._context = context
|
||||||
|
@ -125,6 +125,21 @@ class _column(object):
|
||||||
if not self._classic_write or self.deprecated:
|
if not self._classic_write or self.deprecated:
|
||||||
self._prefetch = False
|
self._prefetch = False
|
||||||
|
|
||||||
|
def new(self, **args):
|
||||||
|
""" return a column like `self` with the given parameters """
|
||||||
|
# memory optimization: reuse self whenever possible; you can reduce the
|
||||||
|
# average memory usage per registry by 10 megabytes!
|
||||||
|
return self if self.same_parameters(args) else type(self)(**args)
|
||||||
|
|
||||||
|
def same_parameters(self, args):
|
||||||
|
dummy = object()
|
||||||
|
return all(
|
||||||
|
# either both are falsy, or they are equal
|
||||||
|
(not val1 and not val) or (val1 == val)
|
||||||
|
for key, val in args.iteritems()
|
||||||
|
for val1 in [getattr(self, key, getattr(self, '_' + key, dummy))]
|
||||||
|
)
|
||||||
|
|
||||||
def to_field(self):
|
def to_field(self):
|
||||||
""" convert column `self` to a new-style field """
|
""" convert column `self` to a new-style field """
|
||||||
from openerp.fields import Field
|
from openerp.fields import Field
|
||||||
|
@ -318,6 +333,10 @@ class float(_column):
|
||||||
# synopsis: digits_compute(cr) -> (precision, scale)
|
# synopsis: digits_compute(cr) -> (precision, scale)
|
||||||
self.digits_compute = digits_compute
|
self.digits_compute = digits_compute
|
||||||
|
|
||||||
|
def new(self, **args):
|
||||||
|
# float columns are database-dependent, so always recreate them
|
||||||
|
return type(self)(**args)
|
||||||
|
|
||||||
def to_field_args(self):
|
def to_field_args(self):
|
||||||
args = super(float, self).to_field_args()
|
args = super(float, self).to_field_args()
|
||||||
args['digits'] = self.digits_compute or self.digits
|
args['digits'] = self.digits_compute or self.digits
|
||||||
|
@ -1248,6 +1267,11 @@ class function(_column):
|
||||||
self._symbol_f = type_class._symbol_f
|
self._symbol_f = type_class._symbol_f
|
||||||
self._symbol_set = type_class._symbol_set
|
self._symbol_set = type_class._symbol_set
|
||||||
|
|
||||||
|
def new(self, **args):
|
||||||
|
# HACK: function fields are tricky to recreate, simply return a copy
|
||||||
|
import copy
|
||||||
|
return copy.copy(self)
|
||||||
|
|
||||||
def to_field_args(self):
|
def to_field_args(self):
|
||||||
args = super(function, self).to_field_args()
|
args = super(function, self).to_field_args()
|
||||||
if self._type in ('float',):
|
if self._type in ('float',):
|
||||||
|
|
Loading…
Reference in New Issue