[MERGE] osv_memory removed, merged into database-backed Model/TransientModel

bzr revid: odo@openerp.com-20110927213712-9aa0x0aqoh13f6ga
This commit is contained in:
Olivier Dony 2011-09-27 23:37:12 +02:00
commit 762637518b
14 changed files with 542 additions and 819 deletions

View File

@ -22,6 +22,8 @@
""" OpenERP core library. """ OpenERP core library.
""" """
# The hard-coded super-user id (a.k.a. administrator, or root user).
SUPERUSER_ID = 1
import addons import addons
import conf import conf

View File

@ -63,7 +63,7 @@ class ir_model(osv.osv):
models = self.browse(cr, uid, ids, context=context) models = self.browse(cr, uid, ids, context=context)
res = dict.fromkeys(ids) res = dict.fromkeys(ids)
for model in models: for model in models:
res[model.id] = isinstance(self.pool.get(model.model), osv.osv_memory) res[model.id] = self.pool.get(model.model).is_transient()
return res return res
def _search_osv_memory(self, cr, uid, model, name, domain, context=None): def _search_osv_memory(self, cr, uid, model, name, domain, context=None):
@ -165,7 +165,7 @@ class ir_model(osv.osv):
pass pass
x_custom_model._name = model x_custom_model._name = model
x_custom_model._module = False x_custom_model._module = False
a = x_custom_model.createInstance(self.pool, cr) a = x_custom_model.create_instance(self.pool, cr)
if (not a._columns) or ('x_name' in a._columns.keys()): if (not a._columns) or ('x_name' in a._columns.keys()):
x_name = 'x_name' x_name = 'x_name'
else: else:
@ -481,14 +481,12 @@ class ir_model_access(osv.osv):
if isinstance(model, browse_record): if isinstance(model, browse_record):
assert model._table_name == 'ir.model', 'Invalid model object' assert model._table_name == 'ir.model', 'Invalid model object'
model_name = model.name model_name = model.model
else: else:
model_name = model model_name = model
# osv_memory objects can be read by everyone, as they only return # TransientModel records have no access rights, only an implicit access rule
# results that belong to the current user (except for superuser) if self.pool.get(model_name).is_transient():
model_obj = self.pool.get(model_name)
if isinstance(model_obj, osv.osv_memory):
return True return True
# We check if a specific rule exists # We check if a specific rule exists
@ -523,7 +521,7 @@ class ir_model_access(osv.osv):
} }
raise except_orm(_('AccessError'), msgs[mode] % (model_name, groups) ) raise except_orm(_('AccessError'), msgs[mode] % (model_name, groups) )
return r return r or False
__cache_clearing_methods = [] __cache_clearing_methods = []

View File

@ -26,8 +26,7 @@ from functools import partial
import tools import tools
from tools.safe_eval import safe_eval as eval from tools.safe_eval import safe_eval as eval
from tools.misc import unquote as unquote from tools.misc import unquote as unquote
from openerp import SUPERUSER_ID
SUPERUSER_UID = 1
class ir_rule(osv.osv): class ir_rule(osv.osv):
_name = 'ir.rule' _name = 'ir.rule'
@ -68,7 +67,7 @@ class ir_rule(osv.osv):
return res return res
def _check_model_obj(self, cr, uid, ids, context=None): def _check_model_obj(self, cr, uid, ids, context=None):
return not any(isinstance(self.pool.get(rule.model_id.model), osv.osv_memory) for rule in self.browse(cr, uid, ids, context)) return not any(self.pool.get(rule.model_id.model).is_transient() for rule in self.browse(cr, uid, ids, context))
_columns = { _columns = {
'name': fields.char('Name', size=128, select=1), 'name': fields.char('Name', size=128, select=1),
@ -104,7 +103,7 @@ class ir_rule(osv.osv):
if mode not in self._MODES: if mode not in self._MODES:
raise ValueError('Invalid mode: %r' % (mode,)) raise ValueError('Invalid mode: %r' % (mode,))
if uid == SUPERUSER_UID: if uid == SUPERUSER_ID:
return None return None
cr.execute("""SELECT r.id cr.execute("""SELECT r.id
FROM ir_rule r FROM ir_rule r
@ -117,10 +116,10 @@ class ir_rule(osv.osv):
rule_ids = [x[0] for x in cr.fetchall()] rule_ids = [x[0] for x in cr.fetchall()]
if rule_ids: if rule_ids:
# browse user as super-admin root to avoid access errors! # browse user as super-admin root to avoid access errors!
user = self.pool.get('res.users').browse(cr, SUPERUSER_UID, uid) user = self.pool.get('res.users').browse(cr, SUPERUSER_ID, uid)
global_domains = [] # list of domains global_domains = [] # list of domains
group_domains = {} # map: group -> list of domains group_domains = {} # map: group -> list of domains
for rule in self.browse(cr, SUPERUSER_UID, rule_ids): for rule in self.browse(cr, SUPERUSER_ID, rule_ids):
# read 'domain' as UID to have the correct eval context for the rule. # read 'domain' as UID to have the correct eval context for the rule.
rule_domain = self.read(cr, uid, rule.id, ['domain'])['domain'] rule_domain = self.read(cr, uid, rule.id, ['domain'])['domain']
dom = expression.normalize(rule_domain) dom = expression.normalize(rule_domain)

View File

@ -19,18 +19,15 @@
# #
############################################################################## ##############################################################################
from osv import osv import openerp
from osv.orm import orm_memory
class osv_memory_autovacuum(osv.osv_memory): class osv_memory_autovacuum(openerp.osv.osv.osv_memory):
""" Expose the osv_memory.vacuum() method to the cron jobs mechanism. """
_name = 'osv_memory.autovacuum' _name = 'osv_memory.autovacuum'
def power_on(self, cr, uid, context=None): def power_on(self, cr, uid, context=None):
for model in self.pool.obj_list(): for model in self.pool.models.values():
obj = self.pool.get(model) if model.is_transient():
if isinstance(obj, orm_memory): model._transient_vacuum(cr, uid)
obj.vaccum(cr, uid)
return True return True
osv_memory_autovacuum()

View File

@ -91,7 +91,7 @@ class res_currency(osv.osv):
(name, (COALESCE(company_id,-1)))""") (name, (COALESCE(company_id,-1)))""")
def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'): def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'):
res = super(osv.osv, self).read(cr, user, ids, fields, context, load) res = super(res_currency, self).read(cr, user, ids, fields, context, load)
currency_rate_obj = self.pool.get('res.currency.rate') currency_rate_obj = self.pool.get('res.currency.rate')
for r in res: for r in res:
if r.__contains__('rate_ids'): if r.__contains__('rate_ids'):

View File

@ -144,45 +144,3 @@
!python {model: res.partner.category}: | !python {model: res.partner.category}: |
self.pool._init = True self.pool._init = True
-
"OSV Memory: Verify that osv_memory properly handles large data allocation"
-
1. No "count-based" auto-vaccuum when max_count is disabled
-
!python {model: base.language.export}: |
# setup special limits for the test, these will be reset at next pool reload anyway
self._max_count = None
num_recs = 250
for i in xrange(num_recs):
self.create(cr, uid, {'format':'po'})
assert (len(self.datas) >= num_recs), "OSV Memory must not auto-vaccum records from the current transaction if max_count is not set"
-
2. Auto-vaccuum should be enabled when max_count is set
-
!python {model: base.language.export}: |
# setup special limits for the test, these will be reset at next pool reload anyway
self._max_count = 100
num_recs = 219
for i in xrange(num_recs):
self.create(cr, uid, {'name': i, 'format':'po'})
assert (self._max_count <= len(self.datas) < self._max_count + self._check_time), "OSV Memory must auto-expire records when max_count is reached"
for k,v in self.datas.iteritems():
assert (int(v['name']) >= (num_recs - (self._max_count + self._check_time))), "OSV Memory must auto-expire records based on age"
-
3. Auto-vaccuum should be based on age only when max_count is not set
-
!python {model: base.language.export}: |
# setup special limits for the test, these will be reset at next pool reload anyway
self._max_count = None
self._max_hours = 0.01 #36 seconds
num_recs = 200
for i in xrange(num_recs):
self.create(cr, uid, {'format':'po'})
assert (len(self.datas) >= num_recs), "OSV Memory must not auto-expire records from the current transaction"
# expire all records
for k,v in self.datas.iteritems():
v['internal.date_access'] = 0
self.vaccum(cr, 1, force=True)
assert (len(self.datas) == 0), "OSV Memory must expire old records after vaccuum"

View File

@ -339,16 +339,16 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
cr.execute("""select model,name from ir_model where id NOT IN (select distinct model_id from ir_model_access)""") cr.execute("""select model,name from ir_model where id NOT IN (select distinct model_id from ir_model_access)""")
for (model, name) in cr.fetchall(): for (model, name) in cr.fetchall():
model_obj = pool.get(model) model_obj = pool.get(model)
if model_obj and not isinstance(model_obj, osv.osv.osv_memory): if model_obj and not model_obj.is_transient():
logger.notifyChannel('init', netsvc.LOG_WARNING, 'object %s (%s) has no access rules!' % (model, name)) logger.notifyChannel('init', netsvc.LOG_WARNING, 'Model %s (%s) has no access rules!' % (model, name))
# Temporary warning while we remove access rights on osv_memory objects, as they have # Temporary warning while we remove access rights on osv_memory objects, as they have
# been replaced by owner-only access rights # been replaced by owner-only access rights
cr.execute("""select distinct mod.model, mod.name from ir_model_access acc, ir_model mod where acc.model_id = mod.id""") cr.execute("""select distinct mod.model, mod.name from ir_model_access acc, ir_model mod where acc.model_id = mod.id""")
for (model, name) in cr.fetchall(): for (model, name) in cr.fetchall():
model_obj = pool.get(model) model_obj = pool.get(model)
if isinstance(model_obj, osv.osv.osv_memory) and not isinstance(model_obj, osv.osv.osv): if model_obj and model_obj.is_transient():
logger.notifyChannel('init', netsvc.LOG_WARNING, 'In-memory object %s (%s) should not have explicit access rules!' % (model, name)) logger.notifyChannel('init', netsvc.LOG_WARNING, 'The transient model %s (%s) should not have explicit access rules!' % (model, name))
cr.execute("SELECT model from ir_model") cr.execute("SELECT model from ir_model")
for (model,) in cr.fetchall(): for (model,) in cr.fetchall():
@ -356,7 +356,7 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
if obj: if obj:
obj._check_removed_columns(cr, log=True) obj._check_removed_columns(cr, log=True)
else: else:
logger.notifyChannel('init', netsvc.LOG_WARNING, "Model %s is referenced but not present in the orm pool!" % model) logger.notifyChannel('init', netsvc.LOG_WARNING, "Model %s is declared but cannot be loaded! (Perhaps a module was partially removed or renamed)" % model)
# Cleanup orphan records # Cleanup orphan records
pool.get('ir.model.data')._process_end(cr, 1, processed_modules) pool.get('ir.model.data')._process_end(cr, 1, processed_modules)

View File

@ -277,7 +277,6 @@ def init_module_models(cr, module_name, obj_list):
TODO better explanation of _auto_init and init. TODO better explanation of _auto_init and init.
""" """
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 = [] todo = []

View File

@ -89,10 +89,10 @@ class Registry(object):
res = [] res = []
# Instantiate registered classes (via metamodel discovery or via explicit # Instantiate registered classes (via the MetaModel automatic discovery
# constructor call), and add them to the pool. # or via explicit constructor call), and add them to the pool.
for cls in openerp.osv.orm.MetaModel.module_to_models.get(module.name, []): for cls in openerp.osv.orm.MetaModel.module_to_models.get(module.name, []):
res.append(cls.createInstance(self, cr)) res.append(cls.create_instance(self, cr))
return res return res

View File

@ -450,7 +450,7 @@ class expression(object):
# field could not be found in model columns, it's probably invalid, unless # field could not be found in model columns, it's probably invalid, unless
# it's one of the _log_access special fields # it's one of the _log_access special fields
# TODO: make these fields explicitly available in self.columns instead! # TODO: make these fields explicitly available in self.columns instead!
if (field_path[0] not in MAGIC_COLUMNS) and (left not in MAGIC_COLUMNS): if field_path[0] not in MAGIC_COLUMNS:
raise ValueError("Invalid field %r in domain expression %r" % (left, exp)) raise ValueError("Invalid field %r in domain expression %r" % (left, exp))
continue continue

View File

@ -72,7 +72,7 @@ class _column(object):
_symbol_set = (_symbol_c, _symbol_f) _symbol_set = (_symbol_c, _symbol_f)
_symbol_get = None _symbol_get = None
def __init__(self, string='unknown', required=False, readonly=False, domain=None, context=None, states=None, priority=0, change_default=False, size=None, ondelete="set null", translate=False, select=False, manual=False, **args): def __init__(self, string='unknown', required=False, readonly=False, domain=None, context=None, states=None, priority=0, change_default=False, size=None, ondelete=None, translate=False, select=False, manual=False, **args):
""" """
The 'manual' keyword argument specifies if the field is a custom one. The 'manual' keyword argument specifies if the field is a custom one.
@ -91,7 +91,7 @@ class _column(object):
self.help = args.get('help', '') self.help = args.get('help', '')
self.priority = priority self.priority = priority
self.change_default = change_default self.change_default = change_default
self.ondelete = ondelete self.ondelete = ondelete.lower() if ondelete else None # defaults to 'set null' in ORM
self.translate = translate self.translate = translate
self._domain = domain self._domain = domain
self._context = context self._context = context
@ -112,12 +112,6 @@ class _column(object):
def set(self, cr, obj, id, name, value, user=None, context=None): def set(self, cr, obj, id, name, value, user=None, context=None):
cr.execute('update '+obj._table+' set '+name+'='+self._symbol_set[0]+' where id=%s', (self._symbol_set[1](value), id)) cr.execute('update '+obj._table+' set '+name+'='+self._symbol_set[0]+' where id=%s', (self._symbol_set[1](value), id))
def set_memory(self, cr, obj, id, name, value, user=None, context=None):
raise Exception(_('Not implemented set_memory method !'))
def get_memory(self, cr, obj, ids, name, user=None, context=None, values=None):
raise Exception(_('Not implemented get_memory method !'))
def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None): def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None):
raise Exception(_('undefined get method !')) raise Exception(_('undefined get method !'))
@ -126,9 +120,6 @@ class _column(object):
res = obj.read(cr, uid, ids, [name], context=context) res = obj.read(cr, uid, ids, [name], context=context)
return [x[name] for x in res] return [x[name] for x in res]
def search_memory(self, cr, obj, args, name, value, offset=0, limit=None, uid=None, context=None):
raise Exception(_('Not implemented search_memory method !'))
# --------------------------------------------------------- # ---------------------------------------------------------
# Simple fields # Simple fields
@ -269,7 +260,7 @@ class binary(_column):
_column.__init__(self, string=string, **args) _column.__init__(self, string=string, **args)
self.filters = filters self.filters = filters
def get_memory(self, cr, obj, ids, name, user=None, context=None, values=None): def get(self, cr, obj, ids, name, user=None, context=None, values=None):
if not context: if not context:
context = {} context = {}
if not values: if not values:
@ -293,9 +284,6 @@ class binary(_column):
res[i] = val res[i] = val
return res return res
get = get_memory
class selection(_column): class selection(_column):
_type = 'selection' _type = 'selection'
@ -355,30 +343,6 @@ class many2one(_column):
_column.__init__(self, string=string, **args) _column.__init__(self, string=string, **args)
self._obj = obj self._obj = obj
def set_memory(self, cr, obj, id, field, values, user=None, context=None):
obj.datas.setdefault(id, {})
obj.datas[id][field] = values
def get_memory(self, cr, obj, ids, name, user=None, context=None, values=None):
result = {}
for id in ids:
result[id] = obj.datas[id].get(name, False)
# build a dictionary of the form {'id_of_distant_resource': name_of_distant_resource}
# we use uid=1 because the visibility of a many2one field value (just id and name)
# must be the access right of the parent form and not the linked object itself.
obj = obj.pool.get(self._obj)
records = dict(obj.name_get(cr, 1,
list(set([x for x in result.values() if x and isinstance(x, (int,long))])),
context=context))
for id in ids:
if result[id] in records:
result[id] = (result[id], records[result[id]])
else:
result[id] = False
return result
def get(self, cr, obj, ids, name, user=None, context=None, values=None): def get(self, cr, obj, ids, name, user=None, context=None, values=None):
if context is None: if context is None:
context = {} context = {}
@ -447,55 +411,6 @@ class one2many(_column):
#one2many can't be used as condition for defaults #one2many can't be used as condition for defaults
assert(self.change_default != True) assert(self.change_default != True)
def get_memory(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None):
if context is None:
context = {}
if self._context:
context = context.copy()
context.update(self._context)
if not values:
values = {}
res = {}
for id in ids:
res[id] = []
ids2 = obj.pool.get(self._obj).search(cr, user, [(self._fields_id, 'in', ids)], limit=self._limit, context=context)
for r in obj.pool.get(self._obj).read(cr, user, ids2, [self._fields_id], context=context, load='_classic_write'):
if r[self._fields_id] in res:
res[r[self._fields_id]].append(r['id'])
return res
def set_memory(self, cr, obj, id, field, values, user=None, context=None):
if not context:
context = {}
if self._context:
context = context.copy()
context.update(self._context)
if not values:
return
obj = obj.pool.get(self._obj)
for act in values:
if act[0] == 0:
act[2][self._fields_id] = id
obj.create(cr, user, act[2], context=context)
elif act[0] == 1:
obj.write(cr, user, [act[1]], act[2], context=context)
elif act[0] == 2:
obj.unlink(cr, user, [act[1]], context=context)
elif act[0] == 3:
obj.datas[act[1]][self._fields_id] = False
elif act[0] == 4:
obj.datas[act[1]][self._fields_id] = id
elif act[0] == 5:
for o in obj.datas.values():
if o[self._fields_id] == id:
o[self._fields_id] = False
elif act[0] == 6:
for id2 in (act[2] or []):
obj.datas[id2][self._fields_id] = id
def search_memory(self, cr, obj, args, name, value, offset=0, limit=None, uid=None, operator='like', context=None):
raise _('Not Implemented')
def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None): def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None):
if context is None: if context is None:
context = {} context = {}
@ -578,14 +493,34 @@ class one2many(_column):
# (6, ?, ids) set a list of links # (6, ?, ids) set a list of links
# #
class many2many(_column): class many2many(_column):
"""Encapsulates the logic of a many-to-many bidirectional relationship, handling the
low-level details of the intermediary relationship table transparently.
:param str obj: destination model
:param str rel: optional name of the intermediary relationship table. If not specified,
a canonical name will be derived based on the alphabetically-ordered
model names of the source and destination (in the form: ``amodel_bmodel_rel``).
Automatic naming is not possible when the source and destination are
the same, for obvious ambiguity reasons.
:param str id1: optional name for the column holding the foreign key to the current
model in the relationship table. If not specified, a canonical name
will be derived based on the model name (in the form: `src_model_id`).
:param str id2: optional name for the column holding the foreign key to the destination
model in the relationship table. If not specified, a canonical name
will be derived based on the model name (in the form: `dest_model_id`)
:param str string: field label
"""
_classic_read = False _classic_read = False
_classic_write = False _classic_write = False
_prefetch = False _prefetch = False
_type = 'many2many' _type = 'many2many'
def __init__(self, obj, rel, id1, id2, string='unknown', limit=None, **args):
def __init__(self, obj, rel=None, id1=None, id2=None, string='unknown', limit=None, **args):
"""
"""
_column.__init__(self, string=string, **args) _column.__init__(self, string=string, **args)
self._obj = obj self._obj = obj
if '.' in rel: if rel and '.' in rel:
raise Exception(_('The second argument of the many2many field %s must be a SQL table !'\ raise Exception(_('The second argument of the many2many field %s must be a SQL table !'\
'You used %s, which is not a valid SQL table name.')% (string,rel)) 'You used %s, which is not a valid SQL table name.')% (string,rel))
self._rel = rel self._rel = rel
@ -593,7 +528,30 @@ class many2many(_column):
self._id2 = id2 self._id2 = id2
self._limit = limit self._limit = limit
def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None): def _sql_names(self, source_model):
"""Return the SQL names defining the structure of the m2m relationship table
:return: (m2m_table, local_col, dest_col) where m2m_table is the table name,
local_col is the name of the column holding the current model's FK, and
dest_col is the name of the column holding the destination model's FK, and
"""
tbl, col1, col2 = self._rel, self._id1, self._id2
if not all((tbl, col1, col2)):
# the default table name is based on the stable alphabetical order of tables
dest_model = source_model.pool.get(self._obj)
tables = tuple(sorted([source_model._table, dest_model._table]))
if not tbl:
assert tables[0] != tables[1], 'Implicit/Canonical naming of m2m relationship table '\
'is not possible when source and destination models are '\
'the same'
tbl = '%s_%s_rel' % tables
if not col1:
col1 = '%s_id' % source_model._table
if not col2:
col2 = '%s_id' % dest_model._table
return (tbl, col1, col2)
def get(self, cr, model, ids, name, user=None, offset=0, context=None, values=None):
if not context: if not context:
context = {} context = {}
if not values: if not values:
@ -606,7 +564,8 @@ class many2many(_column):
if offset: if offset:
warnings.warn("Specifying offset at a many2many.get() may produce unpredictable results.", warnings.warn("Specifying offset at a many2many.get() may produce unpredictable results.",
DeprecationWarning, stacklevel=2) DeprecationWarning, stacklevel=2)
obj = obj.pool.get(self._obj) obj = model.pool.get(self._obj)
rel, id1, id2 = self._sql_names(model)
# static domains are lists, and are evaluated both here and on client-side, while string # static domains are lists, and are evaluated both here and on client-side, while string
# domains supposed by dynamic and evaluated on client-side only (thus ignored here) # domains supposed by dynamic and evaluated on client-side only (thus ignored here)
@ -636,11 +595,11 @@ class many2many(_column):
%(order_by)s \ %(order_by)s \
%(limit)s \ %(limit)s \
OFFSET %(offset)d' \ OFFSET %(offset)d' \
% {'rel': self._rel, % {'rel': rel,
'from_c': from_c, 'from_c': from_c,
'tbl': obj._table, 'tbl': obj._table,
'id1': self._id1, 'id1': id1,
'id2': self._id2, 'id2': id2,
'where_c': where_c, 'where_c': where_c,
'limit': limit_str, 'limit': limit_str,
'order_by': order_by, 'order_by': order_by,
@ -651,31 +610,32 @@ class many2many(_column):
res[r[1]].append(r[0]) res[r[1]].append(r[0])
return res return res
def set(self, cr, obj, id, name, values, user=None, context=None): def set(self, cr, model, id, name, values, user=None, context=None):
if not context: if not context:
context = {} context = {}
if not values: if not values:
return return
obj = obj.pool.get(self._obj) rel, id1, id2 = self._sql_names(model)
obj = model.pool.get(self._obj)
for act in values: for act in values:
if not (isinstance(act, list) or isinstance(act, tuple)) or not act: if not (isinstance(act, list) or isinstance(act, tuple)) or not act:
continue continue
if act[0] == 0: if act[0] == 0:
idnew = obj.create(cr, user, act[2], context=context) idnew = obj.create(cr, user, act[2], context=context)
cr.execute('insert into '+self._rel+' ('+self._id1+','+self._id2+') values (%s,%s)', (id, idnew)) cr.execute('insert into '+rel+' ('+id1+','+id2+') values (%s,%s)', (id, idnew))
elif act[0] == 1: elif act[0] == 1:
obj.write(cr, user, [act[1]], act[2], context=context) obj.write(cr, user, [act[1]], act[2], context=context)
elif act[0] == 2: elif act[0] == 2:
obj.unlink(cr, user, [act[1]], context=context) obj.unlink(cr, user, [act[1]], context=context)
elif act[0] == 3: elif act[0] == 3:
cr.execute('delete from '+self._rel+' where ' + self._id1 + '=%s and '+ self._id2 + '=%s', (id, act[1])) cr.execute('delete from '+rel+' where ' + id1 + '=%s and '+ id2 + '=%s', (id, act[1]))
elif act[0] == 4: elif act[0] == 4:
# following queries are in the same transaction - so should be relatively safe # following queries are in the same transaction - so should be relatively safe
cr.execute('SELECT 1 FROM '+self._rel+' WHERE '+self._id1+' = %s and '+self._id2+' = %s', (id, act[1])) cr.execute('SELECT 1 FROM '+rel+' WHERE '+id1+' = %s and '+id2+' = %s', (id, act[1]))
if not cr.fetchone(): if not cr.fetchone():
cr.execute('insert into '+self._rel+' ('+self._id1+','+self._id2+') values (%s,%s)', (id, act[1])) cr.execute('insert into '+rel+' ('+id1+','+id2+') values (%s,%s)', (id, act[1]))
elif act[0] == 5: elif act[0] == 5:
cr.execute('delete from '+self._rel+' where ' + self._id1 + ' = %s', (id,)) cr.execute('delete from '+rel+' where ' + id1 + ' = %s', (id,))
elif act[0] == 6: elif act[0] == 6:
d1, d2,tables = obj.pool.get('ir.rule').domain_get(cr, user, obj._name, context=context) d1, d2,tables = obj.pool.get('ir.rule').domain_get(cr, user, obj._name, context=context)
@ -683,10 +643,10 @@ class many2many(_column):
d1 = ' and ' + ' and '.join(d1) d1 = ' and ' + ' and '.join(d1)
else: else:
d1 = '' d1 = ''
cr.execute('delete from '+self._rel+' where '+self._id1+'=%s AND '+self._id2+' IN (SELECT '+self._rel+'.'+self._id2+' FROM '+self._rel+', '+','.join(tables)+' WHERE '+self._rel+'.'+self._id1+'=%s AND '+self._rel+'.'+self._id2+' = '+obj._table+'.id '+ d1 +')', [id, id]+d2) cr.execute('delete from '+rel+' where '+id1+'=%s AND '+id2+' IN (SELECT '+rel+'.'+id2+' FROM '+rel+', '+','.join(tables)+' WHERE '+rel+'.'+id1+'=%s AND '+rel+'.'+id2+' = '+obj._table+'.id '+ d1 +')', [id, id]+d2)
for act_nbr in act[2]: for act_nbr in act[2]:
cr.execute('insert into '+self._rel+' ('+self._id1+','+self._id2+') values (%s, %s)', (id, act_nbr)) cr.execute('insert into '+rel+' ('+id1+','+id2+') values (%s, %s)', (id, act_nbr))
# #
# TODO: use a name_search # TODO: use a name_search
@ -694,32 +654,6 @@ class many2many(_column):
def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None, operator='like', context=None): def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None, operator='like', context=None):
return obj.pool.get(self._obj).search(cr, uid, args+self._domain+[('name', operator, value)], offset, limit, context=context) return obj.pool.get(self._obj).search(cr, uid, args+self._domain+[('name', operator, value)], offset, limit, context=context)
def get_memory(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None):
result = {}
for id in ids:
result[id] = obj.datas[id].get(name, [])
return result
def set_memory(self, cr, obj, id, name, values, user=None, context=None):
if not values:
return
for act in values:
# TODO: use constants instead of these magic numbers
if act[0] == 0:
raise _('Not Implemented')
elif act[0] == 1:
raise _('Not Implemented')
elif act[0] == 2:
raise _('Not Implemented')
elif act[0] == 3:
raise _('Not Implemented')
elif act[0] == 4:
raise _('Not Implemented')
elif act[0] == 5:
raise _('Not Implemented')
elif act[0] == 6:
obj.datas[id][name] = act[2]
def get_nice_size(value): def get_nice_size(value):
size = 0 size = 0
@ -801,8 +735,8 @@ class function(_column):
Implements the function field. Implements the function field.
:param orm_template model: model to which the field belongs (should be ``self`` for :param orm model: model to which the field belongs (should be ``self`` for
a model method) a model method)
:param field_name(s): name of the field to compute, or if ``multi`` is provided, :param field_name(s): name of the field to compute, or if ``multi`` is provided,
list of field names to compute. list of field names to compute.
:type field_name(s): str | [str] :type field_name(s): str | [str]
@ -865,8 +799,8 @@ class function(_column):
Callable that implements the ``write`` operation for the function field. Callable that implements the ``write`` operation for the function field.
:param orm_template model: model to which the field belongs (should be ``self`` for :param orm model: model to which the field belongs (should be ``self`` for
a model method) a model method)
:param int id: the identifier of the object to write on :param int id: the identifier of the object to write on
:param str field_name: name of the field to set :param str field_name: name of the field to set
:param fnct_inv_arg: arbitrary value passed when declaring the function field :param fnct_inv_arg: arbitrary value passed when declaring the function field
@ -887,10 +821,10 @@ class function(_column):
a search criterion based on the function field into a new domain based only on a search criterion based on the function field into a new domain based only on
columns that are stored in the database. columns that are stored in the database.
:param orm_template model: model to which the field belongs (should be ``self`` for :param orm model: model to which the field belongs (should be ``self`` for
a model method) a model method)
:param orm_template model_again: same value as ``model`` (seriously! this is for backwards :param orm model_again: same value as ``model`` (seriously! this is for backwards
compatibility) compatibility)
:param str field_name: name of the field to search on :param str field_name: name of the field to search on
:param list criterion: domain component specifying the search criterion on the field. :param list criterion: domain component specifying the search criterion on the field.
:rtype: list :rtype: list
@ -935,7 +869,7 @@ class function(_column):
corresponding records in the source model (whose field values corresponding records in the source model (whose field values
need to be recomputed). need to be recomputed).
:param orm_template model: trigger_model :param orm model: trigger_model
:param list trigger_ids: ids of the records of trigger_model that were :param list trigger_ids: ids of the records of trigger_model that were
modified modified
:rtype: list :rtype: list
@ -1064,14 +998,11 @@ class function(_column):
result[id] = self.postprocess(cr, uid, obj, name, result[id], context) result[id] = self.postprocess(cr, uid, obj, name, result[id], context)
return result return result
get_memory = get
def set(self, cr, obj, id, name, value, user=None, context=None): def set(self, cr, obj, id, name, value, user=None, context=None):
if not context: if not context:
context = {} context = {}
if self._fnct_inv: if self._fnct_inv:
self._fnct_inv(obj, cr, user, id, name, value, self._fnct_inv_arg, context) self._fnct_inv(obj, cr, user, id, name, value, self._fnct_inv_arg, context)
set_memory = set
# --------------------------------------------------------- # ---------------------------------------------------------
# Related fields # Related fields

