Vo Minh Thu (OpenERP) has proposed merging
lp:~openerp-dev/openobject-server/trunk-register-simpler-vmt into
lp:openobject-server.
Requested reviews:
OpenERP Core Team (openerp)
For more details, see:
https://code.launchpad.net/~openerp-dev/openobject-server/trunk-register-simpler-vmt/+merge/64687
--
https://code.launchpad.net/~openerp-dev/openobject-server/trunk-register-simpler-vmt/+merge/64687
Your team OpenERP R&D Team is subscribed to branch
lp:~openerp-dev/openobject-server/trunk-register-simpler-vmt.
=== modified file 'openerp/modules/module.py'
--- openerp/modules/module.py 2011-05-11 17:24:48 +0000
+++ openerp/modules/module.py 2011-06-15 13:31:30 +0000
@@ -274,22 +274,17 @@
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])
=== modified file 'openerp/osv/orm.py'
--- openerp/osv/orm.py 2011-06-10 17:31:30 +0000
+++ openerp/osv/orm.py 2011-06-15 13:31:30 +0000
@@ -416,6 +416,32 @@
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 @@
_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 @@
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 @@
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 @@
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
@@ -1222,6 +1258,8 @@
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
@@ -2070,7 +2108,6 @@
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
@@ -2559,149 +2596,74 @@
- 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)
-
- 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:
+ column_data = self._select_column_data(cr)
+
+ 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,))
-
- 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)
+ self._o2m_raise_on_missing_reference(cr, f)
+
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',
@@ -2839,13 +2801,13 @@
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]))
@@ -2866,21 +2828,21 @@
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:
@@ -2898,8 +2860,6 @@
"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,))
@@ -2907,6 +2867,134 @@
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)
@@ -2955,17 +3043,16 @@
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):
@@ -3044,18 +3131,18 @@
}
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()
@@ -3074,18 +3161,25 @@
#
def _inherits_reload_src(self):
+ """ Recompute the _inherit_fields mapping on each _inherits'd child model."""
for obj in self.pool.obj_pool.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()
=== modified file 'openerp/osv/osv.py'
--- openerp/osv/osv.py 2011-06-01 09:49:08 +0000
+++ openerp/osv/osv.py 2011-06-15 13:31:30 +0000
@@ -35,6 +35,8 @@
from openerp.tools.func import wraps
from openerp.tools.translate import translate
from openerp.osv.orm import module_class_list
+from openerp.osv.orm import MetaModel
+
class except_osv(Exception):
def __init__(self, name, value, exc_type='warning'):
@@ -245,17 +247,24 @@
for klass in module_class_list.get(module, []):
res.append(klass.createInstance(self, cr))
+ # Instanciate classes automatically discovered.
+ for cls in MetaModel.module_to_models.get(module, []):
+ if cls not in module_class_list.get(module, []):
+ res.append(cls.createInstance(self, cr))
+
return res
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():
_______________________________________________
Mailing list: https://launchpad.net/~openerp-dev-gtk
Post to : [email protected]
Unsubscribe : https://launchpad.net/~openerp-dev-gtk
More help : https://help.launchpad.net/ListHelp