diff --git a/openerp/modules/module.py b/openerp/modules/module.py index bf53ca6d87c..1514e836dd8 100644 --- a/openerp/modules/module.py +++ b/openerp/modules/module.py @@ -274,22 +274,17 @@ def init_module_models(cr, module_name, obj_list): logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: creating or updating database tables' % module_name) - # TODO _auto_init doesn't seem to return anything - # so this todo list would be useless. todo = [] for obj in obj_list: - try: - # TODO the module in the context doesn't seem usefull: - # it is available (at least) in the class' _module attribute. - # (So module_name would be useless too.) - result = obj._auto_init(cr, {'module': module_name}) - except Exception, e: - raise + result = obj._auto_init(cr, {'module': module_name}) if result: todo += result if hasattr(obj, 'init'): obj.init(cr) cr.commit() + for obj in obj_list: + obj._auto_end(cr, {'module': module_name}) + cr.commit() todo.sort() for t in todo: t[1](cr, *t[2]) diff --git a/openerp/modules/registry.py b/openerp/modules/registry.py index 3139c8606be..11f484a71a0 100644 --- a/openerp/modules/registry.py +++ b/openerp/modules/registry.py @@ -75,6 +75,11 @@ class Registry(object): for klass in openerp.osv.orm.module_class_list.get(module, []): res.append(klass.createInstance(self, cr)) + # Instanciate classes automatically discovered. + for cls in openerp.osv.orm.MetaModel.module_to_models.get(module, []): + if cls not in openerp.osv.orm.module_class_list.get(module, []): + res.append(cls.createInstance(self, cr)) + return res diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index de6885172d4..8cf06a0aa9d 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -416,6 +416,32 @@ def get_pg_type(f): return f_type +class MetaModel(type): + """ Metaclass for the Model. + + This class is used as the metaclass for the Model class to discover + the models defined in a module (i.e. without instanciating them). + If the automatic discovery is not needed, it is possible to set the + model's _register attribute to False. + + """ + + module_to_models = {} + + def __init__(self, name, bases, attrs): + if not self._register: + self._register = True + super(MetaModel, self).__init__(name, bases, attrs) + return + + module_name = self.__module__.split('.')[0] + if not hasattr(self, '_module'): + self._module = module_name + + # Remember which models to instanciate for this module. + self.module_to_models.setdefault(self._module, []).append(self) + + class orm_template(object): """ Base class for OpenERP models. @@ -445,6 +471,11 @@ class orm_template(object): _sequence = None _description = None _inherits = {} + # Mapping from inherits'd field name to triple (m, r, f) + # where m is the model from which it is inherits'd, + # r is the (local) field towards m, + # and f is the _column object itself. + _inherit_fields = {} _table = None _invalids = set() _log_create = False @@ -469,9 +500,11 @@ class orm_template(object): raise NotImplementedError(_('The read_group method is not implemented on this object !')) def _field_create(self, cr, context=None): - """ + """ Create entries in ir_model_fields for all the model's fields. - Create/update entries in ir_model, ir_model_data, and ir_model_fields. + If necessary, also create an entry in ir_model, and if called from the + modules loading scheme (by receiving 'module' in the context), also + create entries in ir_model_data (for the model and the fields). - create an entry in ir_model (if there is not already one), - create an entry in ir_model_data (if there is not already one, and if @@ -572,6 +605,9 @@ class orm_template(object): raise_on_invalid_object_name(self._name) self._field_create(cr, context=context) + def _auto_end(self, cr, context=None): + pass + # # Goal: try to apply inheritance at the instanciation level and # put objects in the pool var @@ -637,7 +673,7 @@ class orm_template(object): else: new.extend(cls.__dict__.get(s, [])) nattr[s] = new - cls = type(name, (cls, parent_class), nattr) + cls = type(name, (cls, parent_class), dict(nattr, _register=False)) obj = object.__new__(cls) obj.__init__(pool, cr) return obj @@ -1218,6 +1254,8 @@ class orm_template(object): def fields_get_keys(self, cr, user, context=None): res = self._columns.keys() + # TODO I believe this loop can be replace by + # res.extend(self._inherit_fields.key()) for parent in self._inherits: res.extend(self.pool.get(parent).fields_get_keys(cr, user, context)) return res @@ -2064,7 +2102,6 @@ class orm_template(object): class orm_memory(orm_template): _protected = ['read', 'write', 'create', 'default_get', 'perm_read', 'unlink', 'fields_get', 'fields_view_get', 'search', 'name_get', 'distinct_field_get', 'name_search', 'copy', 'import_data', 'search_count', 'exists'] - _inherit_fields = {} _max_count = config.get('osv_memory_count_limit') _max_hours = config.get('osv_memory_age_limit') _check_time = 20 @@ -2553,149 +2590,74 @@ class orm(orm_template): - alter existing database columns to match _columns, - create database tables to match _columns, - add database indices to match _columns, + - save in self._foreign_keys a list a foreign keys to create (see + _auto_end). """ + self._foreign_keys = [] raise_on_invalid_object_name(self._name) if context is None: context = {} store_compute = False - create = False todo_end = [] + update_custom_fields = context.get('update_custom_fields', False) self._field_create(cr, context=context) + create = not self._table_exist(cr) + if getattr(self, '_auto', True): - cr.execute("SELECT relname FROM pg_class WHERE relkind IN ('r','v') AND relname=%s", (self._table,)) - if not cr.rowcount: - cr.execute('CREATE TABLE "%s" (id SERIAL NOT NULL, PRIMARY KEY(id)) WITHOUT OIDS' % (self._table,)) - cr.execute("COMMENT ON TABLE \"%s\" IS '%s'" % (self._table, self._description.replace("'", "''"))) - create = True - self.__schema.debug("Table '%s': created", self._table) + + if create: + self._create_table(cr) cr.commit() if self._parent_store: - cr.execute("""SELECT c.relname - FROM pg_class c, pg_attribute a - WHERE c.relname=%s AND a.attname=%s AND c.oid=a.attrelid - """, (self._table, 'parent_left')) - if not cr.rowcount: - cr.execute('ALTER TABLE "%s" ADD COLUMN "parent_left" INTEGER' % (self._table,)) - cr.execute('ALTER TABLE "%s" ADD COLUMN "parent_right" INTEGER' % (self._table,)) - if 'parent_left' not in self._columns: - self.__logger.error('create a column parent_left on object %s: fields.integer(\'Left Parent\', select=1)', - self._table) - self.__schema.debug("Table '%s': added column '%s' with definition=%s", - self._table, 'parent_left', 'INTEGER') - elif not self._columns['parent_left'].select: - self.__logger.error('parent_left column on object %s must be indexed! Add select=1 to the field definition)', - self._table) - if 'parent_right' not in self._columns: - self.__logger.error('create a column parent_right on object %s: fields.integer(\'Right Parent\', select=1)', - self._table) - self.__schema.debug("Table '%s': added column '%s' with definition=%s", - self._table, 'parent_right', 'INTEGER') - elif not self._columns['parent_right'].select: - self.__logger.error('parent_right column on object %s must be indexed! Add select=1 to the field definition)', - self._table) - if self._columns[self._parent_name].ondelete != 'cascade': - self.__logger.error("The column %s on object %s must be set as ondelete='cascade'", - self._parent_name, self._name) - - cr.commit() + if not self._parent_columns_exist(cr): + self._create_parent_columns(cr) store_compute = True + # Create the create_uid, create_date, write_uid, write_date, columns if desired. if self._log_access: - logs = { - 'create_uid': 'INTEGER REFERENCES res_users ON DELETE SET NULL', - 'create_date': 'TIMESTAMP', - 'write_uid': 'INTEGER REFERENCES res_users ON DELETE SET NULL', - 'write_date': 'TIMESTAMP' - } - for k in logs: - cr.execute(""" - SELECT c.relname - FROM pg_class c, pg_attribute a - WHERE c.relname=%s AND a.attname=%s AND c.oid=a.attrelid - """, (self._table, k)) - if not cr.rowcount: - cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, logs[k])) - cr.commit() - self.__schema.debug("Table '%s': added column '%s' with definition=%s", - self._table, k, logs[k]) + self._add_log_columns(cr) self._check_removed_columns(cr, log=False) # iterate on the "object columns" - todo_update_store = [] - update_custom_fields = context.get('update_custom_fields', False) + column_data = self._select_column_data(cr) - cr.execute("SELECT c.relname,a.attname,a.attlen,a.atttypmod,a.attnotnull,a.atthasdef,t.typname,CASE WHEN a.attlen=-1 THEN a.atttypmod-4 ELSE a.attlen END as size " \ - "FROM pg_class c,pg_attribute a,pg_type t " \ - "WHERE c.relname=%s " \ - "AND c.oid=a.attrelid " \ - "AND a.atttypid=t.oid", (self._table,)) - col_data = dict(map(lambda x: (x['attname'], x),cr.dictfetchall())) - - - for k in self._columns: + for k, f in self._columns.iteritems(): if k in ('id', 'write_uid', 'write_date', 'create_uid', 'create_date'): continue - #Not Updating Custom fields - if k.startswith('x_') and not update_custom_fields: + # Don't update custom (also called manual) fields + if f.manual and not update_custom_fields: continue - f = self._columns[k] - if isinstance(f, fields.one2many): - cr.execute("SELECT relname FROM pg_class WHERE relkind='r' AND relname=%s", (f._obj,)) + self._o2m_raise_on_missing_reference(cr, f) - if self.pool.get(f._obj): - if f._fields_id not in self.pool.get(f._obj)._columns.keys(): - if not self.pool.get(f._obj)._inherits or (f._fields_id not in self.pool.get(f._obj)._inherit_fields.keys()): - raise except_orm('Programming Error', ("There is no reference field '%s' found for '%s'") % (f._fields_id, f._obj,)) - - if cr.fetchone(): - cr.execute("SELECT count(1) as c FROM pg_class c,pg_attribute a WHERE c.relname=%s AND a.attname=%s AND c.oid=a.attrelid", (f._obj, f._fields_id)) - res = cr.fetchone()[0] - if not res: - cr.execute('ALTER TABLE "%s" ADD FOREIGN KEY (%s) REFERENCES "%s" ON DELETE SET NULL' % (self._obj, f._fields_id, f._table)) - self.__schema.debug("Table '%s': added foreign key '%s' with definition=REFERENCES \"%s\" ON DELETE SET NULL", - self._obj, f._fields_id, f._table) elif isinstance(f, fields.many2many): - cr.execute("SELECT relname FROM pg_class WHERE relkind IN ('r','v') AND relname=%s", (f._rel,)) - if not cr.dictfetchall(): - if not self.pool.get(f._obj): - raise except_orm('Programming Error', ('There is no reference available for %s') % (f._obj,)) - ref = self.pool.get(f._obj)._table -# ref = f._obj.replace('.', '_') - cr.execute('CREATE TABLE "%s" ("%s" INTEGER NOT NULL REFERENCES "%s" ON DELETE CASCADE, "%s" INTEGER NOT NULL REFERENCES "%s" ON DELETE CASCADE, UNIQUE("%s","%s")) WITH OIDS' % (f._rel, f._id1, self._table, f._id2, ref, f._id1, f._id2)) - cr.execute('CREATE INDEX "%s_%s_index" ON "%s" ("%s")' % (f._rel, f._id1, f._rel, f._id1)) - cr.execute('CREATE INDEX "%s_%s_index" ON "%s" ("%s")' % (f._rel, f._id2, f._rel, f._id2)) - cr.execute("COMMENT ON TABLE \"%s\" IS 'RELATION BETWEEN %s AND %s'" % (f._rel, self._table, ref)) - cr.commit() - self.__schema.debug("Create table '%s': relation between '%s' and '%s'", - f._rel, self._table, ref) + self._m2m_raise_or_create_relation(cr, f) + else: - res = col_data.get(k, []) - res = res and [res] or [] + res = column_data.get(k) + + # The field is not found as-is in database, try if it + # exists with an old name. if not res and hasattr(f, 'oldname'): - cr.execute("SELECT c.relname,a.attname,a.attlen,a.atttypmod,a.attnotnull,a.atthasdef,t.typname,CASE WHEN a.attlen=-1 THEN a.atttypmod-4 ELSE a.attlen END as size " \ - "FROM pg_class c,pg_attribute a,pg_type t " \ - "WHERE c.relname=%s " \ - "AND a.attname=%s " \ - "AND c.oid=a.attrelid " \ - "AND a.atttypid=t.oid", (self._table, f.oldname)) - res_old = cr.dictfetchall() - if res_old and len(res_old) == 1: + res = column_data.get(f.oldname) + if res: cr.execute('ALTER TABLE "%s" RENAME "%s" TO "%s"' % (self._table, f.oldname, k)) - res = res_old - res[0]['attname'] = k + res['attname'] = k + column_data[k] = res self.__schema.debug("Table '%s': renamed column '%s' to '%s'", self._table, f.oldname, k) - if len(res) == 1: - f_pg_def = res[0] - f_pg_type = f_pg_def['typname'] - f_pg_size = f_pg_def['size'] - f_pg_notnull = f_pg_def['attnotnull'] + # The field already exists in database. Possibly + # change its type, rename it, drop it or change its + # constraints. + if res: + f_pg_type = res['typname'] + f_pg_size = res['size'] + f_pg_notnull = res['attnotnull'] if isinstance(f, fields.function) and not f.store and\ not getattr(f, 'nodrop', False): self.__logger.info('column %s (%s) in table %s removed: converted to a function !\n', @@ -2833,13 +2795,13 @@ class orm(orm_template): if res2: if res2[0]['confdeltype'] != POSTGRES_CONFDELTYPES.get(f.ondelete.upper(), 'a'): cr.execute('ALTER TABLE "' + self._table + '" DROP CONSTRAINT "' + res2[0]['conname'] + '"') - cr.execute('ALTER TABLE "' + self._table + '" ADD FOREIGN KEY ("' + k + '") REFERENCES "' + ref + '" ON DELETE ' + f.ondelete) + self._foreign_keys.append((self._table, k, ref, f.ondelete)) cr.commit() self.__schema.debug("Table '%s': column '%s': XXX", self._table, k) - elif len(res) > 1: - netsvc.Logger().notifyChannel('orm', netsvc.LOG_ERROR, "Programming error, column %s->%s has multiple instances !" % (self._table, k)) - if not res: + + # The field doesn't exist in database. Create it if necessary. + else: if not isinstance(f, fields.function) or f.store: # add the missing field cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, get_pg_type(f)[1])) @@ -2860,21 +2822,21 @@ class orm(orm_template): cr.commit() netsvc.Logger().notifyChannel('data', netsvc.LOG_DEBUG, "Table '%s': setting default value of new column %s" % (self._table, k)) + # remember the functions to call for the stored fields if isinstance(f, fields.function): order = 10 - if f.store is not True: + if f.store is not True: # i.e. if f.store is a dict order = f.store[f.store.keys()[0]][2] - todo_update_store.append((order, f, k)) + todo_end.append((order, self._update_store, (f, k))) # and add constraints if needed if isinstance(f, fields.many2one): if not self.pool.get(f._obj): raise except_orm('Programming Error', ('There is no reference available for %s') % (f._obj,)) ref = self.pool.get(f._obj)._table -# ref = f._obj.replace('.', '_') # ir_actions is inherited so foreign key doesn't work on it if ref != 'ir_actions': - cr.execute('ALTER TABLE "%s" ADD FOREIGN KEY ("%s") REFERENCES "%s" ON DELETE %s' % (self._table, k, ref, f.ondelete)) + self._foreign_keys.append((self._table, k, ref, f.ondelete)) self.__schema.debug("Table '%s': added foreign key '%s' with definition=REFERENCES \"%s\" ON DELETE %s", self._table, k, ref, f.ondelete) if f.select: @@ -2892,8 +2854,6 @@ class orm(orm_template): "ALTER TABLE %s ALTER COLUMN %s SET NOT NULL" self.__logger.warn(msg, k, self._table, self._table, k) cr.commit() - for order, f, k in todo_update_store: - todo_end.append((order, self._update_store, (f, k))) else: cr.execute("SELECT relname FROM pg_class WHERE relkind IN ('r','v') AND relname=%s", (self._table,)) @@ -2901,6 +2861,134 @@ class orm(orm_template): cr.commit() # start a new transaction + self._add_sql_constraints(cr) + + if create: + self._execute_sql(cr) + + if store_compute: + self._parent_store_compute(cr) + cr.commit() + + return todo_end + + + def _auto_end(self, cr, context=None): + """ Create the foreign keys recorded by _auto_init. """ + for t, k, r, d in self._foreign_keys: + cr.execute('ALTER TABLE "%s" ADD FOREIGN KEY ("%s") REFERENCES "%s" ON DELETE %s' % (t, k, r, d)) + cr.commit() + del self._foreign_keys + + + def _table_exist(self, cr): + cr.execute("SELECT relname FROM pg_class WHERE relkind IN ('r','v') AND relname=%s", (self._table,)) + return cr.rowcount + + + def _create_table(self, cr): + cr.execute('CREATE TABLE "%s" (id SERIAL NOT NULL, PRIMARY KEY(id)) WITHOUT OIDS' % (self._table,)) + cr.execute("COMMENT ON TABLE \"%s\" IS '%s'" % (self._table, self._description.replace("'", "''"))) + self.__schema.debug("Table '%s': created", self._table) + + + def _parent_columns_exist(self, cr): + cr.execute("""SELECT c.relname + FROM pg_class c, pg_attribute a + WHERE c.relname=%s AND a.attname=%s AND c.oid=a.attrelid + """, (self._table, 'parent_left')) + return cr.rowcount + + + def _create_parent_columns(self, cr): + cr.execute('ALTER TABLE "%s" ADD COLUMN "parent_left" INTEGER' % (self._table,)) + cr.execute('ALTER TABLE "%s" ADD COLUMN "parent_right" INTEGER' % (self._table,)) + if 'parent_left' not in self._columns: + self.__logger.error('create a column parent_left on object %s: fields.integer(\'Left Parent\', select=1)', + self._table) + self.__schema.debug("Table '%s': added column '%s' with definition=%s", + self._table, 'parent_left', 'INTEGER') + elif not self._columns['parent_left'].select: + self.__logger.error('parent_left column on object %s must be indexed! Add select=1 to the field definition)', + self._table) + if 'parent_right' not in self._columns: + self.__logger.error('create a column parent_right on object %s: fields.integer(\'Right Parent\', select=1)', + self._table) + self.__schema.debug("Table '%s': added column '%s' with definition=%s", + self._table, 'parent_right', 'INTEGER') + elif not self._columns['parent_right'].select: + self.__logger.error('parent_right column on object %s must be indexed! Add select=1 to the field definition)', + self._table) + if self._columns[self._parent_name].ondelete != 'cascade': + self.__logger.error("The column %s on object %s must be set as ondelete='cascade'", + self._parent_name, self._name) + + cr.commit() + + + def _add_log_columns(self, cr): + logs = { + 'create_uid': 'INTEGER REFERENCES res_users ON DELETE SET NULL', + 'create_date': 'TIMESTAMP', + 'write_uid': 'INTEGER REFERENCES res_users ON DELETE SET NULL', + 'write_date': 'TIMESTAMP' + } + for k in logs: + cr.execute(""" + SELECT c.relname + FROM pg_class c, pg_attribute a + WHERE c.relname=%s AND a.attname=%s AND c.oid=a.attrelid + """, (self._table, k)) + if not cr.rowcount: + cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, logs[k])) + cr.commit() + self.__schema.debug("Table '%s': added column '%s' with definition=%s", + self._table, k, logs[k]) + + + def _select_column_data(self, cr): + cr.execute("SELECT c.relname,a.attname,a.attlen,a.atttypmod,a.attnotnull,a.atthasdef,t.typname,CASE WHEN a.attlen=-1 THEN a.atttypmod-4 ELSE a.attlen END as size " \ + "FROM pg_class c,pg_attribute a,pg_type t " \ + "WHERE c.relname=%s " \ + "AND c.oid=a.attrelid " \ + "AND a.atttypid=t.oid", (self._table,)) + return dict(map(lambda x: (x['attname'], x),cr.dictfetchall())) + + + def _o2m_raise_on_missing_reference(self, cr, f): + # TODO this check should be a method on fields.one2many. + other = self.pool.get(f._obj) + if other: + # TODO the condition could use fields_get_keys(). + if f._fields_id not in other._columns.keys(): + if f._fields_id not in other._inherit_fields.keys(): + raise except_orm('Programming Error', ("There is no reference field '%s' found for '%s'") % (f._fields_id, f._obj,)) + + + def _m2m_raise_or_create_relation(self, cr, f): + cr.execute("SELECT relname FROM pg_class WHERE relkind IN ('r','v') AND relname=%s", (f._rel,)) + if not cr.dictfetchall(): + if not self.pool.get(f._obj): + raise except_orm('Programming Error', ('There is no reference available for %s') % (f._obj,)) + ref = self.pool.get(f._obj)._table + cr.execute('CREATE TABLE "%s" ("%s" INTEGER NOT NULL, "%s" INTEGER NOT NULL, UNIQUE("%s","%s")) WITH OIDS' % (f._rel, f._id1, f._id2, f._id1, f._id2)) + self._foreign_keys.append((f._rel, f._id1, self._table, 'CASCADE')) + self._foreign_keys.append((f._rel, f._id2, ref, 'CASCADE')) + cr.execute('CREATE INDEX "%s_%s_index" ON "%s" ("%s")' % (f._rel, f._id1, f._rel, f._id1)) + cr.execute('CREATE INDEX "%s_%s_index" ON "%s" ("%s")' % (f._rel, f._id2, f._rel, f._id2)) + cr.execute("COMMENT ON TABLE \"%s\" IS 'RELATION BETWEEN %s AND %s'" % (f._rel, self._table, ref)) + cr.commit() + self.__schema.debug("Create table '%s': relation between '%s' and '%s'", + f._rel, self._table, ref) + + + def _add_sql_constraints(self, cr): + """ + + Modify this model's database table constraints so they match the one in + _sql_constraints. + + """ for (key, con, _) in self._sql_constraints: conname = '%s_%s' % (self._table, key) @@ -2949,17 +3037,16 @@ class orm(orm_template): self.__schema.warn(sql_action['msg_err']) cr.rollback() - if create: - if hasattr(self, "_sql"): - for line in self._sql.split(';'): - line2 = line.replace('\n', '').strip() - if line2: - cr.execute(line2) - cr.commit() - if store_compute: - self._parent_store_compute(cr) - cr.commit() - return todo_end + + def _execute_sql(self, cr): + """ Execute the SQL code from the _sql attribute (if any).""" + if hasattr(self, "_sql"): + for line in self._sql.split(';'): + line2 = line.replace('\n', '').strip() + if line2: + cr.execute(line2) + cr.commit() + @classmethod def createInstance(cls, pool, cr): @@ -3038,18 +3125,18 @@ class orm(orm_template): } if field['ttype'] == 'selection': - self._columns[field['name']] = getattr(fields, field['ttype'])(eval(field['selection']), **attrs) + self._columns[field['name']] = fields.selection(eval(field['selection']), **attrs) elif field['ttype'] == 'reference': - self._columns[field['name']] = getattr(fields, field['ttype'])(selection=eval(field['selection']), **attrs) + self._columns[field['name']] = fields.reference(selection=eval(field['selection']), **attrs) elif field['ttype'] == 'many2one': - self._columns[field['name']] = getattr(fields, field['ttype'])(field['relation'], **attrs) + self._columns[field['name']] = fields.many2one(field['relation'], **attrs) elif field['ttype'] == 'one2many': - self._columns[field['name']] = getattr(fields, field['ttype'])(field['relation'], field['relation_field'], **attrs) + self._columns[field['name']] = fields.one2many(field['relation'], field['relation_field'], **attrs) elif field['ttype'] == 'many2many': _rel1 = field['relation'].replace('.', '_') _rel2 = field['model'].replace('.', '_') _rel_name = 'x_%s_%s_%s_rel' % (_rel1, _rel2, field['name']) - self._columns[field['name']] = getattr(fields, field['ttype'])(field['relation'], _rel_name, 'id1', 'id2', **attrs) + self._columns[field['name']] = fields.many2many(field['relation'], _rel_name, 'id1', 'id2', **attrs) else: self._columns[field['name']] = getattr(fields, field['ttype'])(**attrs) self._inherits_check() @@ -3068,18 +3155,25 @@ class orm(orm_template): # def _inherits_reload_src(self): + """ Recompute the _inherit_fields mapping on each _inherits'd child model.""" for obj in self.pool.models.values(): if self._name in obj._inherits: obj._inherits_reload() def _inherits_reload(self): + """ Recompute the _inherit_fields mapping. + + This will also call itself on each inherits'd child model. + + """ res = {} for table in self._inherits: - res.update(self.pool.get(table)._inherit_fields) - for col in self.pool.get(table)._columns.keys(): - res[col] = (table, self._inherits[table], self.pool.get(table)._columns[col]) - for col in self.pool.get(table)._inherit_fields.keys(): - res[col] = (table, self._inherits[table], self.pool.get(table)._inherit_fields[col][2]) + other = self.pool.get(table) + res.update(other._inherit_fields) + for col in other._columns.keys(): + res[col] = (table, self._inherits[table], other._columns[col]) + for col in other._inherit_fields.keys(): + res[col] = (table, self._inherits[table], other._inherit_fields[col][2]) self._inherit_fields = res self._inherits_reload_src() diff --git a/openerp/osv/osv.py b/openerp/osv/osv.py index c3953e40a27..acf262af343 100644 --- a/openerp/osv/osv.py +++ b/openerp/osv/osv.py @@ -31,6 +31,7 @@ import logging from psycopg2 import IntegrityError, errorcodes from openerp.tools.func import wraps from openerp.tools.translate import translate +from openerp.osv.orm import MetaModel class except_osv(Exception): @@ -202,12 +203,14 @@ class object_proxy(netsvc.Service): class osv_memory(orm.orm_memory): """ Deprecated class. """ - pass + __metaclass__ = MetaModel + _register = False # Set to false if the model shouldn't be automatically discovered. class osv(orm.orm): """ Deprecated class. """ - pass + __metaclass__ = MetaModel + _register = False # Set to false if the model shouldn't be automatically discovered. def start_object_proxy():