[MERGE] latest trunk
bzr revid: odo@openerp.com-20110617170517-pvhf772lewqk3z8c
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -7,13 +7,13 @@ msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 5.0.4\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2011-01-11 11:14+0000\n"
|
||||
"PO-Revision-Date: 2011-05-11 04:20+0000\n"
|
||||
"Last-Translator: doingit.cl <Unknown>\n"
|
||||
"PO-Revision-Date: 2011-06-06 17:53+0000\n"
|
||||
"Last-Translator: Hector Rojas (doingIT.cl) <Unknown>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2011-05-12 04:34+0000\n"
|
||||
"X-Launchpad-Export-Date: 2011-06-07 04:35+0000\n"
|
||||
"X-Generator: Launchpad (build 12959)\n"
|
||||
|
||||
#. module: base
|
||||
@@ -58,7 +58,7 @@ msgstr "Metadatos"
|
||||
#: field:ir.ui.view,arch:0
|
||||
#: field:ir.ui.view.custom,arch:0
|
||||
msgid "View Architecture"
|
||||
msgstr "Código vista"
|
||||
msgstr "Estructura de la vista"
|
||||
|
||||
#. module: base
|
||||
#: field:base.language.import,code:0
|
||||
|
||||
@@ -8,14 +8,14 @@ msgstr ""
|
||||
"Project-Id-Version: openobject-addons\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2011-01-11 11:14+0000\n"
|
||||
"PO-Revision-Date: 2011-05-29 23:23+0000\n"
|
||||
"PO-Revision-Date: 2011-06-14 10:07+0000\n"
|
||||
"Last-Translator: Phong Nguyen-Thanh <Unknown>\n"
|
||||
"Language-Team: Vietnamese <vi@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2011-05-31 04:47+0000\n"
|
||||
"X-Generator: Launchpad (build 12959)\n"
|
||||
"X-Launchpad-Export-Date: 2011-06-15 04:39+0000\n"
|
||||
"X-Generator: Launchpad (build 13168)\n"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.filters:0
|
||||
@@ -24,7 +24,7 @@ msgstr ""
|
||||
#: field:ir.rule,domain_force:0
|
||||
#: field:res.partner.title,domain:0
|
||||
msgid "Domain"
|
||||
msgstr "Miền"
|
||||
msgstr "Miền giá trị"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
@@ -130,9 +130,9 @@ msgid ""
|
||||
"specified as a Python expression defining a list of triplets. For example: "
|
||||
"[('color','=','red')]"
|
||||
msgstr ""
|
||||
"Miền tùy chọn dùng để giới hạn các giá trị có thể đối với các trường quan "
|
||||
"hệ, được chỉ định bởi một biểu thức Python định nghĩa một danh sách của các "
|
||||
"bộ ba. Ví dụ: [('màu'='đỏ')]"
|
||||
"Miền giá trị tùy chọn dùng để giới hạn các giá trị có thể đối với các trường "
|
||||
"quan hệ, được chỉ định bởi một biểu thức Python định nghĩa một danh sách của "
|
||||
"các bộ ba. Ví dụ: [('màu'='đỏ')]"
|
||||
|
||||
#. module: base
|
||||
#: field:res.partner,ref:0
|
||||
@@ -3787,7 +3787,7 @@ msgstr ""
|
||||
#: field:ir.attachment,res_name:0
|
||||
#: field:ir.ui.view_sc,resource:0
|
||||
msgid "Resource Name"
|
||||
msgstr "Tên tài nguyên"
|
||||
msgstr "Tên Tài nguyên"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.cron,interval_type:0
|
||||
@@ -5381,7 +5381,7 @@ msgstr ""
|
||||
#: code:addons/orm.py:3532
|
||||
#, python-format
|
||||
msgid "UserError"
|
||||
msgstr "UserError"
|
||||
msgstr "Lỗi người sử dụng"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.ae
|
||||
@@ -8841,7 +8841,7 @@ msgstr ""
|
||||
#: code:addons/base/res/partner/partner.py:250
|
||||
#, python-format
|
||||
msgid "Warning"
|
||||
msgstr "Warning"
|
||||
msgstr "Cảnh báo"
|
||||
|
||||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# 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.
|
||||
#
|
||||
# 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
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from osv import fields,osv
|
||||
|
||||
class board(osv.osv):
|
||||
_name = 'ir.board'
|
||||
def create(self, cr, user, vals, context={}):
|
||||
return False
|
||||
def copy(self, cr, uid, id, default=None, context={}):
|
||||
return False
|
||||
_columns = {
|
||||
'name': fields.char('Board', size=64),
|
||||
}
|
||||
board()
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -44,15 +44,10 @@ class ir_translation(osv.osv):
|
||||
_log_access = False
|
||||
|
||||
def _get_language(self, cr, uid, context):
|
||||
lang_obj = self.pool.get('res.lang')
|
||||
lang_ids = lang_obj.search(cr, uid, [('translatable', '=', True)],
|
||||
context=context)
|
||||
langs = lang_obj.browse(cr, uid, lang_ids, context=context)
|
||||
res = [(lang.code, lang.name) for lang in langs]
|
||||
for lang_dict in tools.scan_languages():
|
||||
if lang_dict not in res:
|
||||
res.append(lang_dict)
|
||||
return res
|
||||
lang_model = self.pool.get('res.lang')
|
||||
lang_ids = lang_model.search(cr, uid, [('translatable', '=', True)], context=context)
|
||||
lang_data = lang_model.read(cr, uid, lang_ids, ['code', 'name'], context=context)
|
||||
return [(d['code'], d['name']) for d in lang_data]
|
||||
|
||||
_columns = {
|
||||
'name': fields.char('Field Name', size=128, required=True),
|
||||
|
||||
@@ -46,6 +46,7 @@ def _check_xml(self, cr, uid, ids, context=None):
|
||||
|
||||
class view_custom(osv.osv):
|
||||
_name = 'ir.ui.view.custom'
|
||||
_order = 'create_date desc' # search(limit=1) should return the last customization
|
||||
_columns = {
|
||||
'ref_id': fields.many2one('ir.ui.view', 'Original View', select=True),
|
||||
'user_id': fields.many2one('res.users', 'User', select=True),
|
||||
|
||||
@@ -106,12 +106,13 @@ class ir_values(osv.osv):
|
||||
value = pickle.dumps(value)
|
||||
if meta:
|
||||
meta = pickle.dumps(meta)
|
||||
assert isinstance(models, (list, tuple)), models
|
||||
ids_res = []
|
||||
for model in models:
|
||||
if isinstance(model, (list, tuple)):
|
||||
model,res_id = model
|
||||
else:
|
||||
res_id=False
|
||||
res_id = False
|
||||
if replace:
|
||||
search_criteria = [
|
||||
('key', '=', key),
|
||||
@@ -148,11 +149,13 @@ class ir_values(osv.osv):
|
||||
if context is None:
|
||||
context = {}
|
||||
result = []
|
||||
assert isinstance(models, (list, tuple)), models
|
||||
|
||||
for m in models:
|
||||
if isinstance(m, (list, tuple)):
|
||||
m, res_id = m
|
||||
else:
|
||||
res_id=False
|
||||
res_id = False
|
||||
|
||||
where = ['key=%s','model=%s']
|
||||
params = [key, str(m)]
|
||||
@@ -161,7 +164,7 @@ class ir_values(osv.osv):
|
||||
params.append(key2[:200])
|
||||
elif key2_req and not meta:
|
||||
where.append('key2 is null')
|
||||
if res_id_req and (models[-1][0]==m):
|
||||
if res_id_req and (models[-1][0] == m):
|
||||
if res_id:
|
||||
where.append('res_id=%s')
|
||||
params.append(res_id)
|
||||
|
||||
@@ -158,7 +158,7 @@ showpage'''
|
||||
(No workflow instance defined) show
|
||||
showpage'''
|
||||
else:
|
||||
graph = pydot.Dot(
|
||||
graph = pydot.Dot(graph_name=data['model'].replace('.','_'),
|
||||
fontsize='16',
|
||||
label="""\\\n\\nWorkflow: %s\\n OSV: %s""" % (wkfinfo['name'],wkfinfo['osv']),
|
||||
size='7.3, 10.1', center='1', ratio='auto', rotate='0', rankdir='TB',
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
##############################################################################
|
||||
|
||||
import time
|
||||
from osv import fields,osv
|
||||
|
||||
from osv import osv
|
||||
from osv import fields
|
||||
|
||||
#
|
||||
# Sale/Purchase Canal, Media
|
||||
@@ -31,7 +31,7 @@ class res_partner_canal(osv.osv):
|
||||
_description = "Channels"
|
||||
_order = 'name'
|
||||
_columns = {
|
||||
'name': fields.char('Channel Name',size=64, required=True),
|
||||
'name': fields.char('Channel Name', size=64, required=True),
|
||||
'active': fields.boolean('Active'),
|
||||
}
|
||||
_defaults = {
|
||||
@@ -39,16 +39,10 @@ class res_partner_canal(osv.osv):
|
||||
}
|
||||
res_partner_canal()
|
||||
|
||||
def _links_get(self, cr, uid, context={}):
|
||||
obj = self.pool.get('res.request.link')
|
||||
ids = obj.search(cr, uid, [])
|
||||
res = obj.read(cr, uid, ids, ['object', 'name'], context)
|
||||
return [(r['object'], r['name']) for r in res]
|
||||
|
||||
class res_partner_event(osv.osv):
|
||||
_name = "res.partner.event"
|
||||
_columns = {
|
||||
'name': fields.char('Events',size=64, required=True),
|
||||
'name': fields.char('Events', size=64, required=True),
|
||||
'description': fields.text('Description'),
|
||||
'partner_id': fields.many2one('res.partner', 'Partner', select=True),
|
||||
'date': fields.datetime('Date', size=16),
|
||||
@@ -61,5 +55,4 @@ class res_partner_event(osv.osv):
|
||||
}
|
||||
res_partner_event()
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -188,40 +188,27 @@ class res_company(osv.osv):
|
||||
tools.config['root_path'], '..', 'pixmaps', 'openerp-header.png'),
|
||||
'rb') .read().encode('base64')
|
||||
|
||||
def _get_header3(self,cr,uid,ids):
|
||||
return """
|
||||
_header = """
|
||||
<header>
|
||||
<pageTemplate>
|
||||
<frame id="first" x1="28.0" y1="28.0" width="786" height="525"/>
|
||||
<frame id="first" x1="28.0" y1="28.0" width="%s" height="%s"/>
|
||||
<pageGraphics>
|
||||
<fill color="black"/>
|
||||
<stroke color="black"/>
|
||||
<setFont name="DejaVu Sans" size="8"/>
|
||||
<drawString x="25" y="555"> [[ formatLang(time.strftime("%Y-%m-%d"), date=True) ]] [[ time.strftime("%H:%M") ]]</drawString>
|
||||
<drawString x="%s" y="%s"> [[ formatLang(time.strftime("%%Y-%%m-%%d"), date=True) ]] [[ time.strftime("%%H:%%M") ]]</drawString>
|
||||
<setFont name="DejaVu Sans Bold" size="10"/>
|
||||
<drawString x="382" y="555">[[ company.partner_id.name ]]</drawString>
|
||||
<drawCentredString x="%s" y="%s">[[ company.partner_id.name ]]</drawCentredString>
|
||||
<stroke color="#000000"/>
|
||||
<lines>25 550 818 550</lines>
|
||||
<lines>%s</lines>
|
||||
</pageGraphics>
|
||||
</pageTemplate>
|
||||
</header>"""
|
||||
def _get_header2(self,cr,uid,ids):
|
||||
return """
|
||||
<header>
|
||||
<pageTemplate>
|
||||
<frame id="first" x1="28.0" y1="28.0" width="539" height="772"/>
|
||||
<pageGraphics>
|
||||
<fill color="black"/>
|
||||
<stroke color="black"/>
|
||||
<setFont name="DejaVu Sans" size="8"/>
|
||||
<drawString x="1.0cm" y="28.3cm"> [[ formatLang(time.strftime("%Y-%m-%d"), date=True) ]] [[ time.strftime("%H:%M") ]]</drawString>
|
||||
<setFont name="DejaVu Sans Bold" size="10"/>
|
||||
<drawString x="9.3cm" y="28.3cm">[[ company.partner_id.name ]]</drawString>
|
||||
<stroke color="#000000"/>
|
||||
<lines>1.0cm 28.1cm 20.1cm 28.1cm</lines>
|
||||
</pageGraphics>
|
||||
</pageTemplate>
|
||||
</pageTemplate>
|
||||
</header>"""
|
||||
|
||||
_header2 = _header % (539, 772, "1.0cm", "28.3cm", "11.1cm", "28.3cm", "1.0cm 28.1cm 20.1cm 28.1cm")
|
||||
|
||||
_header3 = _header % (786, 525, 25, 555, 440, 555, "25 550 818 550")
|
||||
|
||||
def _get_header(self,cr,uid,ids):
|
||||
try :
|
||||
header_file = tools.file_open(os.path.join('base', 'report', 'corporate_rml_header.rml'))
|
||||
@@ -267,8 +254,8 @@ class res_company(osv.osv):
|
||||
_defaults = {
|
||||
'currency_id': _get_euro,
|
||||
'rml_header':_get_header,
|
||||
'rml_header2': _get_header2,
|
||||
'rml_header3': _get_header3,
|
||||
'rml_header2': _header2,
|
||||
'rml_header3': _header3,
|
||||
#'logo':_get_logo
|
||||
}
|
||||
|
||||
|
||||
@@ -50,9 +50,9 @@ class lang(osv.osv):
|
||||
if not lang:
|
||||
return False
|
||||
lang_ids = self.search(cr, uid, [('code','=', lang)])
|
||||
ir_values_obj = self.pool.get('ir.values')
|
||||
if not lang_ids:
|
||||
lang_id = self.load_lang(cr, uid, lang)
|
||||
self.load_lang(cr, uid, lang)
|
||||
ir_values_obj = self.pool.get('ir.values')
|
||||
default_value = ir_values_obj.get(cr, uid, 'default', False, ['res.partner'])
|
||||
if not default_value:
|
||||
ir_values_obj.set(cr, uid, 'default', False, 'lang', ['res.partner'], lang)
|
||||
|
||||
@@ -38,7 +38,7 @@ class res_widget_user(osv.osv):
|
||||
_order = "sequence"
|
||||
_columns = {
|
||||
'sequence': fields.integer('Sequence'),
|
||||
'user_id': fields.many2one('res.users','User', select=1),
|
||||
'user_id': fields.many2one('res.users','User', select=1, ondelete='cascade'),
|
||||
'widget_id': fields.many2one('res.widget','Widget',required=True, ondelete='cascade'),
|
||||
}
|
||||
|
||||
|
||||
@@ -274,22 +274,17 @@ def init_module_models(cr, module_name, obj_list):
|
||||
|
||||
logger.notifyChannel('init', netsvc.LOG_INFO,
|
||||
'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 = []
|
||||
for obj in obj_list:
|
||||
try:
|
||||
# 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
|
||||
result = obj._auto_init(cr, {'module': module_name})
|
||||
if result:
|
||||
todo += result
|
||||
if hasattr(obj, 'init'):
|
||||
obj.init(cr)
|
||||
cr.commit()
|
||||
for obj in obj_list:
|
||||
obj._auto_end(cr, {'module': module_name})
|
||||
cr.commit()
|
||||
todo.sort()
|
||||
for t in todo:
|
||||
t[1](cr, *t[2])
|
||||
|
||||
+88
-40
@@ -24,87 +24,135 @@
|
||||
"""
|
||||
|
||||
import openerp.sql_db
|
||||
import openerp.osv.orm
|
||||
|
||||
|
||||
class BoundRegistry(object):
|
||||
""" Model registry/database connection pair."""
|
||||
def __init__(self, db, registry):
|
||||
self.db = db
|
||||
self.registry = registry
|
||||
class Registry(object):
|
||||
""" 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.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, db_name):
|
||||
self.models = {} # model name/model instance mapping
|
||||
self._sql_error = {}
|
||||
self._store_function = {}
|
||||
self._init = True
|
||||
self._init_parent = {}
|
||||
self.db_name = db_name
|
||||
self.db = openerp.sql_db.db_connect(db_name)
|
||||
|
||||
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."""
|
||||
return self.models.keys()
|
||||
|
||||
def add(self, model_name, model):
|
||||
""" Add or replace a model in the registry."""
|
||||
self.models[model_name] = model
|
||||
|
||||
def get(self, model_name):
|
||||
""" Return a model for a given name or None if it doesn't exist."""
|
||||
return self.models.get(model_name)
|
||||
|
||||
def __getitem__(self, model_name):
|
||||
""" Return a model for a given name or raise KeyError if it doesn't exist."""
|
||||
return self.models[model_name]
|
||||
|
||||
def instanciate(self, module, cr):
|
||||
""" Instanciate all the classes of a given module for a particular db."""
|
||||
|
||||
res = []
|
||||
|
||||
# Instanciate classes registered through their constructor and
|
||||
# add them to the pool.
|
||||
for klass in openerp.osv.orm.module_class_list.get(module, []):
|
||||
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
|
||||
|
||||
|
||||
class RegistryManager(object):
|
||||
""" Model registries manager.
|
||||
|
||||
The manager is responsible for creation and deletion of bound model
|
||||
The manager is responsible for creation and deletion of model
|
||||
registries (essentially database connection/model registry pairs).
|
||||
|
||||
"""
|
||||
|
||||
# TODO maybe should receive the addons paths
|
||||
def __init__(self):
|
||||
# Mapping between db name and bound model registry.
|
||||
# Accessed through the methods below.
|
||||
self.bound_registries = {}
|
||||
# Mapping between db name and model registry.
|
||||
# Accessed through the methods below.
|
||||
registries = {}
|
||||
|
||||
|
||||
def get(self, db_name, force_demo=False, status=None, update_module=False,
|
||||
@classmethod
|
||||
def get(cls, db_name, force_demo=False, status=None, update_module=False,
|
||||
pooljobs=True):
|
||||
""" Return a bound registry for a given database name."""
|
||||
""" Return a registry for a given database name."""
|
||||
|
||||
if db_name in self.bound_registries:
|
||||
bound_registry = self.bound_registries[db_name]
|
||||
if db_name in cls.registries:
|
||||
registry = cls.registries[db_name]
|
||||
else:
|
||||
bound_registry = self.new(db_name, force_demo, status,
|
||||
registry = cls.new(db_name, force_demo, status,
|
||||
update_module, pooljobs)
|
||||
return bound_registry
|
||||
return registry
|
||||
|
||||
|
||||
def new(self, db_name, force_demo=False, status=None,
|
||||
@classmethod
|
||||
def new(cls, db_name, force_demo=False, status=None,
|
||||
update_module=False, pooljobs=True):
|
||||
""" Create and return a new bound registry for a given database name.
|
||||
""" Create and return a new registry for a given database name.
|
||||
|
||||
The (possibly) previous bound registry for that database name is
|
||||
discarded.
|
||||
The (possibly) previous registry for that database name is discarded.
|
||||
|
||||
"""
|
||||
|
||||
import openerp.modules
|
||||
import openerp.osv.osv as osv_osv
|
||||
db = openerp.sql_db.db_connect(db_name)
|
||||
pool = osv_osv.osv_pool()
|
||||
registry = Registry(db_name)
|
||||
|
||||
# Initializing a registry will call general code which will in turn
|
||||
# call registries.get (this object) to obtain the registry being
|
||||
# initialized. Make it available in the bound_registries dictionary
|
||||
# then remove it if an exception is raised.
|
||||
self.delete(db_name)
|
||||
bound_registry = BoundRegistry(db, pool)
|
||||
self.bound_registries[db_name] = bound_registry
|
||||
# initialized. Make it available in the registries dictionary then
|
||||
# remove it if an exception is raised.
|
||||
cls.delete(db_name)
|
||||
cls.registries[db_name] = registry
|
||||
try:
|
||||
# This should be a method on BoundRegistry
|
||||
openerp.modules.load_modules(db, force_demo, status, update_module)
|
||||
# This should be a method on Registry
|
||||
openerp.modules.load_modules(registry.db, force_demo, status, update_module)
|
||||
except Exception:
|
||||
del self.bound_registries[db_name]
|
||||
del cls.registries[db_name]
|
||||
raise
|
||||
|
||||
cr = db.cursor()
|
||||
cr = registry.db.cursor()
|
||||
try:
|
||||
pool.do_parent_store(cr)
|
||||
pool.get('ir.actions.report.xml').register_all(cr)
|
||||
registry.do_parent_store(cr)
|
||||
registry.get('ir.actions.report.xml').register_all(cr)
|
||||
cr.commit()
|
||||
finally:
|
||||
cr.close()
|
||||
|
||||
if pooljobs:
|
||||
pool.get('ir.cron').restart(db.dbname)
|
||||
registry.get('ir.cron').restart(registry.db.dbname)
|
||||
|
||||
return bound_registry
|
||||
return registry
|
||||
|
||||
|
||||
def delete(self, db_name):
|
||||
if db_name in self.bound_registries:
|
||||
del self.bound_registries[db_name]
|
||||
@classmethod
|
||||
def delete(cls, db_name):
|
||||
""" Delete the registry linked to a given database. """
|
||||
if db_name in cls.registries:
|
||||
del cls.registries[db_name]
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
+28
-18
@@ -24,23 +24,45 @@
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import cgitb
|
||||
import errno
|
||||
import heapq
|
||||
import logging
|
||||
import logging.handlers
|
||||
import os
|
||||
import platform
|
||||
import release
|
||||
import socket
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
import release
|
||||
from pprint import pformat
|
||||
import warnings
|
||||
import heapq
|
||||
import types
|
||||
from pprint import pformat
|
||||
|
||||
# TODO modules that import netsvc only for things from loglevels must be changed to use loglevels.
|
||||
from loglevels import *
|
||||
import tools
|
||||
|
||||
def close_socket(sock):
|
||||
""" Closes a socket instance cleanly
|
||||
|
||||
:param sock: the network socket to close
|
||||
:type sock: socket.socket
|
||||
"""
|
||||
try:
|
||||
sock.shutdown(socket.SHUT_RDWR)
|
||||
except socket.error, e:
|
||||
# On OSX, socket shutdowns both sides if any side closes it
|
||||
# causing an error 57 'Socket is not connected' on shutdown
|
||||
# of the other side (or something), see
|
||||
# http://bugs.python.org/issue4397
|
||||
# note: stdlib fixed test, not behavior
|
||||
if e.errno != errno.ENOTCONN or platform.system() != 'Darwin':
|
||||
raise
|
||||
sock.close()
|
||||
|
||||
|
||||
class Service(object):
|
||||
""" Base class for *Local* services
|
||||
|
||||
@@ -380,19 +402,7 @@ class Server:
|
||||
return '\n'.join(res)
|
||||
|
||||
def _close_socket(self):
|
||||
if os.name != 'nt':
|
||||
try:
|
||||
self.socket.shutdown(getattr(socket, 'SHUT_RDWR', 2))
|
||||
except socket.error, e:
|
||||
if e.errno != errno.ENOTCONN: raise
|
||||
# OSX, socket shutdowns both sides if any side closes it
|
||||
# causing an error 57 'Socket is not connected' on shutdown
|
||||
# of the other side (or something), see
|
||||
# http://bugs.python.org/issue4397
|
||||
self.__logger.debug(
|
||||
'"%s" when shutting down server socket, '
|
||||
'this is normal under OS X', e)
|
||||
self.socket.close()
|
||||
close_socket(self.socket)
|
||||
|
||||
class OpenERPDispatcherException(Exception):
|
||||
def __init__(self, exception, traceback):
|
||||
@@ -430,8 +440,8 @@ class OpenERPDispatcher:
|
||||
except Exception, e:
|
||||
self.log('exception', tools.exception_to_unicode(e))
|
||||
tb = getattr(e, 'traceback', sys.exc_info())
|
||||
tb_s = "".join(traceback.format_exception(*tb))
|
||||
if tools.config['debug_mode']:
|
||||
tb_s = cgitb.text(tb)
|
||||
if tools.config['debug_mode'] and isinstance(tb, types.TracebackType):
|
||||
import pdb
|
||||
pdb.post_mortem(tb[2])
|
||||
raise OpenERPDispatcherException(e, tb_s)
|
||||
|
||||
+93
-44
@@ -692,24 +692,21 @@ class many2many(_column):
|
||||
obj.datas[id][name] = act[2]
|
||||
|
||||
|
||||
def get_nice_size(a):
|
||||
(x,y) = a
|
||||
if isinstance(y, (int,long)):
|
||||
size = y
|
||||
elif y:
|
||||
size = len(y)
|
||||
else:
|
||||
size = 0
|
||||
return (x, tools.human_size(size))
|
||||
def get_nice_size(value):
|
||||
size = 0
|
||||
if isinstance(value, (int,long)):
|
||||
size = value
|
||||
elif value: # this is supposed to be a string
|
||||
size = len(value)
|
||||
return tools.human_size(size)
|
||||
|
||||
def sanitize_binary_value(dict_item):
|
||||
def sanitize_binary_value(value):
|
||||
# binary fields should be 7-bit ASCII base64-encoded data,
|
||||
# but we do additional sanity checks to make sure the values
|
||||
# are not something else that won't pass via xmlrpc
|
||||
index, value = dict_item
|
||||
if isinstance(value, (xmlrpclib.Binary, tuple, list, dict)):
|
||||
# these builtin types are meant to pass untouched
|
||||
return index, value
|
||||
return value
|
||||
|
||||
# For all other cases, handle the value as a binary string:
|
||||
# it could be a 7-bit ASCII string (e.g base64 data), but also
|
||||
@@ -730,7 +727,7 @@ def sanitize_binary_value(dict_item):
|
||||
# is not likely to produce the expected output, but this is
|
||||
# just a safety mechanism (in these cases base64 data or
|
||||
# xmlrpc.Binary values should be used instead)
|
||||
return index, tools.ustr(value)
|
||||
return tools.ustr(value)
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
@@ -806,44 +803,44 @@ class function(_column):
|
||||
return []
|
||||
return self._fnct_search(obj, cr, uid, obj, name, args, context=context)
|
||||
|
||||
def get(self, cr, obj, ids, name, user=None, context=None, values=None):
|
||||
def postprocess(self, cr, uid, obj, field, value=None, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
if values is None:
|
||||
values = {}
|
||||
res = {}
|
||||
if self._method:
|
||||
res = self._fnct(obj, cr, user, ids, name, self._arg, context)
|
||||
else:
|
||||
res = self._fnct(cr, obj._table, ids, name, self._arg, context)
|
||||
result = value
|
||||
field_type = obj._columns[field]._type
|
||||
if field_type == "many2one":
|
||||
# make the result a tuple if it is not already one
|
||||
if isinstance(value, (int,long)) and hasattr(obj._columns[field], 'relation'):
|
||||
obj_model = obj.pool.get(obj._columns[field].relation)
|
||||
dict_names = dict(obj_model.name_get(cr, uid, [value], context))
|
||||
result = (value, dict_names[value])
|
||||
|
||||
if self._type == "many2one" :
|
||||
# Filtering only integer/long values if passed
|
||||
res_ids = [x for x in res.values() if x and isinstance(x, (int,long))]
|
||||
|
||||
if res_ids:
|
||||
obj_model = obj.pool.get(self._obj)
|
||||
dict_names = dict(obj_model.name_get(cr, user, res_ids, context))
|
||||
for r in res.keys():
|
||||
if res[r] and res[r] in dict_names:
|
||||
res[r] = (res[r], dict_names[res[r]])
|
||||
|
||||
if self._type == 'binary':
|
||||
if field_type == 'binary':
|
||||
if context.get('bin_size', False):
|
||||
# client requests only the size of binary fields
|
||||
res = dict(map(get_nice_size, res.items()))
|
||||
result = get_nice_size(value)
|
||||
else:
|
||||
res = dict(map(sanitize_binary_value, res.items()))
|
||||
result = sanitize_binary_value(value)
|
||||
|
||||
if field_type == "integer":
|
||||
result = tools.ustr(value)
|
||||
return result
|
||||
|
||||
def get(self, cr, obj, ids, name, uid=False, context=None, values=None):
|
||||
result = {}
|
||||
if self._method:
|
||||
result = self._fnct(obj, cr, uid, ids, name, self._arg, context)
|
||||
else:
|
||||
result = self._fnct(cr, obj._table, ids, name, self._arg, context)
|
||||
for id in ids:
|
||||
if self._multi and id in result:
|
||||
for field, value in result[id].iteritems():
|
||||
if value:
|
||||
result[id][field] = self.postprocess(cr, uid, obj, field, value, context)
|
||||
elif result.get(id):
|
||||
result[id] = self.postprocess(cr, uid, obj, name, result[id], context)
|
||||
return result
|
||||
|
||||
if self._type == "integer":
|
||||
for r in res.keys():
|
||||
# Converting value into string so that it does not affect XML-RPC Limits
|
||||
if isinstance(res[r],dict): # To treat integer values with _multi attribute
|
||||
for record in res[r].keys():
|
||||
res[r][record] = str(res[r][record])
|
||||
else:
|
||||
res[r] = str(res[r])
|
||||
return res
|
||||
get_memory = get
|
||||
|
||||
def set(self, cr, obj, id, name, value, user=None, context=None):
|
||||
@@ -1120,5 +1117,57 @@ class property(function):
|
||||
self.field_id = {}
|
||||
|
||||
|
||||
def field_to_dict(self, cr, user, context, field):
|
||||
""" Return a dictionary representation of a field.
|
||||
|
||||
The string, help, and selection attributes (if any) are untranslated. This
|
||||
representation is the one returned by fields_get() (fields_get() will do
|
||||
the translation).
|
||||
|
||||
"""
|
||||
|
||||
res = {'type': field._type}
|
||||
# This additional attributes for M2M and function field is added
|
||||
# because we need to display tooltip with this additional information
|
||||
# when client is started in debug mode.
|
||||
if isinstance(field, function):
|
||||
res['function'] = field._fnct and field._fnct.func_name or False
|
||||
res['store'] = field.store
|
||||
if isinstance(field.store, dict):
|
||||
res['store'] = str(field.store)
|
||||
res['fnct_search'] = field._fnct_search and field._fnct_search.func_name or False
|
||||
res['fnct_inv'] = field._fnct_inv and field._fnct_inv.func_name or False
|
||||
res['fnct_inv_arg'] = field._fnct_inv_arg or False
|
||||
res['func_obj'] = field._obj or False
|
||||
res['func_method'] = field._method
|
||||
if isinstance(field, many2many):
|
||||
res['related_columns'] = list((field._id1, field._id2))
|
||||
res['third_table'] = field._rel
|
||||
for arg in ('string', 'readonly', 'states', 'size', 'required', 'group_operator',
|
||||
'change_default', 'translate', 'help', 'select', 'selectable'):
|
||||
if getattr(field, arg):
|
||||
res[arg] = getattr(field, arg)
|
||||
for arg in ('digits', 'invisible', 'filters'):
|
||||
if getattr(field, arg, None):
|
||||
res[arg] = getattr(field, arg)
|
||||
|
||||
if field.string:
|
||||
res['string'] = field.string
|
||||
if field.help:
|
||||
res['help'] = field.help
|
||||
|
||||
if hasattr(field, 'selection'):
|
||||
if isinstance(field.selection, (tuple, list)):
|
||||
res['selection'] = field.selection
|
||||
else:
|
||||
# call the 'dynamic selection' function
|
||||
res['selection'] = field.selection(self, cr, user, context)
|
||||
if res['type'] in ('one2many', 'many2many', 'many2one', 'one2one'):
|
||||
res['relation'] = field._obj
|
||||
res['domain'] = field._domain
|
||||
res['context'] = field._context
|
||||
|
||||
return res
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
|
||||
+299
-226
@@ -68,6 +68,7 @@ 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.
|
||||
|
||||
@@ -416,6 +417,32 @@ def get_pg_type(f):
|
||||
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):
|
||||
""" Base class for OpenERP models.
|
||||
|
||||
@@ -445,6 +472,11 @@ class orm_template(object):
|
||||
_sequence = None
|
||||
_description = None
|
||||
_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
|
||||
_invalids = set()
|
||||
_log_create = False
|
||||
@@ -469,9 +501,11 @@ class orm_template(object):
|
||||
raise NotImplementedError(_('The read_group method is not implemented on this object !'))
|
||||
|
||||
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_data (if there is not already one, and if
|
||||
@@ -572,6 +606,9 @@ class orm_template(object):
|
||||
raise_on_invalid_object_name(self._name)
|
||||
self._field_create(cr, context=context)
|
||||
|
||||
def _auto_end(self, cr, context=None):
|
||||
pass
|
||||
|
||||
#
|
||||
# Goal: try to apply inheritance at the instanciation level and
|
||||
# put objects in the pool var
|
||||
@@ -637,7 +674,7 @@ class orm_template(object):
|
||||
else:
|
||||
new.extend(cls.__dict__.get(s, []))
|
||||
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.__init__(pool, cr)
|
||||
return obj
|
||||
@@ -830,9 +867,6 @@ class orm_template(object):
|
||||
if x=='.id': return [x]
|
||||
return x.replace(':id','/id').replace('.id','/.id').split('/')
|
||||
fields_to_export = map(fsplit, fields_to_export)
|
||||
fields_export = fields_to_export + []
|
||||
warning = ''
|
||||
warning_fields = []
|
||||
datas = []
|
||||
for row in self.browse(cr, uid, ids, context):
|
||||
datas += self.__export_row(cr, uid, row, fields_to_export, context)
|
||||
@@ -1020,7 +1054,6 @@ class orm_template(object):
|
||||
|
||||
if config.get('import_partial', False) and filename:
|
||||
data = pickle.load(file(config.get('import_partial')))
|
||||
original_value = data.get(filename, 0)
|
||||
|
||||
position = 0
|
||||
while position<len(datas):
|
||||
@@ -1033,7 +1066,7 @@ class orm_template(object):
|
||||
return (-1, res, 'Line ' + str(position) +' : ' + '!\n'.join(warning), '')
|
||||
|
||||
try:
|
||||
id = ir_model_data_obj._update(cr, uid, self._name,
|
||||
ir_model_data_obj._update(cr, uid, self._name,
|
||||
current_module, res, mode=mode, xml_id=xml_id,
|
||||
noupdate=noupdate, res_id=res_id, context=context)
|
||||
except Exception, e:
|
||||
@@ -1222,92 +1255,62 @@ class orm_template(object):
|
||||
|
||||
def fields_get_keys(self, cr, user, context=None):
|
||||
res = self._columns.keys()
|
||||
# TODO I believe this loop can be replace by
|
||||
# res.extend(self._inherit_fields.key())
|
||||
for parent in self._inherits:
|
||||
res.extend(self.pool.get(parent).fields_get_keys(cr, user, context))
|
||||
return res
|
||||
|
||||
# returns the definition of each field in the object
|
||||
# the optional fields parameter can limit the result to some fields
|
||||
|
||||
def fields_get(self, cr, user, allfields=None, context=None, write_access=True):
|
||||
""" Returns the definition of each field.
|
||||
|
||||
The returned value is a dictionary (indiced by field name) of
|
||||
dictionaries. The _inherits'd fields are included. The string,
|
||||
help, and selection (if present) attributes are translated.
|
||||
|
||||
"""
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
res = {}
|
||||
|
||||
translation_obj = self.pool.get('ir.translation')
|
||||
for parent in self._inherits:
|
||||
res.update(self.pool.get(parent).fields_get(cr, user, allfields, context))
|
||||
|
||||
if self._columns.keys():
|
||||
for f in self._columns.keys():
|
||||
field_col = self._columns[f]
|
||||
if allfields and f not in allfields:
|
||||
continue
|
||||
res[f] = {'type': field_col._type}
|
||||
# This additional attributes for M2M and function field is added
|
||||
# because we need to display tooltip with this additional information
|
||||
# when client is started in debug mode.
|
||||
if isinstance(field_col, fields.function):
|
||||
res[f]['function'] = field_col._fnct and field_col._fnct.func_name or False
|
||||
res[f]['store'] = field_col.store
|
||||
if isinstance(field_col.store, dict):
|
||||
res[f]['store'] = str(field_col.store)
|
||||
res[f]['fnct_search'] = field_col._fnct_search and field_col._fnct_search.func_name or False
|
||||
res[f]['fnct_inv'] = field_col._fnct_inv and field_col._fnct_inv.func_name or False
|
||||
res[f]['fnct_inv_arg'] = field_col._fnct_inv_arg or False
|
||||
res[f]['func_obj'] = field_col._obj or False
|
||||
res[f]['func_method'] = field_col._method
|
||||
if isinstance(field_col, fields.many2many):
|
||||
res[f]['related_columns'] = list((field_col._id1, field_col._id2))
|
||||
res[f]['third_table'] = field_col._rel
|
||||
for arg in ('string', 'readonly', 'states', 'size', 'required', 'group_operator',
|
||||
'change_default', 'translate', 'help', 'select', 'selectable'):
|
||||
if getattr(field_col, arg):
|
||||
res[f][arg] = getattr(field_col, arg)
|
||||
if not write_access:
|
||||
res[f]['readonly'] = True
|
||||
res[f]['states'] = {}
|
||||
for arg in ('digits', 'invisible', 'filters'):
|
||||
if getattr(field_col, arg, None):
|
||||
res[f][arg] = getattr(field_col, arg)
|
||||
for f, field in self._columns.iteritems():
|
||||
if allfields and f not in allfields:
|
||||
continue
|
||||
|
||||
if field_col.string:
|
||||
res_trans = translation_obj._get_source(cr, user, self._name + ',' + f, 'field', context.get('lang', False) or 'en_US')
|
||||
if res_trans:
|
||||
res[f]['string'] = res_trans
|
||||
if field_col.help:
|
||||
help_trans = translation_obj._get_source(cr, user, self._name + ',' + f, 'help', context.get('lang', False) or 'en_US')
|
||||
if help_trans:
|
||||
res[f]['help'] = help_trans
|
||||
res[f] = fields.field_to_dict(self, cr, user, context, field)
|
||||
|
||||
if hasattr(field_col, 'selection'):
|
||||
if isinstance(field_col.selection, (tuple, list)):
|
||||
sel = field_col.selection
|
||||
# translate each selection option
|
||||
sel2 = []
|
||||
for (key, val) in sel:
|
||||
val2 = None
|
||||
if val:
|
||||
val2 = translation_obj._get_source(cr, user, self._name + ',' + f, 'selection', context.get('lang', False) or 'en_US', val)
|
||||
sel2.append((key, val2 or val))
|
||||
sel = sel2
|
||||
res[f]['selection'] = sel
|
||||
else:
|
||||
# call the 'dynamic selection' function
|
||||
res[f]['selection'] = field_col.selection(self, cr, user, context)
|
||||
if res[f]['type'] in ('one2many', 'many2many', 'many2one', 'one2one'):
|
||||
res[f]['relation'] = field_col._obj
|
||||
res[f]['domain'] = field_col._domain
|
||||
res[f]['context'] = field_col._context
|
||||
else:
|
||||
#TODO : read the fields from the database
|
||||
pass
|
||||
if not write_access:
|
||||
res['readonly'] = True
|
||||
res['states'] = {}
|
||||
|
||||
if hasattr(res[f], 'string'):
|
||||
res_trans = translation_obj._get_source(cr, user, self._name + ',' + f, 'field', context.get('lang', False) or 'en_US')
|
||||
if res_trans:
|
||||
res[f]['string'] = res_trans
|
||||
if hasattr(res[f], 'help'):
|
||||
help_trans = translation_obj._get_source(cr, user, self._name + ',' + f, 'help', context.get('lang', False) or 'en_US')
|
||||
if help_trans:
|
||||
res[f]['help'] = help_trans
|
||||
if hasattr(res[f], 'selection'):
|
||||
if isinstance(field.selection, (tuple, list)):
|
||||
sel = field.selection
|
||||
sel2 = []
|
||||
for key, val in sel:
|
||||
val2 = None
|
||||
if val:
|
||||
val2 = translation_obj._get_source(cr, user, self._name + ',' + f, 'selection', context.get('lang', False) or 'en_US', val)
|
||||
sel2.append((key, val2 or val))
|
||||
res[f]['selection'] = sel2
|
||||
|
||||
if allfields:
|
||||
# filter out fields which aren't in the fields list
|
||||
for r in res.keys():
|
||||
if r not in allfields:
|
||||
del res[r]
|
||||
return res
|
||||
|
||||
|
||||
#
|
||||
# Overload this method if you need a window title which depends on the context
|
||||
#
|
||||
@@ -1599,7 +1602,7 @@ class orm_template(object):
|
||||
:raise Invalid ArchitectureError: if there is view type other than form, tree, calendar, search etc defined on the structure
|
||||
|
||||
"""
|
||||
if not context:
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
def encode(s):
|
||||
@@ -1609,10 +1612,8 @@ class orm_template(object):
|
||||
|
||||
def raise_view_error(error_msg, child_view_id):
|
||||
view, child_view = self.pool.get('ir.ui.view').browse(cr, user, [view_id, child_view_id], context)
|
||||
raise AttributeError(("View definition error for inherited view '%(xml_id)s' on '%(model)s' model: " + error_msg)
|
||||
% { 'xml_id': child_view.xml_id,
|
||||
'parent_xml_id': view.xml_id,
|
||||
'model': self._name, })
|
||||
raise AttributeError("View definition error for inherited view '%s' on model '%s': %s"
|
||||
% (child_view.xml_id, self._name, error_msg))
|
||||
|
||||
def _inherit_apply(src, inherit, inherit_id=None):
|
||||
def _find(node, node2):
|
||||
@@ -1705,7 +1706,6 @@ class orm_template(object):
|
||||
result = {'type': view_type, 'model': self._name}
|
||||
|
||||
ok = True
|
||||
model = True
|
||||
sql_res = False
|
||||
parent_view_model = None
|
||||
while ok:
|
||||
@@ -1739,7 +1739,6 @@ class orm_template(object):
|
||||
|
||||
ok = sql_res[5]
|
||||
view_id = ok or sql_res[3]
|
||||
model = False
|
||||
parent_view_model = sql_res[6]
|
||||
|
||||
# if a view was found
|
||||
@@ -1957,6 +1956,23 @@ class orm_template(object):
|
||||
"""
|
||||
return self._name_search(cr, user, name, args, operator, context, limit)
|
||||
|
||||
def name_create(self, cr, uid, name, context=None):
|
||||
"""
|
||||
Creates a new record by calling :py:meth:`~osv.osv.osv.create` with only one
|
||||
value provided: the name of the new record (``_rec_name`` field).
|
||||
The new record will also be initialized with any default values applicable
|
||||
to this model, or provided through the context. The usual behavior of
|
||||
:py:meth:`~osv.osv.osv.create` applies.
|
||||
Similarly, this method may raise an exception if the model has multiple
|
||||
required fields and some do not have default values.
|
||||
|
||||
:param name: name of the record to create
|
||||
|
||||
:return: the :py:meth:`~osv.osv.osv.name_get` value for the newly-created record.
|
||||
"""
|
||||
rec_id = self.create(cr, uid, {self._rec_name: name}, context);
|
||||
return self.name_get(cr, uid, [rec_id], context)[0]
|
||||
|
||||
# private implementation of name_search, allows passing a dedicated user for the name_get part to
|
||||
# solve some access rights issues
|
||||
def _name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100, name_get_uid=None):
|
||||
@@ -2544,149 +2560,74 @@ class orm(orm_template):
|
||||
- alter existing database columns to match _columns,
|
||||
- create database tables 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)
|
||||
if context is None:
|
||||
context = {}
|
||||
store_compute = False
|
||||
create = False
|
||||
todo_end = []
|
||||
update_custom_fields = context.get('update_custom_fields', False)
|
||||
self._field_create(cr, context=context)
|
||||
create = not self._table_exist(cr)
|
||||
|
||||
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:
|
||||
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("'", "''")))
|
||||
create = True
|
||||
self.__schema.debug("Table '%s': created", self._table)
|
||||
|
||||
if create:
|
||||
self._create_table(cr)
|
||||
|
||||
cr.commit()
|
||||
if self._parent_store:
|
||||
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'))
|
||||
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()
|
||||
if not self._parent_columns_exist(cr):
|
||||
self._create_parent_columns(cr)
|
||||
store_compute = True
|
||||
|
||||
# Create the create_uid, create_date, write_uid, write_date, columns if desired.
|
||||
if self._log_access:
|
||||
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])
|
||||
self._add_log_columns(cr)
|
||||
|
||||
self._check_removed_columns(cr, log=False)
|
||||
|
||||
# iterate on the "object columns"
|
||||
todo_update_store = []
|
||||
update_custom_fields = context.get('update_custom_fields', False)
|
||||
column_data = self._select_column_data(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,))
|
||||
col_data = dict(map(lambda x: (x['attname'], x),cr.dictfetchall()))
|
||||
|
||||
|
||||
for k in self._columns:
|
||||
for k, f in self._columns.iteritems():
|
||||
if k in ('id', 'write_uid', 'write_date', 'create_uid', 'create_date'):
|
||||
continue
|
||||
#Not Updating Custom fields
|
||||
if k.startswith('x_') and not update_custom_fields:
|
||||
# Don't update custom (also called manual) fields
|
||||
if f.manual and not update_custom_fields:
|
||||
continue
|
||||
|
||||
f = self._columns[k]
|
||||
|
||||
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):
|
||||
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
|
||||
# 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)
|
||||
self._m2m_raise_or_create_relation(cr, f)
|
||||
|
||||
else:
|
||||
res = col_data.get(k, [])
|
||||
res = res and [res] or []
|
||||
res = column_data.get(k)
|
||||
|
||||
# The field is not found as-is in database, try if it
|
||||
# exists with an old name.
|
||||
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 " \
|
||||
"FROM pg_class c,pg_attribute a,pg_type t " \
|
||||
"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:
|
||||
res = column_data.get(f.oldname)
|
||||
if res:
|
||||
cr.execute('ALTER TABLE "%s" RENAME "%s" TO "%s"' % (self._table, f.oldname, k))
|
||||
res = res_old
|
||||
res[0]['attname'] = k
|
||||
res['attname'] = k
|
||||
column_data[k] = res
|
||||
self.__schema.debug("Table '%s': renamed column '%s' to '%s'",
|
||||
self._table, f.oldname, k)
|
||||
|
||||
if len(res) == 1:
|
||||
f_pg_def = res[0]
|
||||
f_pg_type = f_pg_def['typname']
|
||||
f_pg_size = f_pg_def['size']
|
||||
f_pg_notnull = f_pg_def['attnotnull']
|
||||
# The field already exists in database. Possibly
|
||||
# change its type, rename it, drop it or change its
|
||||
# constraints.
|
||||
if res:
|
||||
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\
|
||||
not getattr(f, 'nodrop', False):
|
||||
self.__logger.info('column %s (%s) in table %s removed: converted to a function !\n',
|
||||
@@ -2824,13 +2765,13 @@ class orm(orm_template):
|
||||
if res2:
|
||||
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 + '" ADD FOREIGN KEY ("' + k + '") REFERENCES "' + ref + '" ON DELETE ' + f.ondelete)
|
||||
self._foreign_keys.append((self._table, k, ref, f.ondelete))
|
||||
cr.commit()
|
||||
self.__schema.debug("Table '%s': column '%s': XXX",
|
||||
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))
|
||||
if not res:
|
||||
|
||||
# The field doesn't exist in database. Create it if necessary.
|
||||
else:
|
||||
if not isinstance(f, fields.function) or f.store:
|
||||
# add the missing field
|
||||
cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, get_pg_type(f)[1]))
|
||||
@@ -2851,21 +2792,21 @@ class orm(orm_template):
|
||||
cr.commit()
|
||||
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):
|
||||
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]
|
||||
todo_update_store.append((order, f, k))
|
||||
todo_end.append((order, self._update_store, (f, k)))
|
||||
|
||||
# and add constraints if needed
|
||||
if isinstance(f, fields.many2one):
|
||||
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('.', '_')
|
||||
# ir_actions is inherited so foreign key doesn't work on it
|
||||
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._table, k, ref, f.ondelete)
|
||||
if f.select:
|
||||
@@ -2883,8 +2824,6 @@ class orm(orm_template):
|
||||
"ALTER TABLE %s ALTER COLUMN %s SET NOT NULL"
|
||||
self.__logger.warn(msg, k, self._table, self._table, k)
|
||||
cr.commit()
|
||||
for order, f, k in todo_update_store:
|
||||
todo_end.append((order, self._update_store, (f, k)))
|
||||
|
||||
else:
|
||||
cr.execute("SELECT relname FROM pg_class WHERE relkind IN ('r','v') AND relname=%s", (self._table,))
|
||||
@@ -2892,6 +2831,134 @@ class orm(orm_template):
|
||||
|
||||
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:
|
||||
conname = '%s_%s' % (self._table, key)
|
||||
|
||||
@@ -2940,17 +3007,16 @@ class orm(orm_template):
|
||||
self.__schema.warn(sql_action['msg_err'])
|
||||
cr.rollback()
|
||||
|
||||
if create:
|
||||
if hasattr(self, "_sql"):
|
||||
for line in self._sql.split(';'):
|
||||
line2 = line.replace('\n', '').strip()
|
||||
if line2:
|
||||
cr.execute(line2)
|
||||
cr.commit()
|
||||
if store_compute:
|
||||
self._parent_store_compute(cr)
|
||||
cr.commit()
|
||||
return todo_end
|
||||
|
||||
def _execute_sql(self, cr):
|
||||
""" Execute the SQL code from the _sql attribute (if any)."""
|
||||
if hasattr(self, "_sql"):
|
||||
for line in self._sql.split(';'):
|
||||
line2 = line.replace('\n', '').strip()
|
||||
if line2:
|
||||
cr.execute(line2)
|
||||
cr.commit()
|
||||
|
||||
|
||||
@classmethod
|
||||
def createInstance(cls, pool, cr):
|
||||
@@ -3029,18 +3095,18 @@ class orm(orm_template):
|
||||
}
|
||||
|
||||
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':
|
||||
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':
|
||||
self._columns[field['name']] = getattr(fields, field['ttype'])(field['relation'], **attrs)
|
||||
self._columns[field['name']] = fields.many2one(field['relation'], **attrs)
|
||||
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':
|
||||
_rel1 = field['relation'].replace('.', '_')
|
||||
_rel2 = field['model'].replace('.', '_')
|
||||
_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:
|
||||
self._columns[field['name']] = getattr(fields, field['ttype'])(**attrs)
|
||||
self._inherits_check()
|
||||
@@ -3059,18 +3125,25 @@ class orm(orm_template):
|
||||
#
|
||||
|
||||
def _inherits_reload_src(self):
|
||||
for obj in self.pool.obj_pool.values():
|
||||
""" Recompute the _inherit_fields mapping on each _inherits'd child model."""
|
||||
for obj in self.pool.models.values():
|
||||
if self._name in obj._inherits:
|
||||
obj._inherits_reload()
|
||||
|
||||
def _inherits_reload(self):
|
||||
""" Recompute the _inherit_fields mapping.
|
||||
|
||||
This will also call itself on each inherits'd child model.
|
||||
|
||||
"""
|
||||
res = {}
|
||||
for table in self._inherits:
|
||||
res.update(self.pool.get(table)._inherit_fields)
|
||||
for col in self.pool.get(table)._columns.keys():
|
||||
res[col] = (table, self._inherits[table], self.pool.get(table)._columns[col])
|
||||
for col in self.pool.get(table)._inherit_fields.keys():
|
||||
res[col] = (table, self._inherits[table], self.pool.get(table)._inherit_fields[col][2])
|
||||
other = self.pool.get(table)
|
||||
res.update(other._inherit_fields)
|
||||
for col in other._columns.keys():
|
||||
res[col] = (table, self._inherits[table], other._columns[col])
|
||||
for col in other._inherit_fields.keys():
|
||||
res[col] = (table, self._inherits[table], other._inherit_fields[col][2])
|
||||
self._inherit_fields = res
|
||||
self._inherits_reload_src()
|
||||
|
||||
|
||||
+10
-55
@@ -23,18 +23,16 @@
|
||||
# OSV: Objects Services
|
||||
#
|
||||
|
||||
import sys
|
||||
import inspect
|
||||
import orm
|
||||
import openerp.netsvc as netsvc
|
||||
import openerp.pooler as pooler
|
||||
import openerp.sql_db as sql_db
|
||||
import copy
|
||||
import logging
|
||||
from psycopg2 import IntegrityError, errorcodes
|
||||
from openerp.tools.func import wraps
|
||||
from openerp.tools.translate import translate
|
||||
from openerp.osv.orm import module_class_list
|
||||
from openerp.osv.orm import MetaModel
|
||||
|
||||
|
||||
class except_osv(Exception):
|
||||
def __init__(self, name, value, exc_type='warning'):
|
||||
@@ -79,7 +77,7 @@ class object_proxy(netsvc.Service):
|
||||
# We open a *new* cursor here, one reason is that failed SQL
|
||||
# queries (as in IntegrityError) will invalidate the current one.
|
||||
cr = False
|
||||
|
||||
|
||||
if hasattr(src, '__call__'):
|
||||
# callable. We need to find the right parameters to call
|
||||
# the orm._sql_message(self, cr, uid, ids, context) function,
|
||||
@@ -92,18 +90,18 @@ class object_proxy(netsvc.Service):
|
||||
ids = args[3]
|
||||
else:
|
||||
ids = []
|
||||
cr = sql_db.db_connect(db_name).cursor()
|
||||
cr = sql_db.db_connect(dbname).cursor()
|
||||
return src(obj, cr, uid, ids, context=(ctx or {}))
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
if cr: cr.close()
|
||||
|
||||
|
||||
return False # so that the original SQL error will
|
||||
# be returned, it is the best we have.
|
||||
|
||||
try:
|
||||
cr = sql_db.db_connect(db_name).cursor()
|
||||
cr = sql_db.db_connect(dbname).cursor()
|
||||
res = translate(cr, name=False, source_type=ttype,
|
||||
lang=lang, source=src)
|
||||
if res:
|
||||
@@ -203,59 +201,16 @@ class object_proxy(netsvc.Service):
|
||||
return res
|
||||
|
||||
|
||||
class osv_pool(object):
|
||||
""" 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.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.obj_pool = {} # model name/model instance mapping
|
||||
self._sql_error = {}
|
||||
self._store_function = {}
|
||||
self._init = True
|
||||
self._init_parent = {}
|
||||
|
||||
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."""
|
||||
return self.obj_pool.keys()
|
||||
|
||||
def add(self, model_name, model):
|
||||
""" Add or replace a model in the registry."""
|
||||
self.obj_pool[model_name] = model
|
||||
|
||||
def get(self, name):
|
||||
""" Return a model for a given name or None if it doesn't exist."""
|
||||
return self.obj_pool.get(name)
|
||||
|
||||
def instanciate(self, module, cr):
|
||||
""" Instanciate all the classes of a given module for a particular db."""
|
||||
|
||||
res = []
|
||||
|
||||
# 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, cr))
|
||||
|
||||
return res
|
||||
|
||||
|
||||
class osv_memory(orm.orm_memory):
|
||||
""" Deprecated class. """
|
||||
pass
|
||||
__metaclass__ = MetaModel
|
||||
_register = False # Set to false if the model shouldn't be automatically discovered.
|
||||
|
||||
|
||||
class osv(orm.orm):
|
||||
""" Deprecated class. """
|
||||
pass
|
||||
__metaclass__ = MetaModel
|
||||
_register = False # Set to false if the model shouldn't be automatically discovered.
|
||||
|
||||
|
||||
def start_object_proxy():
|
||||
|
||||
+5
-16
@@ -27,33 +27,22 @@
|
||||
|
||||
from openerp.modules.registry import RegistryManager
|
||||
|
||||
_Registries = None
|
||||
|
||||
|
||||
def ensure_registries():
|
||||
global _Registries
|
||||
if _Registries is None:
|
||||
_Registries = RegistryManager()
|
||||
|
||||
|
||||
def get_db_and_pool(db_name, force_demo=False, status=None, update_module=False, pooljobs=True):
|
||||
"""Create and return a database connection and a newly initialized registry."""
|
||||
ensure_registries()
|
||||
bound_registry = _Registries.get(db_name, force_demo, status, update_module, pooljobs)
|
||||
return bound_registry.db, bound_registry.registry
|
||||
registry = RegistryManager.get(db_name, force_demo, status, update_module, pooljobs)
|
||||
return registry.db, registry
|
||||
|
||||
|
||||
def delete_pool(db_name):
|
||||
"""Delete an existing registry."""
|
||||
ensure_registries()
|
||||
_Registries.delete(db_name)
|
||||
RegistryManager.delete(db_name)
|
||||
|
||||
|
||||
def restart_pool(db_name, force_demo=False, status=None, update_module=False):
|
||||
"""Delete an existing registry and return a database connection and a newly initialized registry."""
|
||||
ensure_registries()
|
||||
bound_registry = _Registries.new(db_name, force_demo, status, update_module, True)
|
||||
return bound_registry.db, bound_registry.registry
|
||||
registry = RegistryManager.new(db_name, force_demo, status, update_module, True)
|
||||
return registry.db, registry
|
||||
|
||||
|
||||
def get_db(db_name):
|
||||
|
||||
@@ -631,9 +631,11 @@ class _rml_flowable(object):
|
||||
paraStyle = None
|
||||
if tr.get('style'):
|
||||
st = copy.deepcopy(self.styles.table_styles[tr.get('style')])
|
||||
for s in st._cmds:
|
||||
s[1][1] = posy
|
||||
s[2][1] = posy
|
||||
for si in range(len(st._cmds)):
|
||||
s = list(st._cmds[si])
|
||||
s[1] = (s[1][0],posy)
|
||||
s[2] = (s[2][0],posy)
|
||||
st._cmds[si] = tuple(s)
|
||||
styles.append(st)
|
||||
if tr.get('paraStyle'):
|
||||
paraStyle = self.styles.styles[tr.get('paraStyle')]
|
||||
|
||||
@@ -45,16 +45,6 @@ class TinySocketClientThread(threading.Thread, netsvc.OpenERPDispatcher):
|
||||
self.sock.settimeout(1200)
|
||||
self.threads = threads
|
||||
|
||||
def __del__(self):
|
||||
if self.sock:
|
||||
try:
|
||||
self.socket.shutdown(
|
||||
getattr(socket, 'SHUT_RDWR', 2))
|
||||
except Exception:
|
||||
pass
|
||||
# That should garbage-collect and close it, too
|
||||
self.sock = None
|
||||
|
||||
def run(self):
|
||||
self.running = True
|
||||
try:
|
||||
@@ -87,11 +77,14 @@ class TinySocketClientThread(threading.Thread, netsvc.OpenERPDispatcher):
|
||||
tb_s = "".join(traceback.format_exception(*tb))
|
||||
logging.getLogger('web-services').debug("netrpc: communication-level exception", exc_info=True)
|
||||
ts.mysend(e, exception=True, traceback=tb_s)
|
||||
break
|
||||
except Exception, ex:
|
||||
#terminate this channel if we can't properly send back the error
|
||||
logging.getLogger('web-services').exception("netrpc: cannot deliver exception message to client")
|
||||
break
|
||||
|
||||
netsvc.close_socket(self.sock)
|
||||
self.sock = None
|
||||
self.threads.remove(self)
|
||||
self.running = False
|
||||
return True
|
||||
|
||||
@@ -90,7 +90,12 @@ class db(netsvc.ExportService):
|
||||
cr = None
|
||||
try:
|
||||
serv.actions[id]['progress'] = 0
|
||||
cr = sql_db.db_connect(db_name).cursor()
|
||||
openerp.modules.db.initialize(cr) # TODO this should be removed as it is done by pooler.restart_pool.
|
||||
tools.config['lang'] = lang
|
||||
cr.commit()
|
||||
cr.close()
|
||||
|
||||
pool = pooler.restart_pool(db_name, demo, serv.actions[id],
|
||||
update_module=True)[1]
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@ import cPickle
|
||||
import cStringIO
|
||||
import marshal
|
||||
|
||||
import netsvc
|
||||
|
||||
class Myexception(Exception):
|
||||
"""
|
||||
custome exception object store
|
||||
@@ -56,8 +58,7 @@ class mysocket:
|
||||
self.sock.connect((host, int(port)))
|
||||
|
||||
def disconnect(self):
|
||||
self.sock.shutdown(socket.SHUT_RDWR)
|
||||
self.sock.close()
|
||||
netsvc.close_socket(self.sock)
|
||||
|
||||
def mysend(self, msg, exception=False, traceback=None):
|
||||
msg = cPickle.dumps([msg,traceback])
|
||||
|
||||
+16
-2
@@ -72,6 +72,9 @@ class configmanager(object):
|
||||
['publisher_warranty_url', 'load_language', 'root_path',
|
||||
'init', 'save', 'config', 'update'])
|
||||
|
||||
# dictionary mapping option destination (keys in self.options) to MyOptions.
|
||||
self.casts = {}
|
||||
|
||||
self.misc = {}
|
||||
self.config_file = fname
|
||||
self.has_ssl = check_ssl()
|
||||
@@ -251,11 +254,14 @@ class configmanager(object):
|
||||
for group in parser.option_groups:
|
||||
for option in group.option_list:
|
||||
self.options[option.dest] = option.my_default
|
||||
self.casts[option.dest] = option
|
||||
|
||||
self.parse_config()
|
||||
|
||||
def parse_config(self, args=[]):
|
||||
opt, args = self.parser.parse_args()
|
||||
def parse_config(self, args=None):
|
||||
if args is None:
|
||||
args = []
|
||||
opt, args = self.parser.parse_args(args)
|
||||
|
||||
def die(cond, msg):
|
||||
if cond:
|
||||
@@ -314,8 +320,12 @@ class configmanager(object):
|
||||
]
|
||||
|
||||
for arg in keys:
|
||||
# Copy the command-line argument...
|
||||
if getattr(opt, arg):
|
||||
self.options[arg] = getattr(opt, arg)
|
||||
# ... or keep, but cast, the config file value.
|
||||
elif isinstance(self.options[arg], basestring) and self.casts[arg].type in optparse.Option.TYPE_CHECKER:
|
||||
self.options[arg] = optparse.Option.TYPE_CHECKER[self.casts[arg].type](self.casts[arg], arg, self.options[arg])
|
||||
|
||||
keys = [
|
||||
'language', 'translate_out', 'translate_in', 'overwrite_existing_translations',
|
||||
@@ -327,8 +337,12 @@ class configmanager(object):
|
||||
]
|
||||
|
||||
for arg in keys:
|
||||
# Copy the command-line argument...
|
||||
if getattr(opt, arg) is not None:
|
||||
self.options[arg] = getattr(opt, arg)
|
||||
# ... or keep, but cast, the config file value.
|
||||
elif isinstance(self.options[arg], basestring) and self.casts[arg].type in optparse.Option.TYPE_CHECKER:
|
||||
self.options[arg] = optparse.Option.TYPE_CHECKER[self.casts[arg].type](self.casts[arg], arg, self.options[arg])
|
||||
|
||||
if opt.assert_exit_level:
|
||||
self.options['assert_exit_level'] = self._LOGLEVELS[opt.assert_exit_level]
|
||||
|
||||
+34
-5
@@ -36,6 +36,7 @@ import threading
|
||||
import time
|
||||
import warnings
|
||||
import zipfile
|
||||
from collections import defaultdict
|
||||
from datetime import datetime
|
||||
from email.MIMEText import MIMEText
|
||||
from email.MIMEBase import MIMEBase
|
||||
@@ -1398,18 +1399,46 @@ class unquote(str):
|
||||
or escaping, keeping the original string untouched. The name come from Lisp's unquote.
|
||||
One of the uses for this is to preserve or insert bare variable names within dicts during eval()
|
||||
of a dict's repr(). Use with care.
|
||||
Some examples:
|
||||
|
||||
Some examples (notice that there are never quotes surrounding
|
||||
the ``active_id`` name:
|
||||
|
||||
>>> unquote('active_id')
|
||||
active_id
|
||||
>>> repr(unquote('active_id'))
|
||||
active_id
|
||||
>>> d = {'test': unquote('active_id')}
|
||||
>>> d
|
||||
{'test': active_id}
|
||||
>>> repr(d)
|
||||
"{'test': active_id}"
|
||||
>>> print d
|
||||
{'test': active_id}
|
||||
"""
|
||||
def __repr__(self):
|
||||
return self
|
||||
|
||||
class UnquoteEvalContext(defaultdict):
|
||||
"""Defaultdict-based evaluation context that returns
|
||||
an ``unquote`` string for any missing name used during
|
||||
the evaluation.
|
||||
Mostly useful for evaluating OpenERP domains/contexts that
|
||||
may refer to names that are unknown at the time of eval,
|
||||
so that when the context/domain is converted back to a string,
|
||||
the original names are preserved.
|
||||
|
||||
**Warning**: using an ``UnquoteEvalContext`` as context for ``eval()`` or
|
||||
``safe_eval()`` will shadow the builtins, which may cause other
|
||||
failures, depending on what is evaluated.
|
||||
|
||||
Example (notice that ``section_id`` is preserved in the final
|
||||
result) :
|
||||
|
||||
>>> context_str = "{'default_user_id': uid, 'default_section_id': section_id}"
|
||||
>>> eval(context_str, UnquoteEvalContext(uid=1))
|
||||
{'default_user_id': 1, 'default_section_id': section_id}
|
||||
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(UnquoteEvalContext, self).__init__(None, *args, **kwargs)
|
||||
|
||||
def __missing__(self, key):
|
||||
return unquote(key)
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -274,6 +274,7 @@ def safe_eval(expr, globals_dict=None, locals_dict=None, mode="eval", nocopy=Fal
|
||||
'tuple': tuple,
|
||||
'map': map,
|
||||
'abs': abs,
|
||||
'min': min,
|
||||
'reduce': reduce,
|
||||
'filter': filter,
|
||||
'round': round,
|
||||
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
import threading
|
||||
import traceback
|
||||
import time
|
||||
|
||||
import openerp
|
||||
|
||||
openerp.tools.config.parse_config(sys.argv[1:])
|
||||
config = openerp.tools.config
|
||||
|
||||
for name in [
|
||||
'db_name',
|
||||
'addons_path',
|
||||
'demo',
|
||||
'osv_memory_count_limit',
|
||||
'osv_memory_age_limit',
|
||||
]:
|
||||
print "%s: %s - %s" % (name, config[name], type(config[name]))
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
[options]
|
||||
osv_memory_count_limit = 5
|
||||
osv_memory_age_limit = 3.4
|
||||
@@ -0,0 +1,46 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
""" Tests for the configuration file/command-line arguments. """
|
||||
|
||||
# This test should be run from its directory.
|
||||
|
||||
# TODO A configmanager object cannot parse multiple times a config file
|
||||
# and/or the command line, preventing to 'reload' a configuration.
|
||||
|
||||
import os
|
||||
|
||||
import config
|
||||
|
||||
config_file_00 = os.path.join(os.path.dirname(__file__),'test-config-values-00.conf')
|
||||
|
||||
# 1. No config file, no command-line arguments (a.k.a. default values)
|
||||
|
||||
conf = config.configmanager()
|
||||
conf.parse_config()
|
||||
|
||||
assert conf['osv_memory_age_limit'] == 1.0
|
||||
assert os.path.join(conf['root_path'], 'addons') == conf['addons_path']
|
||||
|
||||
# 2. No config file, some command-line arguments
|
||||
|
||||
conf = config.configmanager()
|
||||
# mess with the optparse.Option definition to allow an invalid path
|
||||
conf.casts['addons_path'].action = 'store'
|
||||
conf.parse_config(['--addons-path=/xyz/dont-exist', '--osv-memory-age-limit=2.3'])
|
||||
|
||||
assert conf['osv_memory_age_limit'] == 2.3
|
||||
assert conf['addons_path'] == '/xyz/dont-exist'
|
||||
|
||||
# 3. Config file, no command-line arguments
|
||||
|
||||
conf = config.configmanager()
|
||||
conf.parse_config(['-c', config_file_00])
|
||||
|
||||
assert conf['osv_memory_age_limit'] == 3.4
|
||||
|
||||
# 4. Config file, and command-line arguments
|
||||
|
||||
conf = config.configmanager()
|
||||
conf.parse_config(['-c', config_file_00, '--osv-memory-age-limit=2.3'])
|
||||
|
||||
assert conf['osv_memory_age_limit'] == 2.3
|
||||
@@ -536,7 +536,7 @@ def trans_generate(lang, modules, cr):
|
||||
trans_obj = pool.get('ir.translation')
|
||||
model_data_obj = pool.get('ir.model.data')
|
||||
uid = 1
|
||||
l = pool.obj_pool.items()
|
||||
l = pool.models.items()
|
||||
l.sort()
|
||||
|
||||
query = 'SELECT name, model, res_id, module' \
|
||||
@@ -864,7 +864,6 @@ def trans_load_data(cr, fileobj, fileformat, lang, lang_name=None, verbose=True,
|
||||
pool = pooler.get_pool(db_name)
|
||||
lang_obj = pool.get('res.lang')
|
||||
trans_obj = pool.get('ir.translation')
|
||||
model_data_obj = pool.get('ir.model.data')
|
||||
iso_lang = misc.get_iso_codes(lang)
|
||||
try:
|
||||
uid = 1
|
||||
|
||||
@@ -105,7 +105,7 @@ class TestReport(object):
|
||||
success += self._report[severity][True]
|
||||
failure += self._report[severity][False]
|
||||
res.append("total\t%s\t%s" % (success, failure))
|
||||
res.append("end of report (%s assertion(s) checked)" % success + failure)
|
||||
res.append("end of report (%s assertion(s) checked)" % (success + failure))
|
||||
return "\n".join(res)
|
||||
|
||||
class RecordDictWrapper(dict):
|
||||
|
||||
@@ -89,7 +89,7 @@ if os.name == 'nt':
|
||||
"pydot", "asyncore","asynchat", "reportlab", "vobject",
|
||||
"HTMLParser", "select", "mako", "poplib",
|
||||
"imaplib", "smtplib", "email", "yaml", "DAV",
|
||||
"uuid", "commands", "openerp",
|
||||
"uuid", "commands", "openerp", # TODO is it necessary to include cgitb?
|
||||
],
|
||||
"excludes" : ["Tkconstants","Tkinter","tcl"],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user