#.apidoc title: Objects Services (OSV)
+from functools import wraps
+import logging
+from psycopg2 import IntegrityError, errorcodes
+
import orm
import openerp
import openerp.netsvc as netsvc
import openerp.pooler as pooler
import openerp.sql_db as sql_db
-import logging
-from psycopg2 import IntegrityError, errorcodes
-from openerp.tools.config import config
-from openerp.tools.func import wraps
from openerp.tools.translate import translate
-from openerp.osv.orm import MetaModel, Model
+from openerp.osv.orm import MetaModel, Model, TransientModel, AbstractModel
+import openerp.exceptions
+_logger = logging.getLogger(__name__)
+# Deprecated.
class except_osv(Exception):
- def __init__(self, name, value, exc_type='warning'):
+ def __init__(self, name, value):
self.name = name
- self.exc_type = exc_type
self.value = value
- self.args = (exc_type, name)
+ self.args = (name, value)
+service = None
-class object_proxy(netsvc.Service):
+class object_proxy(object):
def __init__(self):
- self.logger = logging.getLogger('web-services')
- netsvc.Service.__init__(self, 'object_proxy', audience='')
- self.exportMethod(self.exec_workflow)
- self.exportMethod(self.execute)
+ global service
+ service = self
def check(f):
@wraps(f)
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:
- if inst.name == 'AccessError':
- self.logger.debug("AccessError", exc_info=True)
- self.abortResponse(1, inst.name, 'warning', inst.value)
- except except_osv, inst:
- self.abortResponse(1, inst.name, inst.exc_type, inst.value)
+ raise except_osv(inst.name, inst.value)
+ except except_osv:
+ raise
except IntegrityError, inst:
osv_pool = pooler.get_pool(dbname)
for key in osv_pool._sql_error.keys():
if key in inst[0]:
- self.abortResponse(1, _('Constraint Error'), 'warning',
+ netsvc.abort_response(1, _('Constraint Error'), 'warning',
tr(osv_pool._sql_error[key], 'sql_constraint') or inst[0])
if inst.pgcode in (errorcodes.NOT_NULL_VIOLATION, errorcodes.FOREIGN_KEY_VIOLATION, errorcodes.RESTRICT_VIOLATION):
msg = _('The operation cannot be completed, probably due to the following:\n- deletion: you may be trying to delete a record while other records still reference it\n- creation/update: a mandatory field is not correctly set')
- self.logger.debug("IntegrityError", exc_info=True)
+ _logger.debug("IntegrityError", exc_info=True)
try:
errortxt = inst.pgerror.replace('«','"').replace('»','"')
if '"public".' in errortxt:
msg += _('\n\n[object with reference: %s - %s]') % (model_name, model)
except Exception:
pass
- self.abortResponse(1, _('Integrity Error'), 'warning', msg)
+ netsvc.abort_response(1, _('Integrity Error'), 'warning', msg)
else:
- self.abortResponse(1, _('Integrity Error'), 'warning', inst[0])
+ netsvc.abort_response(1, _('Integrity Error'), 'warning', inst[0])
except Exception:
- self.logger.exception("Uncaught exception")
+ _logger.exception("Uncaught exception")
raise
return wrapper
raise except_osv('Object Error', 'Object %s doesn\'t exist' % str(obj))
return getattr(object, method)(cr, uid, *args, **kw)
+ def execute_kw(self, db, uid, obj, method, args, kw=None):
+ return self.execute(db, uid, obj, method, *args, **kw or {})
+
@check
def execute(self, db, uid, obj, method, *args, **kw):
cr = pooler.get_db(db).cursor()
raise except_osv('Access Denied', 'Private methods (such as %s) cannot be called remotely.' % (method,))
res = self.execute_cr(cr, uid, obj, method, *args, **kw)
if res is None:
- self.logger.warning('The method %s of the object %s can not return `None` !', method, obj)
+ _logger.warning('The method %s of the object %s can not return `None` !', method, obj)
cr.commit()
except Exception:
cr.rollback()
cr.close()
return res
-
-class TransientModel(Model):
- """ Model for transient records.
-
- A TransientModel works similarly to a regular Model but the assiociated
- records will be cleaned automatically from the database after some time.
-
- A TransientModel has no access rules.
-
- """
- __metaclass__ = MetaModel
- _register = False # Set to false if the model shouldn't be automatically discovered.
- _transient = True
- _max_count = None
- _max_hours = None
- _check_time = 20
-
- def __init__(self, pool, cr):
- super(TransientModel, self).__init__(pool, cr)
- self.check_count = 0
- self._max_count = config.get('osv_memory_count_limit')
- self._max_hours = config.get('osv_memory_age_limit')
- cr.execute('delete from wkf_instance where res_type=%s', (self._name,))
-
- def _clean_transient_rows_older_than(self, cr, seconds):
- if not self._log_access:
- self.logger = logging.getLogger('orm').warning(
- "Transient model without write_date: %s" % (self._name,))
- return
-
- cr.execute("SELECT id FROM " + self._table + " WHERE"
- " COALESCE(write_date, create_date, now())::timestamp <"
- " (now() - interval %s)", ("%s seconds" % seconds,))
- ids = [x[0] for x in cr.fetchall()]
- self.unlink(cr, openerp.SUPERUSER, ids)
-
- def _clean_old_transient_rows(self, cr, count):
- if not self._log_access:
- self.logger = logging.getLogger('orm').warning(
- "Transient model without write_date: %s" % (self._name,))
- return
-
- cr.execute(
- "SELECT id, COALESCE(write_date, create_date, now())::timestamp"
- " AS t FROM " + self._table +
- " ORDER BY t LIMIT %s", (count,))
- ids = [x[0] for x in cr.fetchall()]
- self.unlink(cr, openerp.SUPERUSER, ids)
-
- def vacuum(self, cr, uid, force=False):
- """ Clean the TransientModel records.
-
- This unlinks old records from the transient model tables whenever the
- "_max_count" or "_max_age" conditions (if any) are reached.
- Actual cleaning will happen only once every "_check_time" calls.
- This means this method can be called frequently called (e.g. whenever
- a new record is created).
- """
- self.check_count += 1
- if (not force) and (self.check_count % self._check_time):
- self.check_count = 0
- return True
-
- # Age-based expiration
- if self._max_hours:
- self._clean_transient_rows_older_than(cr, self._max_hours * 60 * 60)
-
- # Count-based expiration
- if self._max_count:
- self._clean_old_transient_rows(cr, self._max_count)
-
- return True
-
- def check_access_rule(self, cr, uid, ids, operation, context=None):
- # No access rules for transient models.
- if self._log_access and uid != openerp.SUPERUSER:
- cr.execute("SELECT distinct create_uid FROM " + self._table + " WHERE"
- " id in ", (tuple(ids),))
- uids = [x[0] for x in cr.fetchall()]
- if len(uids) != 1 or uids[0] != uid:
- raise orm.except_orm(_('AccessError'), '%s access is '
- 'restricted to your own records for transient models '
- '(except for the super-user).' % mode.capitalize())
-
- def create(self, cr, uid, vals, context=None):
- self.vacuum(cr, uid)
- return super(TransientModel, self).create(cr, uid, vals, context)
-
- def unlink(self, cr, uid, ids, context=None):
- super(TransientModel, self).unlink(cr, uid, ids, context)
- if isinstance(ids, (int, long)):
- ids = [ids]
- if ids:
- cr.execute('delete from wkf_instance where res_type=%s and res_id IN %s', (self._name, tuple(ids)))
- return True
-
- def _search(self, cr, uid, domain, offset=0, limit=None, order=None, context=None, count=False, access_rights_uid=None):
-
- # Restrict acces to the current user, except for the super-user.
- if self._log_access and uid != openerp.SUPERUSER:
- domain = expression.expression_and(('create_uid', '=', uid), domain)
-
- # TODO unclear: shoudl access_rights_uid be set to None (effectively ignoring it) or used instead of uid?
- return super(TransientModel, self)._search(cr, uid, domain, offset, limit, order, context, count, access_rights_uid)
-
-
-# For backward compatibility.
+# deprecated - for backward compatibility.
osv = Model
osv_memory = TransientModel
+osv_abstract = AbstractModel # ;-)
def start_object_proxy():