[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
This commit is contained in:
parent
7e454beff1
commit
5eb6e58156
|
@ -248,7 +248,7 @@ class Field(object):
|
||||||
|
|
||||||
automatic = False # whether the field is automatically created ("magic" field)
|
automatic = False # whether the field is automatically created ("magic" field)
|
||||||
inherited = False # whether the field is inherited (_inherits)
|
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
|
setup_done = False # whether the field has been set up
|
||||||
|
|
||||||
name = None # name of the field
|
name = None # name of the field
|
||||||
|
@ -314,6 +314,10 @@ class Field(object):
|
||||||
# by default, related fields are not stored
|
# by default, related fields are not stored
|
||||||
attrs['store'] = attrs.get('store', False)
|
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():
|
for attr, value in attrs.iteritems():
|
||||||
if not hasattr(self, attr):
|
if not hasattr(self, attr):
|
||||||
self._free_attrs.append(attr)
|
self._free_attrs.append(attr)
|
||||||
|
@ -443,7 +447,7 @@ class Field(object):
|
||||||
self.depends = ('.'.join(self.related),)
|
self.depends = ('.'.join(self.related),)
|
||||||
self.compute = self._compute_related
|
self.compute = self._compute_related
|
||||||
self.inverse = self._inverse_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
|
# allow searching on self only if the related field is searchable
|
||||||
self.search = self._search_related
|
self.search = self._search_related
|
||||||
|
|
||||||
|
@ -575,30 +579,7 @@ class Field(object):
|
||||||
return desc
|
return desc
|
||||||
|
|
||||||
# properties used by get_description()
|
# properties used by get_description()
|
||||||
|
_description_store = property(attrgetter('store'))
|
||||||
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_manual = property(attrgetter('manual'))
|
_description_manual = property(attrgetter('manual'))
|
||||||
_description_depends = property(attrgetter('depends'))
|
_description_depends = property(attrgetter('depends'))
|
||||||
_description_related = property(attrgetter('related'))
|
_description_related = property(attrgetter('related'))
|
||||||
|
@ -610,6 +591,14 @@ class Field(object):
|
||||||
_description_change_default = property(attrgetter('change_default'))
|
_description_change_default = property(attrgetter('change_default'))
|
||||||
_description_deprecated = property(attrgetter('deprecated'))
|
_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):
|
def _description_string(self, env):
|
||||||
if self.string and env.lang:
|
if self.string and env.lang:
|
||||||
name = "%s,%s" % (self.model_name, self.name)
|
name = "%s,%s" % (self.model_name, self.name)
|
||||||
|
@ -631,7 +620,7 @@ class Field(object):
|
||||||
|
|
||||||
def to_column(self):
|
def to_column(self):
|
||||||
""" return a low-level field object corresponding to `self` """
|
""" return a low-level field object corresponding to `self` """
|
||||||
assert self.store
|
assert self.store or self.column
|
||||||
|
|
||||||
# determine column parameters
|
# determine column parameters
|
||||||
_logger.debug("Create fields._column for Field %s", self)
|
_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
|
# company-dependent fields are mapped to former property fields
|
||||||
args['type'] = self.type
|
args['type'] = self.type
|
||||||
args['relation'] = self.comodel_name
|
args['relation'] = self.comodel_name
|
||||||
return fields.property(**args)
|
self.column = fields.property(**args)
|
||||||
|
elif self.column:
|
||||||
if self.column:
|
|
||||||
# let the column provide a valid column for the given parameters
|
# 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
|
# properties used by to_column() to create a column instance
|
||||||
_column_copy = property(attrgetter('copy'))
|
_column_copy = property(attrgetter('copy'))
|
||||||
|
@ -821,8 +812,8 @@ class Field(object):
|
||||||
""" Determine the value of `self` for `record`. """
|
""" Determine the value of `self` for `record`. """
|
||||||
env = record.env
|
env = record.env
|
||||||
|
|
||||||
if self.store and not (self.depends and env.in_draft):
|
if self.column and not (self.depends and env.in_draft):
|
||||||
# this is a stored field
|
# this is a stored field or an old-style function field
|
||||||
if self.depends:
|
if self.depends:
|
||||||
# this is a stored computed field, check for recomputation
|
# this is a stored computed field, check for recomputation
|
||||||
recs = record._recompute_check(self)
|
recs = record._recompute_check(self)
|
||||||
|
@ -979,9 +970,8 @@ class Float(Field):
|
||||||
if self.digits:
|
if self.digits:
|
||||||
assert isinstance(self.digits, (tuple, list)) and len(self.digits) >= 2, \
|
assert isinstance(self.digits, (tuple, list)) and len(self.digits) >= 2, \
|
||||||
"Float field %s with digits %r, expecting (total, decimal)" % (self, self.digits)
|
"Float field %s with digits %r, expecting (total, decimal)" % (self, self.digits)
|
||||||
if self.store:
|
if self.column:
|
||||||
column = env[self.model_name]._columns[self.name]
|
self.column.digits_change(env.cr)
|
||||||
column.digits_change(env.cr)
|
|
||||||
|
|
||||||
def _setup_regular(self, env):
|
def _setup_regular(self, env):
|
||||||
super(Float, self)._setup_regular(env)
|
super(Float, self)._setup_regular(env)
|
||||||
|
@ -1692,11 +1682,10 @@ class Many2many(_RelationalMulti):
|
||||||
def _setup_regular(self, env):
|
def _setup_regular(self, env):
|
||||||
super(Many2many, self)._setup_regular(env)
|
super(Many2many, self)._setup_regular(env)
|
||||||
|
|
||||||
if self.store and not self.relation:
|
if not self.relation:
|
||||||
model = env[self.model_name]
|
if isinstance(self.column, fields.many2many):
|
||||||
column = model._columns[self.name]
|
self.relation, self.column1, self.column2 = \
|
||||||
if not isinstance(column, fields.function):
|
self.column._sql_names(env[self.model_name])
|
||||||
self.relation, self.column1, self.column2 = column._sql_names(model)
|
|
||||||
|
|
||||||
if self.relation:
|
if self.relation:
|
||||||
m2m = env.registry._m2m
|
m2m = env.registry._m2m
|
||||||
|
@ -1725,7 +1714,8 @@ 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):
|
||||||
return fields.integer('ID')
|
self.column = fields.integer('ID')
|
||||||
|
return self.column
|
||||||
|
|
||||||
def __get__(self, record, owner):
|
def __get__(self, record, owner):
|
||||||
if record is None:
|
if record is None:
|
||||||
|
|
|
@ -475,7 +475,7 @@ class BaseModel(object):
|
||||||
# basic setup of field
|
# basic setup of field
|
||||||
field.set_class_name(cls, name)
|
field.set_class_name(cls, name)
|
||||||
|
|
||||||
if field.store:
|
if field.store or field.column:
|
||||||
cls._columns[name] = field.to_column()
|
cls._columns[name] = field.to_column()
|
||||||
else:
|
else:
|
||||||
# remove potential column that may be overridden by field
|
# 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
|
# update columns (fields may have changed), and column_infos
|
||||||
for name, field in self._fields.iteritems():
|
for name, field in self._fields.iteritems():
|
||||||
if field.store:
|
if field.column:
|
||||||
self._columns[name] = field.to_column()
|
self._columns[name] = field.to_column()
|
||||||
self._inherits_reload()
|
self._inherits_reload()
|
||||||
|
|
||||||
|
@ -3653,7 +3653,7 @@ class BaseModel(object):
|
||||||
for key, val in vals.iteritems():
|
for key, val in vals.iteritems():
|
||||||
field = self._fields.get(key)
|
field = self._fields.get(key)
|
||||||
if field:
|
if field:
|
||||||
if field.store or field.inherited:
|
if field.column or field.inherited:
|
||||||
old_vals[key] = val
|
old_vals[key] = val
|
||||||
if field.inverse and not field.inherited:
|
if field.inverse and not field.inherited:
|
||||||
new_vals[key] = val
|
new_vals[key] = val
|
||||||
|
@ -3954,7 +3954,7 @@ class BaseModel(object):
|
||||||
for key, val in vals.iteritems():
|
for key, val in vals.iteritems():
|
||||||
field = self._fields.get(key)
|
field = self._fields.get(key)
|
||||||
if field:
|
if field:
|
||||||
if field.store or field.inherited:
|
if field.column or field.inherited:
|
||||||
old_vals[key] = val
|
old_vals[key] = val
|
||||||
if field.inverse and not field.inherited:
|
if field.inverse and not field.inherited:
|
||||||
new_vals[key] = val
|
new_vals[key] = val
|
||||||
|
|
|
@ -855,7 +855,7 @@ class expression(object):
|
||||||
leaf.leaf = ('id', 'in', table_ids)
|
leaf.leaf = ('id', 'in', table_ids)
|
||||||
push(leaf)
|
push(leaf)
|
||||||
|
|
||||||
elif not field.store:
|
elif not column:
|
||||||
# Non-stored field should provide an implementation of search.
|
# Non-stored field should provide an implementation of search.
|
||||||
if not field.search:
|
if not field.search:
|
||||||
# field does not support search!
|
# field does not support search!
|
||||||
|
|
|
@ -1292,6 +1292,7 @@ class function(_column):
|
||||||
|
|
||||||
def to_field_args(self):
|
def to_field_args(self):
|
||||||
args = super(function, self).to_field_args()
|
args = super(function, self).to_field_args()
|
||||||
|
args['store'] = bool(self.store)
|
||||||
if self._type in ('float',):
|
if self._type in ('float',):
|
||||||
args['digits'] = self.digits_compute or self.digits
|
args['digits'] = self.digits_compute or self.digits
|
||||||
elif self._type in ('selection', 'reference'):
|
elif self._type in ('selection', 'reference'):
|
||||||
|
|
Loading…
Reference in New Issue