From 5eb6e5815666faf1bb7a191e451b4c8a907323f2 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Thu, 30 Oct 2014 11:00:10 +0100 Subject: [PATCH] [FIX] fields: make field.store=False on old-style function fields Clarify the semantics of field attributes: - field.store is True when the field is actually stored in the database; - field.column is the column corresponding to field or None. The various field definitions correspond to: - new-style stored field: field.store and field.column - new-style non-stored field: not field.store and not field.column - old-style regular field: field.store and field.column - old-style function field: not field.store and field.column --- openerp/fields.py | 76 +++++++++++++++++---------------------- openerp/models.py | 8 ++--- openerp/osv/expression.py | 2 +- openerp/osv/fields.py | 1 + 4 files changed, 39 insertions(+), 48 deletions(-) diff --git a/openerp/fields.py b/openerp/fields.py index 52d594df659..53f6024d739 100644 --- a/openerp/fields.py +++ b/openerp/fields.py @@ -248,7 +248,7 @@ class Field(object): automatic = False # whether the field is automatically created ("magic" field) inherited = False # whether the field is inherited (_inherits) - column = None # the column interfaced by the field + column = None # the column corresponding to the field setup_done = False # whether the field has been set up name = None # name of the field @@ -314,6 +314,10 @@ class Field(object): # by default, related fields are not stored attrs['store'] = attrs.get('store', False) + # fix for function fields overridden by regular columns + if not isinstance(attrs.get('column'), (NoneType, fields.function)): + attrs.pop('store', None) + for attr, value in attrs.iteritems(): if not hasattr(self, attr): self._free_attrs.append(attr) @@ -443,7 +447,7 @@ class Field(object): self.depends = ('.'.join(self.related),) self.compute = self._compute_related self.inverse = self._inverse_related - if field._description_searchable(env): + if field._description_searchable: # allow searching on self only if the related field is searchable self.search = self._search_related @@ -575,30 +579,7 @@ class Field(object): return desc # properties used by get_description() - - def _description_store(self, env): - if self.store: - # if the corresponding column is a function field, check the column - column = env[self.model_name]._columns.get(self.name) - return bool(getattr(column, 'store', True)) - return False - - def _description_searchable(self, env): - if self.store: - column = env[self.model_name]._columns.get(self.name) - return bool(getattr(column, 'store', True)) or \ - bool(getattr(column, '_fnct_search', False)) - return bool(self.search) - - def _description_sortable(self, env): - if self.store: - column = env[self.model_name]._columns.get(self.name) - return bool(getattr(column, 'store', True)) - if self.inherited: - # self is sortable if the inherited field is itself sortable - return self.related_field._description_sortable(env) - return False - + _description_store = property(attrgetter('store')) _description_manual = property(attrgetter('manual')) _description_depends = property(attrgetter('depends')) _description_related = property(attrgetter('related')) @@ -610,6 +591,14 @@ class Field(object): _description_change_default = property(attrgetter('change_default')) _description_deprecated = property(attrgetter('deprecated')) + @property + def _description_searchable(self): + return bool(self.store or self.search or (self.column and self.column._fnct_search)) + + @property + def _description_sortable(self): + return self.store or (self.inherited and self.related_field._description_sortable) + def _description_string(self, env): if self.string and env.lang: name = "%s,%s" % (self.model_name, self.name) @@ -631,7 +620,7 @@ class Field(object): def to_column(self): """ return a low-level field object corresponding to `self` """ - assert self.store + assert self.store or self.column # determine column parameters _logger.debug("Create fields._column for Field %s", self) @@ -645,13 +634,15 @@ class Field(object): # company-dependent fields are mapped to former property fields args['type'] = self.type args['relation'] = self.comodel_name - return fields.property(**args) - - if self.column: + self.column = fields.property(**args) + elif self.column: # let the column provide a valid column for the given parameters - return self.column.new(**args) + self.column = self.column.new(**args) + else: + # create a fresh new column of the right type + self.column = getattr(fields, self.type)(**args) - return getattr(fields, self.type)(**args) + return self.column # properties used by to_column() to create a column instance _column_copy = property(attrgetter('copy')) @@ -821,8 +812,8 @@ class Field(object): """ Determine the value of `self` for `record`. """ env = record.env - if self.store and not (self.depends and env.in_draft): - # this is a stored field + if self.column and not (self.depends and env.in_draft): + # this is a stored field or an old-style function field if self.depends: # this is a stored computed field, check for recomputation recs = record._recompute_check(self) @@ -979,9 +970,8 @@ class Float(Field): if self.digits: assert isinstance(self.digits, (tuple, list)) and len(self.digits) >= 2, \ "Float field %s with digits %r, expecting (total, decimal)" % (self, self.digits) - if self.store: - column = env[self.model_name]._columns[self.name] - column.digits_change(env.cr) + if self.column: + self.column.digits_change(env.cr) def _setup_regular(self, env): super(Float, self)._setup_regular(env) @@ -1692,11 +1682,10 @@ class Many2many(_RelationalMulti): def _setup_regular(self, env): super(Many2many, self)._setup_regular(env) - if self.store and not self.relation: - model = env[self.model_name] - column = model._columns[self.name] - if not isinstance(column, fields.function): - self.relation, self.column1, self.column2 = column._sql_names(model) + if not self.relation: + if isinstance(self.column, fields.many2many): + self.relation, self.column1, self.column2 = \ + self.column._sql_names(env[self.model_name]) if self.relation: m2m = env.registry._m2m @@ -1725,7 +1714,8 @@ class Id(Field): super(Id, self).__init__(type='integer', string=string, **kwargs) def to_column(self): - return fields.integer('ID') + self.column = fields.integer('ID') + return self.column def __get__(self, record, owner): if record is None: diff --git a/openerp/models.py b/openerp/models.py index 18acf3f38bf..48274ea5865 100644 --- a/openerp/models.py +++ b/openerp/models.py @@ -475,7 +475,7 @@ class BaseModel(object): # basic setup of field field.set_class_name(cls, name) - if field.store: + if field.store or field.column: cls._columns[name] = field.to_column() else: # remove potential column that may be overridden by field @@ -2982,7 +2982,7 @@ class BaseModel(object): # update columns (fields may have changed), and column_infos for name, field in self._fields.iteritems(): - if field.store: + if field.column: self._columns[name] = field.to_column() self._inherits_reload() @@ -3653,7 +3653,7 @@ class BaseModel(object): for key, val in vals.iteritems(): field = self._fields.get(key) if field: - if field.store or field.inherited: + if field.column or field.inherited: old_vals[key] = val if field.inverse and not field.inherited: new_vals[key] = val @@ -3954,7 +3954,7 @@ class BaseModel(object): for key, val in vals.iteritems(): field = self._fields.get(key) if field: - if field.store or field.inherited: + if field.column or field.inherited: old_vals[key] = val if field.inverse and not field.inherited: new_vals[key] = val diff --git a/openerp/osv/expression.py b/openerp/osv/expression.py index 9bf4083769d..d42544e14b8 100644 --- a/openerp/osv/expression.py +++ b/openerp/osv/expression.py @@ -855,7 +855,7 @@ class expression(object): leaf.leaf = ('id', 'in', table_ids) push(leaf) - elif not field.store: + elif not column: # Non-stored field should provide an implementation of search. if not field.search: # field does not support search! diff --git a/openerp/osv/fields.py b/openerp/osv/fields.py index 9506a5ca4fa..14e101818e5 100644 --- a/openerp/osv/fields.py +++ b/openerp/osv/fields.py @@ -1292,6 +1292,7 @@ class function(_column): def to_field_args(self): args = super(function, self).to_field_args() + args['store'] = bool(self.store) if self._type in ('float',): args['digits'] = self.digits_compute or self.digits elif self._type in ('selection', 'reference'):