[MERGE] osv: osv classes merged inside orm classes.

bzr revid: vmt@openerp.com-20110530074205-98kdkwhpbu9e19qw
This commit is contained in:
Vo Minh Thu 2011-05-30 09:42:05 +02:00
commit 52731ee4b8
4 changed files with 184 additions and 138 deletions

View File

@ -162,7 +162,7 @@ class ir_model(osv.osv):
pass
x_custom_model._name = model
x_custom_model._module = False
a = x_custom_model.createInstance(self.pool, '', cr)
a = x_custom_model.createInstance(self.pool, cr)
if (not a._columns) or ('x_name' in a._columns.keys()):
x_name = 'x_name'
else:

View File

@ -90,7 +90,7 @@ class RegistryManager(object):
cr = db.cursor()
try:
pool.init_set(cr, False)
pool.do_parent_store(cr)
pool.get('ir.actions.report.xml').register_all(cr)
cr.commit()
finally:

View File

@ -65,6 +65,9 @@ from openerp.tools import SKIPPED_ELEMENT_TYPES
regex_order = re.compile('^(([a-z0-9_]+|"[a-z0-9_]+")( *desc| *asc)?( *, *|))+$', re.I)
regex_object_name = re.compile(r'^[a-z0-9_.]+$')
# Mapping between openerp module names and their osv classes.
module_class_list = {}
def check_object_name(name):
""" Check if the given name is a valid openerp object name.
@ -414,6 +417,21 @@ def get_pg_type(f):
class orm_template(object):
""" Base class for OpenERP models.
OpenERP models are created by inheriting from this class (although
not directly; more specifically by inheriting from osv or
osv_memory). The constructor is called once, usually directly
after the class definition, e.g.:
class user(osv):
...
user()
The system will later instanciate the class once per database (on
which the class' module is installed).
"""
_name = None
_columns = {}
_constraints = []
@ -451,6 +469,18 @@ class orm_template(object):
raise NotImplementedError(_('The read_group method is not implemented on this object !'))
def _field_create(self, cr, context=None):
"""
Create/update entries in ir_model, ir_model_data, and ir_model_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
'module' is in the context),
- update ir_model_fields with the fields found in _columns
(TODO there is some redundancy as _columns is updated from
ir_model_fields in __init__).
"""
if context is None:
context = {}
cr.execute("SELECT id FROM ir_model WHERE model=%s", (self._name,))
@ -542,7 +572,104 @@ class orm_template(object):
raise_on_invalid_object_name(self._name)
self._field_create(cr, context=context)
def __init__(self, cr):
#
# Goal: try to apply inheritance at the instanciation level and
# put objects in the pool var
#
@classmethod
def makeInstance(cls, pool, cr, attributes):
""" Instanciate a given model.
This class method instanciates the class of some model (i.e. a class
deriving from osv or osv_memory). The class might be the class passed
in argument or, if it inherits from another class, a class constructed
by combining the two classes.
The ``attributes`` argument specifies which parent class attributes
have to be combined.
TODO: the creation of the combined class is repeated at each call of
this method. This is probably unnecessary.
"""
parent_names = getattr(cls, '_inherit', None)
if parent_names:
if isinstance(parent_names, (str, unicode)):
name = cls._name or parent_names
parent_names = [parent_names]
else:
name = cls._name
if not name:
raise TypeError('_name is mandatory in case of multiple inheritance')
for parent_name in ((type(parent_names)==list) and parent_names or [parent_names]):
parent_class = pool.get(parent_name).__class__
if not pool.get(parent_name):
raise TypeError('The model "%s" specifies an unexisting parent class "%s"\n'
'You may need to add a dependency on the parent class\' module.' % (name, parent_name))
nattr = {}
for s in attributes:
new = copy.copy(getattr(pool.get(parent_name), s))
if s == '_columns':
# Don't _inherit custom fields.
for c in new.keys():
if new[c].manual:
del new[c]
if hasattr(new, 'update'):
new.update(cls.__dict__.get(s, {}))
elif s=='_constraints':
for c in cls.__dict__.get(s, []):
exist = False
for c2 in range(len(new)):
#For _constraints, we should check field and methods as well
if new[c2][2]==c[2] and (new[c2][0] == c[0] \
or getattr(new[c2][0],'__name__', True) == \
getattr(c[0],'__name__', False)):
# If new class defines a constraint with
# same function name, we let it override
# the old one.
new[c2] = c
exist = True
break
if not exist:
new.append(c)
else:
new.extend(cls.__dict__.get(s, []))
nattr[s] = new
cls = type(name, (cls, parent_class), nattr)
obj = object.__new__(cls)
obj.__init__(pool, cr)
return obj
def __new__(cls):
""" Register this model.
This doesn't create an instance but simply register the model
as being part of the module where it is defined.
TODO make it possible to not even have to call the constructor
to be registered.
"""
# Set the module name (e.g. base, sale, accounting, ...) on the class.
module = cls.__module__.split('.')[0]
if not hasattr(cls, '_module'):
cls._module = module
# Remember which models to instanciate for this module.
module_class_list.setdefault(cls._module, []).append(cls)
# Since we don't return an instance here, the __init__
# method won't be called.
return None
def __init__(self, pool, cr):
""" Initialize a model and make it part of the given registry."""
pool.add(self._name, self)
self.pool = pool
if not self._name and not hasattr(self, '_inherit'):
name = type(self).__name__.split('.')[0]
msg = "The class %s has to have a _name attribute" % name
@ -1931,8 +2058,12 @@ class orm_memory(orm_template):
_max_hours = config.get('osv_memory_age_limit')
_check_time = 20
def __init__(self, cr):
super(orm_memory, self).__init__(cr)
@classmethod
def createInstance(cls, pool, cr):
return cls.makeInstance(pool, cr, ['_columns', '_defaults'])
def __init__(self, pool, cr):
super(orm_memory, self).__init__(pool, cr)
self.datas = {}
self.next_id = 0
self.check_id = 0
@ -2398,6 +2529,21 @@ class orm(orm_template):
self._table, column['attname'])
def _auto_init(self, cr, context=None):
"""
Call _field_create and, unless _auto is False:
- create the corresponding table in database for the model,
- possibly add the parent columns in database,
- possibly add the columns 'create_uid', 'create_date', 'write_uid',
'write_date' in database if _log_access is True (the default),
- report on database columns no more existing in _columns,
- remove no more existing not null constraints,
- alter existing database columns to match _columns,
- create database tables to match _columns,
- add database indices to match _columns,
"""
raise_on_invalid_object_name(self._name)
if context is None:
context = {}
@ -2804,8 +2950,22 @@ class orm(orm_template):
cr.commit()
return todo_end
def __init__(self, cr):
super(orm, self).__init__(cr)
@classmethod
def createInstance(cls, pool, cr):
return cls.makeInstance(pool, cr, ['_columns', '_defaults',
'_inherits', '_constraints', '_sql_constraints'])
def __init__(self, pool, cr):
"""
- copy the stored fields' functions in the osv_pool,
- update the _columns with the fields found in ir_model_fields,
- ensure there is a many2one for each _inherits'd parent,
- update the children's _columns,
- give a chance to each field to initialize itself.
"""
super(orm, self).__init__(pool, cr)
if not hasattr(self, '_log_access'):
# if not access is not specify, it is the same value as _auto
@ -2890,6 +3050,8 @@ class orm(orm_template):
for f in self._columns:
self._columns[f].restart()
__init__.__doc__ = orm_template.__init__.__doc__ + __init__.__doc__
#
# Update objects that uses this one to update their _inherits fields
#

View File

@ -34,9 +34,7 @@ import logging
from psycopg2 import IntegrityError, errorcodes
from openerp.tools.func import wraps
from openerp.tools.translate import translate
# Mapping between openerp module names and their osv classes.
module_class_list = {}
from openerp.osv.orm import module_class_list
class except_osv(Exception):
def __init__(self, name, value, exc_type='warning'):
@ -119,7 +117,7 @@ class object_proxy(netsvc.Service):
return tr(src, 'code')
try:
if not pooler.get_pool(dbname)._ready:
if pooler.get_pool(dbname)._init:
raise except_osv('Database not ready', 'Currently, this database is not fully loaded and can not be used.')
return f(self, dbname, *args, **kwargs)
except orm.except_orm, inst:
@ -214,25 +212,16 @@ class osv_pool(object):
"""
def __init__(self):
self._ready = False
self.obj_pool = {} # model name/model instance mapping
self._sql_error = {}
self._store_function = {}
self._init = True
self._init_parent = {}
def init_set(self, cr, mode):
different = mode != self._init
if different:
if mode:
self._init_parent = {}
if not mode:
for o in self._init_parent:
self.get(o)._parent_store_compute(cr)
self._init = mode
self._ready = True
return different
def do_parent_store(self, cr):
for o in self._init_parent:
self.get(o)._parent_store_compute(cr)
self._init = False
def obj_list(self):
""" Return the list of model names in this registry."""
@ -246,132 +235,27 @@ class osv_pool(object):
""" Return a model for a given name or None if it doesn't exist."""
return self.obj_pool.get(name)
#TODO: pass a list of modules to load
def instanciate(self, module, cr):
""" Instanciate all the classes of a given module for a particular db."""
res = []
# instanciate classes registered through their constructor
# Instanciate classes registered through their constructor and
# add them to the pool.
for klass in module_class_list.get(module, []):
res.append(klass.createInstance(self, module, cr))
res.append(klass.createInstance(self, cr))
return res
class osv_base(object):
""" Base class for openerp models.
OpenERP models are created by inheriting from this class (although
not directly; more specifically by inheriting from osv or
osv_memory). The constructor is called once, usually directly
after the class definition, e.g.:
class user(osv):
...
user()
The system will later instanciate the class once per database (on
which the class' module is installed).
"""
def __init__(self, pool, cr):
""" Initialize a model and make it part of the given registry."""
pool.add(self._name, self)
self.pool = pool
super(osv_base, self).__init__(cr)
def __new__(cls):
""" Register this model.
This doesn't create an instance but simply register the model
as being part of the module where it is defined.
TODO make it possible to not even have to call the constructor
to be registered.
"""
# Set the module name (e.g. base, sale, accounting, ...) on the class.
module = cls.__module__.split('.')[0]
if not hasattr(cls, '_module'):
cls._module = module
# Remember which models to instanciate for this module.
module_class_list.setdefault(cls._module, []).append(cls)
# Since we don't return an instance here, the __init__
# method won't be called.
return None
#
# Goal: try to apply inheritance at the instanciation level and
# put objects in the pool var
#
@classmethod
def makeInstance(cls, pool, module, cr, attributes):
parent_names = getattr(cls, '_inherit', None)
if parent_names:
if isinstance(parent_names, (str, unicode)):
name = cls._name or parent_names
parent_names = [parent_names]
else:
name = cls._name
if not name:
raise TypeError('_name is mandatory in case of multiple inheritance')
for parent_name in ((type(parent_names)==list) and parent_names or [parent_names]):
parent_class = pool.get(parent_name).__class__
assert pool.get(parent_name), "parent class %s does not exist in module %s !" % (parent_name, module)
nattr = {}
for s in attributes:
new = copy.copy(getattr(pool.get(parent_name), s))
if s == '_columns':
# Don't _inherit custom fields.
for c in new.keys():
if new[c].manual:
del new[c]
if hasattr(new, 'update'):
new.update(cls.__dict__.get(s, {}))
elif s=='_constraints':
for c in cls.__dict__.get(s, []):
exist = False
for c2 in range(len(new)):
#For _constraints, we should check field and methods as well
if new[c2][2]==c[2] and (new[c2][0] == c[0] \
or getattr(new[c2][0],'__name__', True) == \
getattr(c[0],'__name__', False)):
# If new class defines a constraint with
# same function name, we let it override
# the old one.
new[c2] = c
exist = True
break
if not exist:
new.append(c)
else:
new.extend(cls.__dict__.get(s, []))
nattr[s] = new
cls = type(name, (cls, parent_class), nattr)
obj = object.__new__(cls)
obj.__init__(pool, cr)
return obj
class osv_memory(orm.orm_memory):
""" Deprecated class. """
pass
class osv_memory(osv_base, orm.orm_memory):
@classmethod
def createInstance(cls, pool, module, cr):
return cls.makeInstance(pool, module, cr, ['_columns', '_defaults'])
class osv(osv_base, orm.orm):
@classmethod
def createInstance(cls, pool, module, cr):
return cls.makeInstance(pool, module, cr, ['_columns', '_defaults',
'_inherits', '_constraints', '_sql_constraints'])
class osv(orm.orm):
""" Deprecated class. """
pass
def start_object_proxy():