File diff suppressed because it is too large Load Diff

View File

@ -21,16 +21,17 @@
#.apidoc title: Objects Services (OSV) #.apidoc title: Objects Services (OSV)
import logging
from psycopg2 import IntegrityError, errorcodes
import orm import orm
import openerp
import openerp.netsvc as netsvc import openerp.netsvc as netsvc
import openerp.pooler as pooler import openerp.pooler as pooler
import openerp.sql_db as sql_db import openerp.sql_db as sql_db
import logging
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 from openerp.osv.orm import MetaModel, Model, TransientModel, AbstractModel
class except_osv(Exception): class except_osv(Exception):
def __init__(self, name, value, exc_type='warning'): def __init__(self, name, value, exc_type='warning'):
@ -198,17 +199,10 @@ class object_proxy():
cr.close() cr.close()
return res return res
# deprecated - for backward compatibility.
class osv_memory(orm.orm_memory): osv = Model
""" Deprecated class. """ osv_memory = TransientModel
__metaclass__ = MetaModel osv_abstract = AbstractModel # ;-)
_register = False # Set to false if the model shouldn't be automatically discovered.
class osv(orm.orm):
""" Deprecated class. """
__metaclass__ = MetaModel
_register = False # Set to false if the model shouldn't be automatically discovered.
def start_object_proxy(): def start_object_proxy():

View File

@ -307,7 +307,7 @@ class YamlInterpreter(object):
import openerp.osv as osv import openerp.osv as osv
record, fields = node.items()[0] record, fields = node.items()[0]
model = self.get_model(record.model) model = self.get_model(record.model)
if isinstance(model, osv.osv.osv_memory): if model.is_transient():
record_dict=self.create_osv_memory_record(record, fields) record_dict=self.create_osv_memory_record(record, fields)
else: else:
self.validate_xml_id(record.id) self.validate_xml_id(record.id)