diff --git a/openerp/fields.py b/openerp/fields.py index 6081a61e1dc..ba3c5be844d 100644 --- a/openerp/fields.py +++ b/openerp/fields.py @@ -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): diff --git a/openerp/osv/fields.py b/openerp/osv/fields.py index 9ae41142ea4..30fe1f9a5eb 100644 --- a/openerp/osv/fields.py +++ b/openerp/osv/fields.py @@ -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',):