[MERGE] orm: _auto_init made of smaller methods, use a metaclass to discover new models.

bzr revid: vmt@openerp.com-20110615160123-7bk8u94y916mdpkj
This commit is contained in:
Vo Minh Thu 2011-06-15 18:01:23 +02:00
commit abb606aa7c
4 changed files with 253 additions and 156 deletions

View File

@ -274,22 +274,17 @@ def init_module_models(cr, module_name, obj_list):
logger.notifyChannel('init', netsvc.LOG_INFO, logger.notifyChannel('init', netsvc.LOG_INFO,
'module %s: creating or updating database tables' % module_name) '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 = [] todo = []
for obj in obj_list: for obj in obj_list:
try: result = obj._auto_init(cr, {'module': module_name})
# 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
if result: if result:
todo += result todo += result
if hasattr(obj, 'init'): if hasattr(obj, 'init'):
obj.init(cr) obj.init(cr)
cr.commit() cr.commit()
for obj in obj_list:
obj._auto_end(cr, {'module': module_name})
cr.commit()
todo.sort() todo.sort()
for t in todo: for t in todo:
t[1](cr, *t[2]) t[1](cr, *t[2])

View File

@ -75,6 +75,11 @@ class Registry(object):
for klass in openerp.osv.orm.module_class_list.get(module, []): for klass in openerp.osv.orm.module_class_list.get(module, []):
res.append(klass.createInstance(self, cr)) 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 return res

View File

@ -416,6 +416,32 @@ def get_pg_type(f):
return f_type 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): class orm_template(object):
""" Base class for OpenERP models. """ Base class for OpenERP models.
@ -445,6 +471,11 @@ class orm_template(object):
_sequence = None _sequence = None
_description = None _description = None
_inherits = {} _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 _table = None
_invalids = set() _invalids = set()
_log_create = False _log_create = False
@ -469,9 +500,11 @@ class orm_template(object):
raise NotImplementedError(_('The read_group method is not implemented on this object !')) raise NotImplementedError(_('The read_group method is not implemented on this object !'))
def _field_create(self, cr, context=None): 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 (if there is not already one),
- create an entry in ir_model_data (if there is not already one, and if - 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) raise_on_invalid_object_name(self._name)
self._field_create(cr, context=context) self._field_create(cr, context=context)
def _auto_end(self, cr, context=None):
pass
# #
# Goal: try to apply inheritance at the instanciation level and # Goal: try to apply inheritance at the instanciation level and
# put objects in the pool var # put objects in the pool var
@ -637,7 +673,7 @@ class orm_template(object):
else: else:
new.extend(cls.__dict__.get(s, [])) new.extend(cls.__dict__.get(s, []))
nattr[s] = new 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 = object.__new__(cls)
obj.__init__(pool, cr) obj.__init__(pool, cr)
return obj return obj
@ -1218,6 +1254,8 @@ class orm_template(object):
def fields_get_keys(self, cr, user, context=None): def fields_get_keys(self, cr, user, context=None):
res = self._columns.keys() res = self._columns.keys()
# TODO I believe this loop can be replace by
# res.extend(self._inherit_fields.key())
for parent in self._inherits: for parent in self._inherits:
res.extend(self.pool.get(parent).fields_get_keys(cr, user, context)) res.extend(self.pool.get(parent).fields_get_keys(cr, user, context))
return res return res
@ -2064,7 +2102,6 @@ class orm_template(object):
class orm_memory(orm_template): 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'] _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_count = config.get('osv_memory_count_limit')
_max_hours = config.get('osv_memory_age_limit') _max_hours = config.get('osv_memory_age_limit')
_check_time = 20 _check_time = 20
@ -2553,149 +2590,74 @@ class orm(orm_template):
- alter existing database columns to match _columns, - alter existing database columns to match _columns,
- create database tables to match _columns, - create database tables to match _columns,
- add database indices 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) raise_on_invalid_object_name(self._name)
if context is None: if context is None:
context = {} context = {}
store_compute = False store_compute = False
create = False
todo_end = [] todo_end = []
update_custom_fields = context.get('update_custom_fields', False)
self._field_create(cr, context=context) self._field_create(cr, context=context)
create = not self._table_exist(cr)
if getattr(self, '_auto', True): 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: if create:
cr.execute('CREATE TABLE "%s" (id SERIAL NOT NULL, PRIMARY KEY(id)) WITHOUT OIDS' % (self._table,)) self._create_table(cr)
cr.execute("COMMENT ON TABLE \"%s\" IS '%s'" % (self._table, self._description.replace("'", "''")))
create = True
self.__schema.debug("Table '%s': created", self._table)
cr.commit() cr.commit()
if self._parent_store: if self._parent_store:
cr.execute("""SELECT c.relname if not self._parent_columns_exist(cr):
FROM pg_class c, pg_attribute a self._create_parent_columns(cr)
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()
store_compute = True store_compute = True
# Create the create_uid, create_date, write_uid, write_date, columns if desired.
if self._log_access: if self._log_access:
logs = { self._add_log_columns(cr)
'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._check_removed_columns(cr, log=False) self._check_removed_columns(cr, log=False)
# iterate on the "object columns" # iterate on the "object columns"
todo_update_store = [] column_data = self._select_column_data(cr)
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 " \ for k, f in self._columns.iteritems():
"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:
if k in ('id', 'write_uid', 'write_date', 'create_uid', 'create_date'): if k in ('id', 'write_uid', 'write_date', 'create_uid', 'create_date'):
continue continue
#Not Updating Custom fields # Don't update custom (also called manual) fields
if k.startswith('x_') and not update_custom_fields: if f.manual and not update_custom_fields:
continue continue
f = self._columns[k]
if isinstance(f, fields.one2many): 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): elif isinstance(f, fields.many2many):
cr.execute("SELECT relname FROM pg_class WHERE relkind IN ('r','v') AND relname=%s", (f._rel,)) self._m2m_raise_or_create_relation(cr, f)
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)
else: else:
res = col_data.get(k, []) res = column_data.get(k)
res = res and [res] or []
# The field is not found as-is in database, try if it
# exists with an old name.
if not res and hasattr(f, 'oldname'): 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 " \ res = column_data.get(f.oldname)
"FROM pg_class c,pg_attribute a,pg_type t " \ if res:
"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:
cr.execute('ALTER TABLE "%s" RENAME "%s" TO "%s"' % (self._table, f.oldname, k)) cr.execute('ALTER TABLE "%s" RENAME "%s" TO "%s"' % (self._table, f.oldname, k))
res = res_old res['attname'] = k
res[0]['attname'] = k column_data[k] = res
self.__schema.debug("Table '%s': renamed column '%s' to '%s'", self.__schema.debug("Table '%s': renamed column '%s' to '%s'",
self._table, f.oldname, k) self._table, f.oldname, k)
if len(res) == 1: # The field already exists in database. Possibly
f_pg_def = res[0] # change its type, rename it, drop it or change its
f_pg_type = f_pg_def['typname'] # constraints.
f_pg_size = f_pg_def['size'] if res:
f_pg_notnull = f_pg_def['attnotnull'] 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\ if isinstance(f, fields.function) and not f.store and\
not getattr(f, 'nodrop', False): not getattr(f, 'nodrop', False):
self.__logger.info('column %s (%s) in table %s removed: converted to a function !\n', 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:
if res2[0]['confdeltype'] != POSTGRES_CONFDELTYPES.get(f.ondelete.upper(), 'a'): 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 + '" 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() cr.commit()
self.__schema.debug("Table '%s': column '%s': XXX", self.__schema.debug("Table '%s': column '%s': XXX",
self._table, k) 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)) # The field doesn't exist in database. Create it if necessary.
if not res: else:
if not isinstance(f, fields.function) or f.store: if not isinstance(f, fields.function) or f.store:
# add the missing field # add the missing field
cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, get_pg_type(f)[1])) 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() cr.commit()
netsvc.Logger().notifyChannel('data', netsvc.LOG_DEBUG, "Table '%s': setting default value of new column %s" % (self._table, k)) 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): if isinstance(f, fields.function):
order = 10 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] 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 # and add constraints if needed
if isinstance(f, fields.many2one): if isinstance(f, fields.many2one):
if not self.pool.get(f._obj): if not self.pool.get(f._obj):
raise except_orm('Programming Error', ('There is no reference available for %s') % (f._obj,)) raise except_orm('Programming Error', ('There is no reference available for %s') % (f._obj,))
ref = self.pool.get(f._obj)._table ref = self.pool.get(f._obj)._table
# ref = f._obj.replace('.', '_')
# ir_actions is inherited so foreign key doesn't work on it # ir_actions is inherited so foreign key doesn't work on it
if ref != 'ir_actions': 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.__schema.debug("Table '%s': added foreign key '%s' with definition=REFERENCES \"%s\" ON DELETE %s",
self._table, k, ref, f.ondelete) self._table, k, ref, f.ondelete)
if f.select: if f.select:
@ -2892,8 +2854,6 @@ class orm(orm_template):
"ALTER TABLE %s ALTER COLUMN %s SET NOT NULL" "ALTER TABLE %s ALTER COLUMN %s SET NOT NULL"
self.__logger.warn(msg, k, self._table, self._table, k) self.__logger.warn(msg, k, self._table, self._table, k)
cr.commit() cr.commit()
for order, f, k in todo_update_store:
todo_end.append((order, self._update_store, (f, k)))
else: else:
cr.execute("SELECT relname FROM pg_class WHERE relkind IN ('r','v') AND relname=%s", (self._table,)) 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 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: for (key, con, _) in self._sql_constraints:
conname = '%s_%s' % (self._table, key) conname = '%s_%s' % (self._table, key)
@ -2949,17 +3037,16 @@ class orm(orm_template):
self.__schema.warn(sql_action['msg_err']) self.__schema.warn(sql_action['msg_err'])
cr.rollback() cr.rollback()
if create:
if hasattr(self, "_sql"): def _execute_sql(self, cr):
for line in self._sql.split(';'): """ Execute the SQL code from the _sql attribute (if any)."""
line2 = line.replace('\n', '').strip() if hasattr(self, "_sql"):
if line2: for line in self._sql.split(';'):
cr.execute(line2) line2 = line.replace('\n', '').strip()
cr.commit() if line2:
if store_compute: cr.execute(line2)
self._parent_store_compute(cr) cr.commit()
cr.commit()
return todo_end
@classmethod @classmethod
def createInstance(cls, pool, cr): def createInstance(cls, pool, cr):
@ -3038,18 +3125,18 @@ class orm(orm_template):
} }
if field['ttype'] == 'selection': 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': 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': 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': 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': elif field['ttype'] == 'many2many':
_rel1 = field['relation'].replace('.', '_') _rel1 = field['relation'].replace('.', '_')
_rel2 = field['model'].replace('.', '_') _rel2 = field['model'].replace('.', '_')
_rel_name = 'x_%s_%s_%s_rel' % (_rel1, _rel2, field['name']) _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: else:
self._columns[field['name']] = getattr(fields, field['ttype'])(**attrs) self._columns[field['name']] = getattr(fields, field['ttype'])(**attrs)
self._inherits_check() self._inherits_check()
@ -3068,18 +3155,25 @@ class orm(orm_template):
# #
def _inherits_reload_src(self): def _inherits_reload_src(self):
""" Recompute the _inherit_fields mapping on each _inherits'd child model."""
for obj in self.pool.models.values(): for obj in self.pool.models.values():
if self._name in obj._inherits: if self._name in obj._inherits:
obj._inherits_reload() obj._inherits_reload()
def _inherits_reload(self): def _inherits_reload(self):
""" Recompute the _inherit_fields mapping.
This will also call itself on each inherits'd child model.
"""
res = {} res = {}
for table in self._inherits: for table in self._inherits:
res.update(self.pool.get(table)._inherit_fields) other = self.pool.get(table)
for col in self.pool.get(table)._columns.keys(): res.update(other._inherit_fields)
res[col] = (table, self._inherits[table], self.pool.get(table)._columns[col]) for col in other._columns.keys():
for col in self.pool.get(table)._inherit_fields.keys(): res[col] = (table, self._inherits[table], other._columns[col])
res[col] = (table, self._inherits[table], self.pool.get(table)._inherit_fields[col][2]) for col in other._inherit_fields.keys():
res[col] = (table, self._inherits[table], other._inherit_fields[col][2])
self._inherit_fields = res self._inherit_fields = res
self._inherits_reload_src() self._inherits_reload_src()

View File

@ -31,6 +31,7 @@ import logging
from psycopg2 import IntegrityError, errorcodes from psycopg2 import IntegrityError, errorcodes
from openerp.tools.func import wraps from openerp.tools.func import wraps
from openerp.tools.translate import translate from openerp.tools.translate import translate
from openerp.osv.orm import MetaModel
class except_osv(Exception): class except_osv(Exception):
@ -202,12 +203,14 @@ class object_proxy(netsvc.Service):
class osv_memory(orm.orm_memory): class osv_memory(orm.orm_memory):
""" Deprecated class. """ """ Deprecated class. """
pass __metaclass__ = MetaModel
_register = False # Set to false if the model shouldn't be automatically discovered.
class osv(orm.orm): class osv(orm.orm):
""" Deprecated class. """ """ Deprecated class. """
pass __metaclass__ = MetaModel
_register = False # Set to false if the model shouldn't be automatically discovered.
def start_object_proxy(): def start_object_proxy():