[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` """
|
||||
assert self.store
|
||||
|
||||
# some columns are registry-dependent, like float fields (digits);
|
||||
# duplicate them to avoid sharing between registries
|
||||
# determine column parameters
|
||||
_logger.debug("Create fields._column for Field %s", self)
|
||||
args = {}
|
||||
for attr, prop in self.column_attrs:
|
||||
|
@ -644,10 +643,9 @@ class Field(object):
|
|||
args['relation'] = self.comodel_name
|
||||
return fields.property(**args)
|
||||
|
||||
if isinstance(self.column, fields.function):
|
||||
# it is too tricky to recreate a function field, so for that case,
|
||||
# we make a stupid (and possibly incorrect) copy of the column
|
||||
return copy(self.column)
|
||||
if self.column:
|
||||
# let the column provide a valid column for the given parameters
|
||||
return self.column.new(**args)
|
||||
|
||||
return getattr(fields, self.type)(**args)
|
||||
|
||||
|
@ -1303,14 +1301,14 @@ class Selection(Field):
|
|||
|
||||
class Reference(Selection):
|
||||
type = 'reference'
|
||||
size = 128
|
||||
size = None
|
||||
|
||||
def __init__(self, selection=None, string=None, **kwargs):
|
||||
super(Reference, self).__init__(selection=selection, string=string, **kwargs)
|
||||
|
||||
def _setup(self, 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)
|
||||
|
||||
_related_size = property(attrgetter('size'))
|
||||
|
@ -1725,10 +1723,6 @@ class Id(Field):
|
|||
super(Id, self).__init__(type='integer', string=string, **kwargs)
|
||||
|
||||
def to_column(self):
|
||||
""" to_column() -> fields._column
|
||||
|
||||
Whatever
|
||||
"""
|
||||
return fields.integer('ID')
|
||||
|
||||
def __get__(self, record, owner):
|
||||
|
|
|
@ -104,7 +104,7 @@ class _column(object):
|
|||
self.help = args.get('help', '')
|
||||
self.priority = priority
|
||||
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._domain = domain
|
||||
self._context = context
|
||||
|
@ -125,6 +125,21 @@ class _column(object):
|
|||
if not self._classic_write or self.deprecated:
|
||||
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):
|
||||
""" convert column `self` to a new-style field """
|
||||
from openerp.fields import Field
|
||||
|
@ -318,6 +333,10 @@ class float(_column):
|
|||
# synopsis: digits_compute(cr) -> (precision, scale)
|
||||
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):
|
||||
args = super(float, self).to_field_args()
|
||||
args['digits'] = self.digits_compute or self.digits
|
||||
|
@ -1248,6 +1267,11 @@ class function(_column):
|
|||
self._symbol_f = type_class._symbol_f
|
||||
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):
|
||||
args = super(function, self).to_field_args()
|
||||
if self._type in ('float',):
|
||||
|
|
Loading…
Reference in New Issue