2009-10-20 10:52:23 +00:00
# -*- coding: utf-8 -*-
2006-12-07 13:41:40 +00:00
##############################################################################
2010-03-09 14:28:07 +00:00
#
2009-01-07 14:52:34 +00:00
# OpenERP, Open Source Management Solution
2009-11-26 14:12:40 +00:00
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
2008-06-16 07:24:04 +00:00
#
2008-11-03 18:27:16 +00:00
# This program is free software: you can redistribute it and/or modify
2009-11-26 14:12:40 +00:00
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
2006-12-07 13:41:40 +00:00
#
2008-11-03 18:27:16 +00:00
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2009-11-26 14:12:40 +00:00
# GNU Affero General Public License for more details.
2006-12-07 13:41:40 +00:00
#
2009-11-26 14:12:40 +00:00
# You should have received a copy of the GNU Affero General Public License
2010-03-09 14:28:07 +00:00
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2006-12-07 13:41:40 +00:00
#
2008-11-03 18:27:16 +00:00
##############################################################################
2006-12-07 13:41:40 +00:00
#
2008-08-13 08:42:42 +00:00
# OSV: Objects Services
2006-12-07 13:41:40 +00:00
#
2011-05-19 09:03:00 +00:00
import sys
import inspect
2008-08-13 08:42:42 +00:00
import orm
2011-02-07 12:57:23 +00:00
import openerp . netsvc as netsvc
import openerp . pooler as pooler
2011-04-20 14:14:07 +00:00
import openerp . sql_db as sql_db
2008-08-13 08:42:42 +00:00
import copy
2010-04-02 07:08:23 +00:00
import logging
2010-05-21 15:59:02 +00:00
from psycopg2 import IntegrityError , errorcodes
2011-02-07 12:57:23 +00:00
from openerp . tools . func import wraps
from openerp . tools . translate import translate
2009-11-05 06:39:15 +00:00
2011-05-19 08:50:35 +00:00
# Mapping between openerp module names and their osv classes.
2008-08-13 08:42:42 +00:00
module_class_list = { }
2006-12-07 13:41:40 +00:00
2008-08-13 08:42:42 +00:00
class except_osv ( Exception ) :
def __init__ ( self , name , value , exc_type = ' warning ' ) :
2008-07-22 14:24:36 +00:00
self . name = name
2008-08-13 08:42:42 +00:00
self . exc_type = exc_type
2008-07-22 14:24:36 +00:00
self . value = value
2008-10-07 14:51:30 +00:00
self . args = ( exc_type , name )
2006-12-07 13:41:40 +00:00
2010-12-13 10:51:04 +00:00
class object_proxy ( netsvc . Service ) :
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 )
2010-03-09 14:28:07 +00:00
2009-01-03 00:29:30 +00:00
def check ( f ) :
@wraps ( f )
def wrapper ( self , dbname , * args , * * kwargs ) :
2010-11-18 18:46:38 +00:00
""" Wraps around OSV functions and normalises a few exceptions
"""
def tr ( src , ttype ) :
# We try to do the same as the _(), but without the frame
# inspection, since we aready are wrapping an osv function
# trans_obj = self.get('ir.translation') cannot work yet :(
ctx = { }
if not kwargs :
if args and isinstance ( args [ - 1 ] , dict ) :
ctx = args [ - 1 ]
elif isinstance ( kwargs , dict ) :
ctx = kwargs . get ( ' context ' , { } )
uid = 1
if args and isinstance ( args [ 0 ] , ( long , int ) ) :
uid = args [ 0 ]
2010-11-24 16:40:41 +00:00
lang = ctx and ctx . get ( ' lang ' )
2010-11-18 18:46:43 +00:00
if not ( lang or hasattr ( src , ' __call__ ' ) ) :
2010-11-18 18:46:38 +00:00
return src
# We open a *new* cursor here, one reason is that failed SQL
# queries (as in IntegrityError) will invalidate the current one.
cr = False
2010-11-18 18:46:43 +00:00
if hasattr ( src , ' __call__ ' ) :
# callable. We need to find the right parameters to call
# the orm._sql_message(self, cr, uid, ids, context) function,
# or we skip..
# our signature is f(osv_pool, dbname [,uid, obj, method, args])
try :
if args and len ( args ) > 1 :
obj = self . get ( args [ 1 ] )
if len ( args ) > 3 and isinstance ( args [ 3 ] , ( long , int , list ) ) :
ids = args [ 3 ]
else :
ids = [ ]
2011-04-20 14:14:07 +00:00
cr = sql_db . db_connect ( db_name ) . cursor ( )
2010-11-18 18:46:43 +00:00
return src ( obj , cr , uid , ids , context = ( ctx or { } ) )
2010-11-18 18:48:40 +00:00
except Exception :
2010-11-18 18:46:43 +00:00
pass
finally :
if cr : cr . close ( )
return False # so that the original SQL error will
# be returned, it is the best we have.
2010-11-18 18:46:38 +00:00
try :
2011-04-20 14:14:07 +00:00
cr = sql_db . db_connect ( db_name ) . cursor ( )
2010-11-18 18:46:43 +00:00
res = translate ( cr , name = False , source_type = ttype ,
2010-11-18 18:46:38 +00:00
lang = lang , source = src )
2010-11-18 18:46:43 +00:00
if res :
return res
else :
return src
2010-11-18 18:46:38 +00:00
finally :
if cr : cr . close ( )
def _ ( src ) :
return tr ( src , ' code ' )
2009-01-03 00:29:30 +00:00
try :
if not pooler . get_pool ( dbname ) . _ready :
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 :
2010-02-04 17:26:10 +00:00
if inst . name == ' AccessError ' :
2010-04-01 15:08:54 +00:00
self . logger . debug ( " AccessError " , exc_info = True )
2009-01-03 00:29:30 +00:00
self . abortResponse ( 1 , inst . name , ' warning ' , inst . value )
except except_osv , inst :
self . abortResponse ( 1 , inst . name , inst . exc_type , inst . value )
except IntegrityError , inst :
2010-12-14 13:18:12 +00:00
osv_pool = pooler . get_pool ( dbname )
for key in osv_pool . _sql_error . keys ( ) :
2009-01-03 00:29:30 +00:00
if key in inst [ 0 ] :
2010-11-18 18:46:38 +00:00
self . abortResponse ( 1 , _ ( ' Constraint Error ' ) , ' warning ' ,
2010-12-14 13:18:12 +00:00
tr ( osv_pool . _sql_error [ key ] , ' sql_constraint ' ) or inst [ 0 ] )
2010-09-16 11:39:06 +00:00
if inst . pgcode in ( errorcodes . NOT_NULL_VIOLATION , errorcodes . FOREIGN_KEY_VIOLATION , errorcodes . RESTRICT_VIOLATION ) :
2010-09-09 11:02:11 +00:00
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 ' )
2010-05-21 15:59:02 +00:00
self . logger . debug ( " IntegrityError " , exc_info = True )
try :
2010-09-16 11:39:06 +00:00
errortxt = inst . pgerror . replace ( ' « ' , ' " ' ) . replace ( ' » ' , ' " ' )
if ' " public " . ' in errortxt :
context = errortxt . split ( ' " public " . ' ) [ 1 ]
model_name = table = context . split ( ' " ' ) [ 1 ]
else :
last_quote_end = errortxt . rfind ( ' " ' )
last_quote_begin = errortxt . rfind ( ' " ' , 0 , last_quote_end )
model_name = table = errortxt [ last_quote_begin + 1 : last_quote_end ] . strip ( )
2010-05-21 15:59:02 +00:00
model = table . replace ( " _ " , " . " )
2010-12-14 13:18:12 +00:00
model_obj = osv_pool . get ( model )
2010-05-21 15:59:02 +00:00
if model_obj :
model_name = model_obj . _description or model_obj . _name
2010-09-09 11:02:11 +00:00
msg + = _ ( ' \n \n [object with reference: %s - %s ] ' ) % ( model_name , model )
2010-05-21 15:59:02 +00:00
except Exception :
pass
2010-09-09 11:02:11 +00:00
self . abortResponse ( 1 , _ ( ' Integrity Error ' ) , ' warning ' , msg )
2010-05-19 13:15:36 +00:00
else :
2010-09-09 11:02:11 +00:00
self . abortResponse ( 1 , _ ( ' Integrity Error ' ) , ' warning ' , inst [ 0 ] )
2010-12-13 10:51:04 +00:00
except Exception :
2010-04-01 15:08:54 +00:00
self . logger . exception ( " Uncaught exception " )
2009-01-03 00:29:30 +00:00
raise
return wrapper
2008-08-13 08:42:42 +00:00
def execute_cr ( self , cr , uid , obj , method , * args , * * kw ) :
2009-01-03 00:29:30 +00:00
object = pooler . get_pool ( cr . dbname ) . get ( obj )
if not object :
raise except_osv ( ' Object Error ' , ' Object %s doesn \' t exist ' % str ( obj ) )
return getattr ( object , method ) ( cr , uid , * args , * * kw )
2010-03-09 14:28:07 +00:00
2009-01-03 00:29:30 +00:00
@check
2008-08-13 08:42:42 +00:00
def execute ( self , db , uid , obj , method , * args , * * kw ) :
2010-12-13 10:51:04 +00:00
cr = pooler . get_db ( db ) . cursor ( )
2008-08-13 08:42:42 +00:00
try :
2008-07-22 14:24:36 +00:00
try :
2010-03-09 14:28:07 +00:00
if method . startswith ( ' _ ' ) :
2010-04-01 15:08:54 +00:00
raise except_osv ( ' Access Denied ' , ' Private methods (such as %s ) cannot be called remotely. ' % ( method , ) )
2010-12-13 10:51:04 +00:00
res = self . execute_cr ( cr , uid , obj , method , * args , * * kw )
2010-03-09 14:28:07 +00:00
if res is None :
2010-06-12 16:49:35 +00:00
self . logger . warning ( ' The method %s of the object %s can not return `None` ! ' , method , obj )
2008-08-13 08:42:42 +00:00
cr . commit ( )
except Exception :
2008-07-22 14:24:36 +00:00
cr . rollback ( )
2008-08-13 08:42:42 +00:00
raise
finally :
cr . close ( )
return res
2008-08-12 14:44:56 +00:00
2008-08-13 08:42:42 +00:00
def exec_workflow_cr ( self , cr , uid , obj , method , * args ) :
wf_service = netsvc . LocalService ( " workflow " )
return wf_service . trg_validate ( uid , obj , args [ 0 ] , method , cr )
2008-08-12 14:44:56 +00:00
2009-01-03 00:29:30 +00:00
@check
2008-08-13 08:42:42 +00:00
def exec_workflow ( self , db , uid , obj , method , * args ) :
cr = pooler . get_db ( db ) . cursor ( )
try :
try :
res = self . exec_workflow_cr ( cr , uid , obj , method , * args )
cr . commit ( )
2009-01-03 00:29:30 +00:00
except Exception :
2008-08-13 08:42:42 +00:00
cr . rollback ( )
2009-01-03 00:29:30 +00:00
raise
2008-08-13 08:42:42 +00:00
finally :
cr . close ( )
return res
2008-08-12 14:44:56 +00:00
2010-12-13 10:51:04 +00:00
class osv_pool ( object ) :
2011-05-19 08:50:35 +00:00
""" Model registry for a particular database.
The registry is essentially a mapping between model names and model
instances . There is one registry instance per database .
"""
2010-12-13 10:51:04 +00:00
def __init__ ( self ) :
self . _ready = False
2011-05-19 08:50:35 +00:00
self . obj_pool = { } # model name/model instance mapping
2010-12-13 10:51:04 +00:00
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
2008-08-13 08:42:42 +00:00
def obj_list ( self ) :
2011-05-19 08:50:35 +00:00
""" Return the list of model names in this registry. """
2008-08-13 08:42:42 +00:00
return self . obj_pool . keys ( )
2011-05-19 08:50:35 +00:00
def add ( self , model_name , model ) :
""" Add or replace a model in the registry. """
self . obj_pool [ model_name ] = model
2008-08-13 08:42:42 +00:00
def get ( self , name ) :
2011-05-19 08:50:35 +00:00
""" Return a model for a given name or None if it doesn ' t exist. """
return self . obj_pool . get ( name )
2008-08-13 08:42:42 +00:00
#TODO: pass a list of modules to load
def instanciate ( self , module , cr ) :
2011-05-19 09:03:00 +00:00
""" Instanciate all the classes of a given module for a particular db. """
2008-08-13 08:42:42 +00:00
res = [ ]
2011-05-19 09:03:00 +00:00
# instanciate classes registered through their constructor
for klass in module_class_list . get ( module , [ ] ) :
2008-08-13 08:42:42 +00:00
res . append ( klass . createInstance ( self , module , cr ) )
2011-05-19 09:03:00 +00:00
2008-07-22 14:24:36 +00:00
return res
2008-08-13 08:42:42 +00:00
2009-12-17 19:21:39 +00:00
class osv_base ( object ) :
2011-05-19 08:50:35 +00:00
""" 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).
"""
2009-12-17 19:21:39 +00:00
def __init__ ( self , pool , cr ) :
2011-05-19 08:50:35 +00:00
""" Initialize a model and make it part of the given registry. """
2009-12-17 19:21:39 +00:00
pool . add ( self . _name , self )
self . pool = pool
super ( osv_base , self ) . __init__ ( cr )
2008-10-07 14:51:30 +00:00
2008-08-13 08:42:42 +00:00
def __new__ ( cls ) :
2011-05-19 08:50:35 +00:00
""" 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.
2011-04-29 08:51:45 +00:00
module = cls . __module__ . split ( ' . ' ) [ 0 ]
2008-08-13 08:42:42 +00:00
if not hasattr ( cls , ' _module ' ) :
cls . _module = module
2011-05-19 08:50:35 +00:00
# Remember which models to instanciate for this module.
2008-08-13 08:42:42 +00:00
module_class_list . setdefault ( cls . _module , [ ] ) . append ( cls )
2011-05-19 08:50:35 +00:00
# Since we don't return an instance here, the __init__
# method won't be called.
2008-08-13 08:42:42 +00:00
return None
2008-07-22 14:24:36 +00:00
2009-12-17 19:21:39 +00:00
class osv_memory ( osv_base , orm . orm_memory ) :
2008-07-22 14:24:36 +00:00
#
2008-08-13 08:42:42 +00:00
# Goal: try to apply inheritancy at the instanciation level and
# put objects in the pool var
2008-07-22 14:24:36 +00:00
#
2010-01-12 15:39:21 +00:00
def createInstance ( cls , pool , module , cr ) :
2010-01-03 14:10:21 +00:00
parent_names = getattr ( cls , ' _inherit ' , None )
2009-12-17 18:05:28 +00:00
if parent_names :
2010-01-12 12:52:16 +00:00
if isinstance ( parent_names , ( str , unicode ) ) :
2010-01-12 15:38:44 +00:00
name = cls . _name or parent_names
2010-01-12 12:52:16 +00:00
parent_names = [ parent_names ]
else :
name = cls . _name
if not name :
raise TypeError ( ' _name is mandatory in case of multiple inheritance ' )
2009-12-17 18:05:28 +00:00
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 ( ' _columns ' , ' _defaults ' ) :
new = copy . copy ( getattr ( pool . get ( parent_name ) , s ) )
2011-05-25 13:26:12 +00:00
if s == ' _columns ' :
# Don't _inherit custom fields.
for c in new . keys ( ) :
if new [ c ] . manual :
del new [ c ]
2009-12-17 18:05:28 +00:00
if hasattr ( new , ' update ' ) :
new . update ( cls . __dict__ . get ( s , { } ) )
else :
new . extend ( cls . __dict__ . get ( s , [ ] ) )
nattr [ s ] = new
cls = type ( name , ( cls , parent_class ) , nattr )
2009-09-17 07:27:12 +00:00
2008-08-13 08:42:42 +00:00
obj = object . __new__ ( cls )
obj . __init__ ( pool , cr )
return obj
createInstance = classmethod ( createInstance )
2009-12-17 19:21:39 +00:00
class osv ( osv_base , orm . orm ) :
2008-07-22 14:24:36 +00:00
#
2008-08-13 08:42:42 +00:00
# Goal: try to apply inheritancy at the instanciation level and
# put objects in the pool var
2008-07-22 14:24:36 +00:00
#
2008-08-13 08:42:42 +00:00
def createInstance ( cls , pool , module , cr ) :
2010-01-12 15:39:21 +00:00
parent_names = getattr ( cls , ' _inherit ' , None )
2009-12-17 18:07:56 +00:00
if parent_names :
2010-01-12 12:52:16 +00:00
if isinstance ( parent_names , ( str , unicode ) ) :
2010-01-12 15:38:44 +00:00
name = cls . _name or parent_names
2010-01-12 12:52:16 +00:00
parent_names = [ parent_names ]
else :
name = cls . _name
if not name :
raise TypeError ( ' _name is mandatory in case of multiple inheritance ' )
2009-12-17 18:07:56 +00:00
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 ( ' _columns ' , ' _defaults ' , ' _inherits ' , ' _constraints ' , ' _sql_constraints ' ) :
new = copy . copy ( getattr ( pool . get ( parent_name ) , s ) )
2011-05-25 13:26:12 +00:00
if s == ' _columns ' :
# Don't _inherit custom fields.
for c in new . keys ( ) :
if new [ c ] . manual :
del new [ c ]
2009-12-17 18:07:56 +00:00
if hasattr ( new , ' update ' ) :
new . update ( cls . __dict__ . get ( s , { } ) )
2008-11-23 11:27:27 +00:00
else :
2009-12-17 18:07:56 +00:00
if s == ' _constraints ' :
for c in cls . __dict__ . get ( s , [ ] ) :
exist = False
for c2 in range ( len ( new ) ) :
2010-05-11 08:36:01 +00:00
#For _constraints, we should check field and methods as well
[FIX] osv: _constraints can be inherited by redefining a constraint with the same name
Indeed, when we define an expression like
class bar(osv.osv):
_inherit = 'bar.bar'
def _check_foo(self, cr, uid, ids, context):
return True
_constraints = [ (_check_foo, "Foo failed!", ['foo']) ]
... it means that _check_foo will be passed as an *object* to the model's
structure of _constraints. Therefore, it would be unequal and just append
the list of any existing constraints. So, an older (_check_foo, , ['foo'])
would always remain active using the previous code. This has to do with
the _check_foo being an unbound (ie. not inheritable) function.
Now, we check the /string name/ of the function, too. We say that if the
inherited class's constraint function has the same name "_check_foo", the
old ones shall be replaced.
Note: this MAY introduce unpredictable results, if several modules try
to override the same inherited constraint. There is no guaranteed order
of inheritance. Please avoid using this feature unless necessary.
lp bug: https://launchpad.net/bugs/700451 fixed
bzr revid: odo@openerp.com-20110117094750-4lyzx165f1z1zgl4
2011-01-17 09:47:50 +00:00
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.
2009-12-17 18:07:56 +00:00
new [ c2 ] = c
exist = True
break
if not exist :
new . append ( c )
else :
new . extend ( cls . __dict__ . get ( s , [ ] ) )
2010-01-12 12:34:31 +00:00
nattr [ s ] = new
2009-12-17 18:07:56 +00:00
cls = type ( name , ( cls , parent_class ) , nattr )
2008-08-13 08:42:42 +00:00
obj = object . __new__ ( cls )
obj . __init__ ( pool , cr )
return obj
createInstance = classmethod ( createInstance )
2008-07-22 14:24:36 +00:00
2011-05-07 11:21:29 +00:00
def start_object_proxy ( ) :
object_proxy ( )
2009-01-07 14:52:34 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: