diff --git a/CHANGELOG b/CHANGELOG
index 7dfd90bec0..28aae47f2c 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,6 @@
+* Drop support of Python 2.5
+* Remove support of Many2Many field in record XML
+* Change Pool into a Singleton
* Remove support of zipped modules
Version 2.0.0 - 2011-04-26
diff --git a/bin/trytond b/bin/trytond
index ffd5d54750..a55071c781 100755
--- a/bin/trytond
+++ b/bin/trytond
@@ -37,6 +37,8 @@ def parse_commandline():
help="file where the server pid will be stored")
parser.add_option("--logfile", dest="logfile",
help="file where the server log will be stored")
+ parser.add_option("--disable-cron", dest="cron",
+ action="store_false", help="disable cron")
(opt, _) = parser.parse_args()
@@ -51,6 +53,7 @@ def parse_commandline():
'debug_mode',
'pidfile',
'logfile',
+ 'cron',
):
if getattr(opt, arg) is not None:
options[arg] = getattr(opt, arg)
diff --git a/doc/ref/models/fields.rst b/doc/ref/models/fields.rst
index 4d741c22ba..52e8d036ee 100644
--- a/doc/ref/models/fields.rst
+++ b/doc/ref/models/fields.rst
@@ -508,7 +508,7 @@ A many-to-many relation field.
Instance methods:
-.. method:: Many2Many.get_target(pool)
+.. method:: Many2Many.get_target()
Return the target :class:`~trytond.model.Model`.
@@ -530,7 +530,7 @@ A one-to-one relation field.
Instance methods:
-.. method:: One2One.get_target(pool)
+.. method:: One2One.get_target()
Return the target :class:`~trytond.model.Model`.
diff --git a/doc/topics/install.rst b/doc/topics/install.rst
index e44100ed6a..ed496ac66a 100644
--- a/doc/topics/install.rst
+++ b/doc/topics/install.rst
@@ -7,7 +7,7 @@ How to install Tryton
Prerequisites
=============
- * Python 2.5 or later (http://www.python.org/)
+ * Python 2.6 or later (http://www.python.org/)
* lxml 2.0 or later (http://codespeak.net/lxml/)
* relatorio 0.2.0 or later (http://relatorio.openhex.org/)
* python-dateutil (http://labix.org/python-dateutil)
diff --git a/doc/topics/reports/index.rst b/doc/topics/reports/index.rst
index f45b8a62f5..96a70b11ad 100644
--- a/doc/topics/reports/index.rst
+++ b/doc/topics/reports/index.rst
@@ -61,7 +61,7 @@ When defining an `ir.action.report` the following attributes are available:
* ``name``: The name of the report.
* ``report_name``: The name of the report model, for example
- my_module.my_report. This is the name you would use with `pool.get`
+ my_module.my_report. This is the name you would use with `Pool().get`
* ``module_name``: If this report is of an existing model this is its name.
For example my_module.my_model. Custom reports that aren't of a specific
@@ -228,7 +228,7 @@ context. Now the invoice report will be able to access the employee object.
class InvoiceReport(Report):
_name = 'account.invoice'
def parse(self, report, objects, datas, localcontext):
- employee_obj = self.pool.get('company.employee')
+ employee_obj = Pool().get('company.employee')
employee = False
if Transaction().context.get('employee'):
employee = employee_obj.browse(Transaction().context['employee'])
diff --git a/etc/trytond.conf b/etc/trytond.conf
index 90e74c0c97..894d201565 100644
--- a/etc/trytond.conf
+++ b/etc/trytond.conf
@@ -90,3 +90,6 @@
# Prevent database listing
#prevent_dblist = False
+
+# Enable cron
+# cron = True
diff --git a/setup.py b/setup.py
index 5bb69db1e5..e21a377c6b 100644
--- a/setup.py
+++ b/setup.py
@@ -8,10 +8,6 @@ import sys
execfile(os.path.join('trytond', 'version.py'))
-SIMPLEJSON = []
-if sys.version_info < (2, 6):
- SIMPLEJSON = ['simplejson']
-
def read(fname):
return open(os.path.join(os.path.dirname(__file__), fname)).read()
@@ -52,7 +48,6 @@ setup(name=PACKAGE,
'Natural Language :: Russian',
'Natural Language :: Spanish',
'Operating System :: OS Independent',
- 'Programming Language :: Python :: 2.5',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Topic :: Software Development :: Libraries :: Application Frameworks',
@@ -63,7 +58,7 @@ setup(name=PACKAGE,
'relatorio >= 0.2.0',
'Genshi',
'python-dateutil',
- ] + SIMPLEJSON,
+ ],
extras_require={
'PostgreSQL': ['psycopg2 >= 2.0'],
'MySQL': ['MySQL-python'],
diff --git a/trytond/backend/database.py b/trytond/backend/database.py
index 168d1f9624..b61c88baa2 100644
--- a/trytond/backend/database.py
+++ b/trytond/backend/database.py
@@ -1,5 +1,6 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
+from trytond.const import CONTEXT_CACHE_SIZE, MODEL_CACHE_SIZE
DatabaseIntegrityError = None
DatabaseOperationalError = None
@@ -103,7 +104,8 @@ class CursorInterface(object):
IN_MAX = 1000
def __init__(self):
- self.cache = {}
+ from trytond.cache import LRUDict
+ self.cache = LRUDict(CONTEXT_CACHE_SIZE)
def get_cache(self, context=None):
'''
@@ -112,6 +114,7 @@ class CursorInterface(object):
:param context: the context
:return: the cache dictionary
'''
+ from trytond.cache import LRUDict
if context is None:
context = {}
cache_ctx = context.copy()
@@ -119,7 +122,8 @@ class CursorInterface(object):
'_delete_records'):
if i in cache_ctx:
del cache_ctx[i]
- return self.cache.setdefault(repr(cache_ctx), {})
+ return self.cache.setdefault(repr(cache_ctx),
+ LRUDict(MODEL_CACHE_SIZE))
def execute(self, sql, params=None):
'''
diff --git a/trytond/backend/mysql/database.py b/trytond/backend/mysql/database.py
index de7fc52785..c01a3030cc 100644
--- a/trytond/backend/mysql/database.py
+++ b/trytond/backend/mysql/database.py
@@ -1,6 +1,5 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
-from __future__ import with_statement
from trytond.backend.database import DatabaseInterface, CursorInterface
from trytond.config import CONFIG
import MySQLdb
diff --git a/trytond/backend/mysql/init.sql b/trytond/backend/mysql/init.sql
index bb500939fd..a74b656807 100644
--- a/trytond/backend/mysql/init.sql
+++ b/trytond/backend/mysql/init.sql
@@ -84,10 +84,10 @@ CREATE TABLE res_group (
) ENGINE=InnoDB;
CREATE TABLE `res_user-res_group` (
- uid BIGINT NOT NULL,
- gid BIGINT NOT NULL,
- CONSTRAINT `res_user-res_group_uid_fkey` FOREIGN KEY (uid) REFERENCES res_user (id) ON DELETE CASCADE,
- CONSTRAINT `res_user-res_group_gid_fkey` FOREIGN KEY (gid) REFERENCES res_group (id) ON DELETE CASCADE
+ `user` BIGINT NOT NULL,
+ `group` BIGINT NOT NULL,
+ CONSTRAINT `res_user-res_group_user_fkey` FOREIGN KEY (`user`) REFERENCES res_user (id) ON DELETE CASCADE,
+ CONSTRAINT `res_user-res_group_group_fkey` FOREIGN KEY (`group`) REFERENCES res_group (id) ON DELETE CASCADE
) ENGINE=InnoDB;
CREATE TABLE wkf (
diff --git a/trytond/backend/postgresql/database.py b/trytond/backend/postgresql/database.py
index c432fa9673..be7f01d7ec 100644
--- a/trytond/backend/postgresql/database.py
+++ b/trytond/backend/postgresql/database.py
@@ -1,6 +1,5 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
-from __future__ import with_statement
from trytond.backend.database import DatabaseInterface, CursorInterface
from trytond.config import CONFIG
from trytond.session import Session
diff --git a/trytond/backend/postgresql/init.sql b/trytond/backend/postgresql/init.sql
index be4da11b13..3e3c4229c5 100644
--- a/trytond/backend/postgresql/init.sql
+++ b/trytond/backend/postgresql/init.sql
@@ -102,10 +102,10 @@ CREATE SEQUENCE "res_user-res_group_id_seq";
CREATE TABLE "res_user-res_group" (
id INTEGER DEFAULT NEXTVAL('res_user-res_group_id_seq') NOT NULL,
- uid INTEGER NOT NULL,
- gid INTEGER NOT NULL,
- FOREIGN KEY (uid) REFERENCES res_user (id) ON DELETE CASCADE,
- FOREIGN KEY (gid) REFERENCES res_group (id) ON DELETE CASCADE,
+ "user" INTEGER NOT NULL,
+ "group" INTEGER NOT NULL,
+ FOREIGN KEY ("user") REFERENCES res_user (id) ON DELETE CASCADE,
+ FOREIGN KEY ("group") REFERENCES res_group (id) ON DELETE CASCADE,
PRIMARY KEY(id)
);
diff --git a/trytond/backend/sqlite/database.py b/trytond/backend/sqlite/database.py
index 0c4fa0ae60..e4d04a2b65 100644
--- a/trytond/backend/sqlite/database.py
+++ b/trytond/backend/sqlite/database.py
@@ -1,6 +1,5 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
-from __future__ import with_statement
from trytond.backend.database import DatabaseInterface, CursorInterface
from trytond.config import CONFIG
from trytond.session import Session
@@ -9,6 +8,7 @@ import re
from decimal import Decimal
import datetime
import time
+import sys
_FIX_ROWCOUNT = False
try:
@@ -144,10 +144,9 @@ class Database(DatabaseInterface):
return
path = os.path.join(CONFIG['data_path'],
database_name + '.sqlite')
- conn = sqlite.connect(path)
- cursor = conn.cursor()
- cursor.close()
- conn.close()
+ with sqlite.connect(path) as conn:
+ cursor = conn.cursor()
+ cursor.close()
def drop(self, cursor, database_name):
if database_name == ':memory:':
@@ -358,7 +357,10 @@ class Cursor(CursorInterface):
return select
sqlite.register_converter('NUMERIC', lambda val: Decimal(val))
-sqlite.register_adapter(Decimal, lambda val: buffer(str(val)))
+if sys.version_info[0] == 2:
+ sqlite.register_adapter(Decimal, lambda val: buffer(str(val)))
+else:
+ sqlite.register_adapter(Decimal, lambda val: bytes(str(val)))
sqlite.register_adapter(Session, int)
def adapt_datetime(val):
return val.replace(tzinfo=None).isoformat(" ")
diff --git a/trytond/backend/sqlite/init.sql b/trytond/backend/sqlite/init.sql
index 52bbf66e15..9d1879c227 100644
--- a/trytond/backend/sqlite/init.sql
+++ b/trytond/backend/sqlite/init.sql
@@ -73,8 +73,8 @@ CREATE TABLE res_group (
CREATE TABLE "res_user-res_group" (
id INTEGER PRIMARY KEY AUTOINCREMENT,
- uid INTEGER,
- gid INTEGER
+ "user" INTEGER,
+ "group" INTEGER
);
CREATE TABLE wkf (
diff --git a/trytond/backend/sqlite/table.py b/trytond/backend/sqlite/table.py
index 6d942b7962..2a18b038b6 100644
--- a/trytond/backend/sqlite/table.py
+++ b/trytond/backend/sqlite/table.py
@@ -127,7 +127,7 @@ class TableHandler(TableHandlerInterface):
match = re.match(r'(\w+)(\((.*?)\))?', type_)
if match:
typname = match.group(1).upper()
- size = match.group(3) and int(match.group(3)) or None
+ size = match.group(3) and int(match.group(3)) or 0
else:
typname = type_.upper()
size = -1
diff --git a/trytond/cache.py b/trytond/cache.py
index dfda7e465c..005030e3af 100644
--- a/trytond/cache.py
+++ b/trytond/cache.py
@@ -7,6 +7,7 @@ from threading import Lock
from trytond.transaction import Transaction
from trytond.config import CONFIG
from trytond.backend import Database
+from trytond.tools import OrderedDict
class Cache(object):
@@ -167,3 +168,32 @@ class Cache(object):
cursor.commit()
cursor.close()
Cache._resets_lock.release()
+
+
+class LRUDict(OrderedDict):
+ """
+ Dictionary with a size limit.
+ If size limit is reached, it will remove the first added items.
+ """
+ def __init__(self, size_limit, *args, **kwargs):
+ assert size_limit > 0
+ self.size_limit = size_limit
+ super(LRUDict, self).__init__(*args, **kwargs)
+ self._check_size_limit()
+
+ def __setitem__(self, key, value):
+ super(LRUDict, self).__setitem__(key, value)
+ self._check_size_limit()
+
+ def update(self, *args, **kwargs):
+ super(LRUDict, self).update(*args, **kwargs)
+ self._check_size_limit()
+
+ def setdefault(self, key, default=None):
+ default = super(LRUDict, self).setdefault(key, default=default)
+ self._check_size_limit()
+ return default
+
+ def _check_size_limit(self):
+ while len(self) > self.size_limit:
+ self.popitem(last=False)
diff --git a/trytond/config.py b/trytond/config.py
index 6d59bccdcb..39e772c0ad 100644
--- a/trytond/config.py
+++ b/trytond/config.py
@@ -51,6 +51,7 @@ class ConfigManager(object):
'prevent_dblist': False,
'init': {},
'update': {},
+ 'cron': True,
}
self.configfile = None
@@ -82,12 +83,9 @@ class ConfigManager(object):
if not self.configfile:
return
- fp = open(self.configfile)
parser = ConfigParser.ConfigParser()
- try:
+ with open(self.configfile) as fp:
parser.readfp(fp)
- finally:
- fp.close()
for (name, value) in parser.items('options'):
if value == 'True' or value == 'true':
value = True
diff --git a/trytond/const.py b/trytond/const.py
index 2a569f5f6b..b0a2006442 100644
--- a/trytond/const.py
+++ b/trytond/const.py
@@ -18,4 +18,7 @@ OPERATORS = (
'>',
)
+CONTEXT_CACHE_SIZE = 10
+MODEL_CACHE_SIZE = 200
+RECORD_CACHE_SIZE = 2000
BROWSE_FIELD_TRESHOLD = 100
diff --git a/trytond/convert.py b/trytond/convert.py
index 03ba03d2a6..3b2eea2fd6 100644
--- a/trytond/convert.py
+++ b/trytond/convert.py
@@ -1,6 +1,5 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
-from __future__ import with_statement
import time
from xml import sax
from decimal import Decimal
@@ -110,16 +109,7 @@ class MenuitemTagHandler:
values['icon'] = 'tryton-new'
if values.get('groups'):
- g_names = values['groups'].split(',')
- groups_value = []
- for group in g_names:
- if group.startswith('-'):
- group_id = self.mh.get_id(group[1:])
- groups_value.append(('remove', group_id))
- else:
- group_id = self.mh.get_id(group)
- groups_value.append(('add', group_id))
- values['groups'] = groups_value
+ raise Exception("Please use separate records for groups")
if not values.get('name'):
if not action_name:
@@ -205,17 +195,8 @@ class RecordTagHandler:
search_model = self.model._columns[field_name].model_name
f_obj = self.mh.pool.get(search_model)
with Transaction().set_context(active_test=False):
- answer = f_obj.browse(f_obj.search(safe_eval(search_attr)))
-
- if not answer: return
-
- if self.model._columns[field_name]._type == 'many2many':
- self.values[field_name] = [
- ('set', [x['id'] for x in answer]),
- ]
-
- elif self.model._columns[field_name]._type == 'many2one':
- self.values[field_name] = answer[0]['id']
+ self.values[field_name], = \
+ f_obj.search(safe_eval(search_attr))
elif ref_attr:
self.values[field_name] = self.mh.get_id(ref_attr)
@@ -527,7 +508,7 @@ class TrytondXmlHandler(sax.handler.ContentHandler):
ref_id = ref_id[0]
return ref_mode + ',' + str(ref_id)
elif field_type in ['one2many', 'many2many']:
- raise Unhandled_field()
+ raise Unhandled_field("Unhandled field %s" % key)
else:
return browse_record[key]
@@ -695,13 +676,7 @@ class TrytondXmlHandler(sax.handler.ContentHandler):
to_update = {}
for key in values:
- try:
- db_field = self._clean_value(key, db_val, object_ref)
- except Unhandled_field:
- logging.getLogger("convert").info(
- 'Field %s on %s : integrity not tested.'%(key, model))
- to_update[key] = values[key]
- continue
+ db_field = self._clean_value(key, db_val, object_ref)
# if the fs value is the same has in the db, whe ignore it
val = values[key]
@@ -813,11 +788,7 @@ class TrytondXmlHandler(sax.handler.ContentHandler):
if not db_val:
db_val = object_ref.browse(db_id)
for key in to_update:
- try:
- values[key] = self._clean_value(
- key, db_val, object_ref)
- except Unhandled_field:
- continue
+ values[key] = self._clean_value( key, db_val, object_ref)
if module != self.module:
temp_values = old_values.copy()
@@ -904,11 +875,7 @@ class TrytondXmlHandler(sax.handler.ContentHandler):
# in the model_data table:
db_val = object_ref.browse(db_id)
for key in values:
- try:
- values[key] = self._clean_value(key, db_val,
- object_ref)
- except Unhandled_field:
- continue
+ values[key] = self._clean_value(key, db_val, object_ref)
for table in inherit_db_ids.keys():
self.modeldata_obj.create({
diff --git a/trytond/error.py b/trytond/error.py
index 1ca8e42b9c..46381d2dd8 100644
--- a/trytond/error.py
+++ b/trytond/error.py
@@ -1,6 +1,7 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
from trytond.transaction import Transaction
+from trytond.pool import Pool
class WarningErrorMixin(object):
@@ -24,7 +25,7 @@ class WarningErrorMixin(object):
(or tuple if error_description is not empty) instead of raising an
exception.
'''
- translation_obj = self.pool.get('ir.translation')
+ translation_obj = Pool().get('ir.translation')
error = self._error_messages.get(error, error)
@@ -92,7 +93,7 @@ class WarningErrorMixin(object):
:param warning_description_args: the arguments that will be used
for "%"-based substitution
'''
- warning_obj = self.pool.get('res.user.warning')
+ warning_obj = Pool().get('res.user.warning')
if warning_obj.check(warning_name):
if warning_description:
warning, warning_description = self.raise_user_error(warning,
diff --git a/trytond/ir/action.py b/trytond/ir/action.py
index 6996c13b04..cbd5cbd750 100644
--- a/trytond/ir/action.py
+++ b/trytond/ir/action.py
@@ -1,6 +1,5 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
-from __future__ import with_statement
import base64
import os
from trytond.model import ModelView, ModelSQL, ModelStorage, fields
@@ -9,6 +8,7 @@ from trytond.backend import TableHandler
from trytond.pyson import PYSONEncoder, CONTEXT, PYSON
from trytond.transaction import Transaction
from trytond.cache import Cache
+from trytond.pool import Pool
class Action(ModelSQL, ModelView):
@@ -20,7 +20,7 @@ class Action(ModelSQL, ModelView):
usage = fields.Char('Usage')
keywords = fields.One2Many('ir.action.keyword', 'action',
'Keywords')
- groups = fields.Many2Many('ir.action-res.group', 'action_id', 'gid',
+ groups = fields.Many2Many('ir.action-res.group', 'action', 'group',
'Groups')
icon = fields.Many2One('ir.ui.icon', 'Icon')
active = fields.Boolean('Active', select=2)
@@ -38,6 +38,7 @@ class Action(ModelSQL, ModelView):
return True
def get_action_id(self, action_id):
+ pool = Pool()
with Transaction().set_context(active_test=False):
if self.search([
('id', '=', action_id),
@@ -49,7 +50,7 @@ class Action(ModelSQL, ModelView):
'ir.action.wizard',
'ir.action.url',
):
- action_obj = self.pool.get(action_type)
+ action_obj = pool.get(action_type)
action_id2 = action_obj.search([
('id', '=', action_id),
])
@@ -88,7 +89,8 @@ class ActionKeyword(ModelSQL, ModelView):
})
def check_wizard_model(self, ids):
- action_wizard_obj = self.pool.get('ir.action.wizard')
+ pool = Pool()
+ action_wizard_obj = pool.get('ir.action.wizard')
for action_keyword in self.browse(ids):
if action_keyword.action.type == 'ir.action.wizard':
action_wizard_id = action_wizard_obj.search([
@@ -103,13 +105,15 @@ class ActionKeyword(ModelSQL, ModelView):
def _convert_vals(self, vals):
vals = vals.copy()
- action_obj = self.pool.get('ir.action')
+ pool = Pool()
+ action_obj = pool.get('ir.action')
if 'action' in vals:
vals['action'] = action_obj.get_action_id(vals['action'])
return vals
def models_get(self):
- model_obj = self.pool.get('ir.model')
+ pool = Pool()
+ model_obj = pool.get('ir.model')
model_ids = model_obj.search([])
res = []
for model in model_obj.browse(model_ids):
@@ -117,36 +121,39 @@ class ActionKeyword(ModelSQL, ModelView):
return res
def delete(self, ids):
+ pool = Pool()
if isinstance(ids, (int, long)):
ids = [ids]
for keyword in self.browse(ids):
# Restart the cache view
try:
- self.pool.get(keyword.model.split(',')[0]
+ pool.get(keyword.model.split(',')[0]
).fields_view_get.reset()
except Exception:
pass
return super(ActionKeyword, self).delete(ids)
def create(self, vals):
+ pool = Pool()
vals = self._convert_vals(vals)
if 'model' in vals:
# Restart the cache view
try:
- self.pool.get(vals['model'].split(',')[0]
+ pool.get(vals['model'].split(',')[0]
).fields_view_get.reset()
except Exception:
pass
return super(ActionKeyword, self).create(vals)
def write(self, ids, vals):
+ pool = Pool()
vals = self._convert_vals(vals)
if isinstance(ids, (int, long)):
ids = [ids]
for keyword in self.browse(ids):
# Restart the cache view
try:
- self.pool.get(keyword.model.split(',')[0]
+ pool.get(keyword.model.split(',')[0]
).fields_view_get.reset()
except Exception:
pass
@@ -154,13 +161,14 @@ class ActionKeyword(ModelSQL, ModelView):
for keyword in self.browse(ids):
# Restart the cache view
try:
- self.pool.get(keyword.model.split(',')[0]
+ pool.get(keyword.model.split(',')[0]
).fields_view_get.reset()
except Exception:
pass
return res
def get_keyword(self, keyword, value):
+ pool = Pool()
res = []
model, model_id = value
@@ -176,7 +184,7 @@ class ActionKeyword(ModelSQL, ModelView):
]))
for action_keyword in self.browse(action_keyword_ids):
try:
- action_obj = self.pool.get(action_keyword.action.type)
+ action_obj = pool.get(action_keyword.action.type)
except Exception:
continue
action_id = action_obj.search([
@@ -369,7 +377,8 @@ class ActionReport(ModelSQL, ModelView):
return super(ActionReport, self).write(ids, vals)
def delete(self, ids):
- action_obj = self.pool.get('ir.action')
+ pool = Pool()
+ action_obj = pool.get('ir.action')
if isinstance(ids, (int, long)):
ids = [ids]
@@ -563,7 +572,8 @@ class ActionActWindow(ModelSQL, ModelView):
return new_id
def delete(self, ids):
- action_obj = self.pool.get('ir.action')
+ pool = Pool()
+ action_obj = pool.get('ir.action')
if isinstance(ids, (int, long)):
ids = [ids]
@@ -639,7 +649,8 @@ class ActionWizard(ModelSQL, ModelView):
return new_id
def delete(self, ids):
- action_obj = self.pool.get('ir.action')
+ pool = Pool()
+ action_obj = pool.get('ir.action')
if isinstance(ids, (int, long)):
ids = [ids]
@@ -773,7 +784,8 @@ class ActionURL(ModelSQL, ModelView):
return new_id
def delete(self, ids):
- action_obj = self.pool.get('ir.action')
+ pool = Pool()
+ action_obj = pool.get('ir.action')
if isinstance(ids, (int, long)):
ids = [ids]
diff --git a/trytond/ir/action.xml b/trytond/ir/action.xml
index 088ca1c6e4..0fd88dbc8c 100644
--- a/trytond/ir/action.xml
+++ b/trytond/ir/action.xml
@@ -33,7 +33,6 @@ this repository contains the full copyright notices and license terms. -->
-
@@ -119,7 +118,6 @@ this repository contains the full copyright notices and license terms. -->
-
@@ -218,7 +216,6 @@ this repository contains the full copyright notices and license terms. -->
-
@@ -294,7 +291,6 @@ this repository contains the full copyright notices and license terms. -->
-
@@ -423,7 +419,6 @@ this repository contains the full copyright notices and license terms. -->
-
diff --git a/trytond/ir/attachment.py b/trytond/ir/attachment.py
index 46d5df1aa1..4136554184 100644
--- a/trytond/ir/attachment.py
+++ b/trytond/ir/attachment.py
@@ -1,6 +1,5 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
-from __future__ import with_statement
import os
try:
import hashlib
@@ -13,6 +12,7 @@ from trytond.config import CONFIG
from trytond.backend import TableHandler
from trytond.transaction import Transaction
from trytond.pyson import Not, Equal, Eval
+from trytond.pool import Pool
def firstline(description):
try:
@@ -84,7 +84,8 @@ class Attachment(ModelSQL, ModelView):
return 0
def models_get(self):
- model_obj = self.pool.get('ir.model')
+ pool = Pool()
+ model_obj = pool.get('ir.model')
model_ids = model_obj.search([])
res = []
for model in model_obj.browse(model_ids):
@@ -189,7 +190,8 @@ class Attachment(ModelSQL, ModelView):
def check_access(self, ids, mode='read'):
- model_access_obj = self.pool.get('ir.model.access')
+ pool = Pool()
+ model_access_obj = pool.get('ir.model.access')
if Transaction().user == 0:
return
if not ids:
@@ -224,7 +226,8 @@ class Attachment(ModelSQL, ModelView):
return res
def view_header_get(self, value, view_type='form'):
- ir_model_obj = self.pool.get('ir.model')
+ pool = Pool()
+ ir_model_obj = pool.get('ir.model')
value = super(Attachment, self).view_header_get(value,
view_type=view_type)
resource = Transaction().context.get('resource')
@@ -232,7 +235,7 @@ class Attachment(ModelSQL, ModelView):
model_name, record_id = resource.split(',', 1)
ir_model_id, = ir_model_obj.search([('model', '=', model_name)])
ir_model = ir_model_obj.browse(ir_model_id)
- model_obj = self.pool.get(model_name)
+ model_obj = pool.get(model_name)
record = model_obj.browse(int(record_id))
value = '%s - %s - %s' % (ir_model.name, record.rec_name, value)
return value
diff --git a/trytond/ir/cron.py b/trytond/ir/cron.py
index dfa81d2c45..99ed8d329b 100644
--- a/trytond/ir/cron.py
+++ b/trytond/ir/cron.py
@@ -1,18 +1,19 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
-from __future__ import with_statement
import contextlib
import datetime
from dateutil.relativedelta import relativedelta
import traceback
import sys
+import logging
from trytond.backend import Database
from trytond.model import ModelView, ModelSQL, fields
from trytond.tools import safe_eval
from trytond.transaction import Transaction
+from trytond.pool import Pool
+from trytond.backend import TableHandler
_INTERVALTYPES = {
- 'work_days': lambda interval: relativedelta(days=interval),
'days': lambda interval: relativedelta(days=interval),
'hours': lambda interval: relativedelta(hours=interval),
'weeks': lambda interval: relativedelta(weeks=interval),
@@ -26,7 +27,8 @@ class Cron(ModelSQL, ModelView):
_description = __doc__
name = fields.Char('Name', required=True, translate=True)
user = fields.Many2One('res.user', 'Execution User', required=True,
- help="The user used to execute this action")
+ domain=[('active', '=', False)],
+ help="The user used to execute this action")
request_user = fields.Many2One(
'res.user', 'Request User', required=True,
help="The user who will receive requests in case of failure")
@@ -35,24 +37,20 @@ class Cron(ModelSQL, ModelView):
interval_type = fields.Selection( [
('minutes', 'Minutes'),
('hours', 'Hours'),
- ('work_days', 'Work Days'),
('days', 'Days'),
('weeks', 'Weeks'),
('months', 'Months'),
], 'Interval Unit')
- numbercall = fields.Integer('Number of calls', select=1,
- help='Number of times the function is called,\n' \
- 'a negative number indicates that the function ' \
- 'will always be called')
- doall = fields.Boolean('Repeat missed')
- nextcall = fields.DateTime('Next call date', required=True,
+ number_calls = fields.Integer('Number of Calls', select=1,
+ help=('Number of times the function is called, a negative '
+ 'number indicates that the function will always be '
+ 'called'))
+ repeat_missed = fields.Boolean('Repeat Missed')
+ next_call = fields.DateTime('Next Call', required=True,
select=1)
model = fields.Char('Model')
function = fields.Char('Function')
args = fields.Text('Arguments')
- priority = fields.Integer('Priority',
- help='0=Very Urgent\n10=Not urgent')
- running = fields.Boolean('Running', readonly=True, select=1)
def __init__(self):
super(Cron, self).__init__()
@@ -62,135 +60,132 @@ class Cron(ModelSQL, ModelView):
"properly: \"%s\"\n Traceback: \n\n%s\n"
})
- def default_nextcall(self):
+ def init(self, module_name):
+ cursor = Transaction().cursor
+
+ # Migration from 2.0: rename numbercall, doall and nextcall
+ table = TableHandler(cursor, self, module_name)
+ table.column_rename('numbercall', 'number_calls')
+ table.column_rename('doall', 'repeat_missed')
+ table.column_rename('nextcall', 'next_call')
+
+ super(Cron, self).init(module_name)
+
+ # Migration from 2.0: work_days removed
+ cursor.execute('UPDATE "%s" '
+ 'SET interval_type = %%s '
+ 'WHERE interval_type = %%s' % self._table,
+ ('days', 'work_days'))
+
+ def default_next_call(self):
return datetime.datetime.now()
- def default_priority(self):
- return 5
-
- def default_user(self):
- return int(Transaction().user)
-
def default_interval_number(self):
return 1
def default_interval_type(self):
return 'months'
- def default_numbercall(self):
- return 1
+ def default_number_calls(self):
+ return -1
def default_active(self):
return True
- def default_doall(self):
+ def default_repeat_missed(self):
return True
- def default_running(self):
- return False
-
def check_xml_record(self, ids, values):
return True
+ def get_delta(self, cron):
+ '''
+ Return the relativedelta for the next call
+ '''
+ return _INTERVALTYPES[cron.interval_type](cron.interval_number)
+
+ def _get_request_values(self, cron):
+ tb_s = reduce(lambda x, y: x + y,
+ traceback.format_exception(*sys.exc_info()))
+ tb_s = tb_s.decode('utf-8', 'ignore')
+ name = self.raise_user_error('request_title',
+ raise_exception=False)
+ body = self.raise_user_error('request_body', (cron.name, tb_s),
+ raise_exception=False)
+ values = {
+ 'name': name,
+ 'priority': '2',
+ 'act_from': cron.user.id,
+ 'act_to': cron.request_user.id,
+ 'body': body,
+ 'date_sent': datetime.datetime.now(),
+ 'references': [
+ ('create', {
+ 'reference': '%s,%s' % (self._name, cron.id),
+ }),
+ ],
+ 'state': 'waiting',
+ 'trigger_date': datetime.datetime.now(),
+ }
+ return values
+
def _callback(self, cron):
+ pool = Pool()
try:
- args = (cron['args'] or []) and safe_eval(cron['args'])
- obj = self.pool.get(cron['model'])
- if not obj and hasattr(obj, cron['function']):
- return False
- fct = getattr(obj, cron['function'])
- with Transaction().set_user(cron['user']):
- fct(*args)
+ args = (cron.args or []) and safe_eval(cron.args)
+ model_obj = pool.get(cron.model)
+ with Transaction().set_user(cron.user.id):
+ getattr(model_obj, cron.function)(*args)
except Exception, error:
Transaction().cursor.rollback()
- tb_s = ''
- for line in traceback.format_exception(*sys.exc_info()):
- try:
- line = line.encode('utf-8', 'ignore')
- except Exception:
- continue
- tb_s += line
- try:
- tb_s += str(error).decode('utf-8', 'ignore')
- except Exception:
- pass
+ request_obj = pool.get('res.request')
+ req_user = cron.request_user
+ language = (req_user.language.code if req_user.language
+ else 'en_US')
+ with contextlib.nested(Transaction().set_user(cron.user.id),
+ Transaction().set_context(language=language)):
+ values = self._get_request_values(cron)
+ rid = request_obj.create(values)
+ Transaction().cursor.commit()
- request_obj = self.pool.get('res.request')
- try:
- user_obj = self.pool.get('res.user')
- req_user = user_obj.browse(cron['request_user'])
- language = (req_user.language.code if req_user.language
- else 'en_US')
- with contextlib.nested(Transaction().set_user(cron['user']),
- Transaction().set_context(language=language)):
- rid = request_obj.create({
- 'name': self.raise_user_error('request_title',
- raise_exception=False),
- 'priority': '2',
- 'act_from': cron['user'],
- 'act_to': cron['request_user'],
- 'body': self.raise_user_error('request_body',
- (cron['name'], tb_s), raise_exception=False),
- 'date_sent': datetime.datetime.now(),
- 'references': [
- ('create', {
- 'reference': "ir.cron,%s" % cron['id'],
- }),
- ],
- 'state': 'waiting',
- 'trigger_date': datetime.datetime.now(),
- })
- Transaction().cursor.commit()
- except Exception:
- Transaction().cursor.rollback()
-
- def pool_jobs(self, db_name):
+ def run(self, db_name):
now = datetime.datetime.now()
with Transaction().start(db_name, 0) as transaction:
- try:
- transaction.cursor.lock(self._table)
- transaction.cursor.execute('SELECT * FROM ir_cron '
- 'WHERE numbercall <> 0 '
- 'AND active '
- 'AND nextcall <= %s '
- 'AND NOT running '
- 'ORDER BY priority', (datetime.datetime.now(),))
- crons = transaction.cursor.dictfetchall()
+ transaction.cursor.lock(self._table)
+ cron_ids = self.search([
+ ('number_calls', '!=', 0),
+ ('next_call', '<=', datetime.datetime.now()),
+ ])
+ crons = self.browse(cron_ids)
- for cron in crons:
- transaction.cursor.execute('UPDATE ir_cron '
- 'SET running = %s '
- 'WHERE id = %s', (True, cron['id']))
- nextcall = cron['nextcall']
- numbercall = cron['numbercall']
- done = False
-
- while nextcall < now and numbercall:
- if numbercall > 0:
- numbercall -= 1
- if not done or cron['doall']:
+ for cron in crons:
+ try:
+ next_call = cron.next_call
+ number_calls = cron.number_calls
+ first = True
+ while next_call < now and number_calls != 0:
+ if first or cron.repeat_missed:
self._callback(cron)
- if numbercall:
- nextcall += _INTERVALTYPES[cron['interval_type']](
- cron['interval_number'])
- done = True
+ next_call += self.get_delta(cron)
+ if number_calls > 0:
+ number_calls -= 1
+ first = False
- addsql = ''
- addsql_param = []
- if not numbercall:
- addsql = ', active = %s'
- addsql_param = [False]
- transaction.cursor.execute("UPDATE ir_cron "
- "SET nextcall = %s, "
- "running = %s, "
- "numbercall = %s" + addsql + " "
- "WHERE id = %s",
- [nextcall, False, numbercall] + addsql_param +
- [cron['id']])
+ values = {
+ 'next_call': next_call,
+ 'number_calls': number_calls,
+ }
+ if not number_calls:
+ values['active'] = False
+ self.write(cron.id, values)
transaction.cursor.commit()
- except Exception:
- transaction.cursor.rollback()
- raise
+ except Exception:
+ transaction.cursor.rollback()
+ tb_s = reduce(lambda x, y: x + y,
+ traceback.format_exception(*sys.exc_info()))
+ tb_s = tb_s.decode('utf-8', 'ignore')
+ logger = logging.getLogger('cron')
+ logger.error('Exception:\n%s' % tb_s)
Cron()
diff --git a/trytond/ir/cron.xml b/trytond/ir/cron.xml
index ac7842a8e9..cef16614b4 100644
--- a/trytond/ir/cron.xml
+++ b/trytond/ir/cron.xml
@@ -12,16 +12,14 @@ this repository contains the full copyright notices and license terms. -->
-
+
-
-
+
-
]]>
@@ -31,37 +29,33 @@ this repository contains the full copyright notices and license terms. -->
form
+
]]>
@@ -69,7 +63,7 @@ this repository contains the full copyright notices and license terms. -->
Scheduled Actions
ir.cron
- {'active_test': False}
+
diff --git a/trytond/ir/gen_time_locale.py b/trytond/ir/gen_time_locale.py
index 72f8f298d1..2aca343d09 100644
--- a/trytond/ir/gen_time_locale.py
+++ b/trytond/ir/gen_time_locale.py
@@ -1,6 +1,5 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
-from __future__ import with_statement
import time
import locale
import os
diff --git a/trytond/ir/lang.py b/trytond/ir/lang.py
index 6ce101fb87..2e74373943 100644
--- a/trytond/ir/lang.py
+++ b/trytond/ir/lang.py
@@ -1,12 +1,12 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
-from __future__ import with_statement
import time
import datetime
from trytond.model import ModelView, ModelSQL, fields
from trytond.model.cacheable import Cacheable
from trytond.tools import safe_eval, datetime_strftime
from trytond.transaction import Transaction
+from trytond.pool import Pool
from time_locale import TIME_LOCALE
from locale import CHAR_MAX
@@ -57,7 +57,8 @@ class Lang(ModelSQL, ModelView, Cacheable):
return [('name',) + clause[1:]]
def read(self, ids, fields_names=None):
- translation_obj = self.pool.get('ir.translation')
+ pool = Pool()
+ translation_obj = pool.get('ir.translation')
res = super(Lang, self).read(ids, fields_names=fields_names)
if (Transaction().context.get('translate_name')
and (not fields_names or 'name' in fields_names)):
diff --git a/trytond/ir/model.py b/trytond/ir/model.py
index bb36f8df80..65d0da6ce8 100644
--- a/trytond/ir/model.py
+++ b/trytond/ir/model.py
@@ -8,6 +8,7 @@ from trytond.report import Report
from trytond.wizard import Wizard
from trytond.transaction import Transaction
from trytond.cache import Cache
+from trytond.pool import Pool
IDENTIFIER = re.compile(r'^[a-zA-z_][a-zA-Z0-9_]*$')
@@ -47,21 +48,24 @@ class Model(ModelSQL, ModelView):
return True
def create(self, vals):
- property_obj = self.pool.get('ir.property')
+ pool = Pool()
+ property_obj = pool.get('ir.property')
res = super(Model, self).create(vals)
# Restart the cache of models_get
property_obj.models_get.reset()
return res
def write(self, ids, vals):
- property_obj = self.pool.get('ir.property')
+ pool = Pool()
+ property_obj = pool.get('ir.property')
res = super(Model, self).write(ids, vals)
# Restart the cache of models_get
property_obj.models_get.reset()
return res
def delete(self, ids):
- property_obj = self.pool.get('ir.property')
+ pool = Pool()
+ property_obj = pool.get('ir.property')
res = super(Model, self).delete(ids)
# Restart the cache of models_get
property_obj.models_get.reset()
@@ -117,7 +121,8 @@ class ModelField(ModelSQL, ModelView):
return True
def read(self, ids, fields_names=None):
- translation_obj = self.pool.get('ir.translation')
+ pool = Pool()
+ translation_obj = pool.get('ir.translation')
to_delete = []
if Transaction().context.get('language'):
@@ -249,8 +254,9 @@ class ModelAccess(ModelSQL, ModelView):
if Transaction().user == 0:
return True
- ir_model_obj = self.pool.get('ir.model')
- user_group_obj = self.pool.get('res.user-res.group')
+ pool = Pool()
+ ir_model_obj = pool.get('ir.model')
+ user_group_obj = pool.get('res.user-res.group')
cursor = Transaction().cursor
cursor.execute('SELECT MAX(CASE WHEN a.perm_%s THEN 1 ELSE 0 END) '
@@ -258,8 +264,8 @@ class ModelAccess(ModelSQL, ModelView):
'JOIN "%s" AS m '
'ON (a.model = m.id) '
'LEFT JOIN "%s" AS gu '
- 'ON (gu.gid = a."group") '
- 'WHERE m.model = %%s AND (gu.uid = %%s OR a."group" IS NULL)'
+ 'ON (gu."group" = a."group") '
+ 'WHERE m.model = %%s AND (gu."user" = %%s OR a."group" IS NULL)'
% (mode, self._table, ir_model_obj._table,
user_group_obj._table),
(model_name, Transaction().user))
@@ -275,7 +281,8 @@ class ModelAccess(ModelSQL, ModelView):
res = super(ModelAccess, self).write(ids, vals)
# Restart the cache
self.check.reset()
- for _, model in self.pool.iterobject():
+ pool = Pool()
+ for _, model in pool.iterobject():
try:
model.fields_view_get.reset()
except Exception:
@@ -286,7 +293,8 @@ class ModelAccess(ModelSQL, ModelView):
res = super(ModelAccess, self).create(vals)
# Restart the cache
self.check.reset()
- for _, model in self.pool.iterobject():
+ pool = Pool()
+ for _, model in pool.iterobject():
try:
model.fields_view_get.reset()
except Exception:
@@ -297,7 +305,8 @@ class ModelAccess(ModelSQL, ModelView):
res = super(ModelAccess, self).delete(ids)
# Restart the cache
self.check.reset()
- for _, model in self.pool.iterobject():
+ pool = Pool()
+ for _, model in pool.iterobject():
try:
model.fields_view_get.reset()
except Exception:
@@ -359,9 +368,10 @@ class ModelFieldAccess(ModelSQL, ModelView):
return dict((x, True) for x in fields)
return True
- ir_model_obj = self.pool.get('ir.model')
- ir_model_field_obj = self.pool.get('ir.model.field')
- user_group_obj = self.pool.get('res.user-res.group')
+ pool = Pool()
+ ir_model_obj = pool.get('ir.model')
+ ir_model_field_obj = pool.get('ir.model.field')
+ user_group_obj = pool.get('res.user-res.group')
cursor = Transaction().cursor
@@ -373,8 +383,8 @@ class ModelFieldAccess(ModelSQL, ModelView):
'JOIN "%s" AS m '
'ON (f.model = m.id) '
'LEFT JOIN "%s" AS gu '
- 'ON (gu.gid = a."group") '
- 'WHERE m.model = %%s AND (gu.uid = %%s OR a."group" IS NULL) '
+ 'ON (gu."group" = a."group") '
+ 'WHERE m.model = %%s AND (gu."user" = %%s OR a."group" IS NULL) '
'GROUP BY f.name'
% (mode, self._table, ir_model_field_obj._table,
ir_model_obj._table, user_group_obj._table),
@@ -394,7 +404,8 @@ class ModelFieldAccess(ModelSQL, ModelView):
res = super(ModelFieldAccess, self).write(ids, vals)
# Restart the cache
self.check.reset()
- for _, model in self.pool.iterobject():
+ pool = Pool()
+ for _, model in pool.iterobject():
try:
model.fields_view_get.reset()
except Exception:
@@ -405,7 +416,8 @@ class ModelFieldAccess(ModelSQL, ModelView):
res = super(ModelFieldAccess, self).create(vals)
# Restart the cache
self.check.reset()
- for _, model in self.pool.iterobject():
+ pool = Pool()
+ for _, model in pool.iterobject():
try:
model.fields_view_get.reset()
except Exception:
@@ -416,7 +428,8 @@ class ModelFieldAccess(ModelSQL, ModelView):
res = super(ModelFieldAccess, self).delete(ids)
# Restart the cache
self.check.reset()
- for _, model in self.pool.iterobject():
+ pool = Pool()
+ for _, model in pool.iterobject():
try:
model.fields_view_get.reset()
except Exception:
@@ -525,8 +538,9 @@ class ModelGraph(Report):
def execute(self, ids, datas):
import pydot
- model_obj = self.pool.get('ir.model')
- action_report_obj = self.pool.get('ir.action.report')
+ pool = Pool()
+ model_obj = pool.get('ir.model')
+ action_report_obj = pool.get('ir.action.report')
if not datas['form']['filter']:
filter = None
@@ -560,7 +574,8 @@ class ModelGraph(Report):
models
'''
import pydot
- model_obj = self.pool.get('ir.model')
+ pool = Pool()
+ model_obj = pool.get('ir.model')
sub_models = set()
if level > 0:
diff --git a/trytond/ir/module/module.py b/trytond/ir/module/module.py
index 4f3ee3ee57..701a7a0834 100644
--- a/trytond/ir/module/module.py
+++ b/trytond/ir/module/module.py
@@ -1,6 +1,5 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
-from __future__ import with_statement
import os
from trytond.model import ModelView, ModelSQL, fields
import trytond.tools as tools
@@ -216,7 +215,8 @@ class Module(ModelSQL, ModelView):
# update the list of available packages
def update_list(self):
- lang_obj = self.pool.get('ir.lang')
+ pool = Pool()
+ lang_obj = pool.get('ir.lang')
res = 0
with Transaction().set_context(language=False):
lang_ids = lang_obj.search([
@@ -313,7 +313,8 @@ class Module(ModelSQL, ModelView):
return res
def _update_dependencies(self, module, depends=None):
- dependency_obj = self.pool.get('ir.module.module.dependency')
+ pool = Pool()
+ dependency_obj = pool.get('ir.module.module.dependency')
dependency_obj.delete([x.id for x in module.dependencies
if x.name not in depends])
if depends is None:
@@ -354,7 +355,8 @@ class ModuleDependency(ModelSQL, ModelView):
def get_state(self, ids, name):
result = {}
- module_obj = self.pool.get('ir.module.module')
+ pool = Pool()
+ module_obj = pool.get('ir.module.module')
for dependency in self.browse(ids):
ids = module_obj.search([
('name', '=', dependency.name),
@@ -442,7 +444,8 @@ class ModuleConfigWizard(Wizard):
return res
def _action_wizard(self, data):
- item_obj = self.pool.get('ir.module.module.config_wizard.item')
+ pool = Pool()
+ item_obj = pool.get('ir.module.module.config_wizard.item')
item_ids = item_obj.search([
('state', '=', 'open'),
], limit=1)
@@ -458,7 +461,8 @@ class ModuleConfigWizard(Wizard):
return {}
def _next(self, data):
- item_obj = self.pool.get('ir.module.module.config_wizard.item')
+ pool = Pool()
+ item_obj = pool.get('ir.module.module.config_wizard.item')
item_ids = item_obj.search([
('state', '=', 'open'),
])
@@ -491,7 +495,8 @@ class ModuleInstallUpgrade(Wizard):
_name = 'ir.module.module.install_upgrade'
def _get_install(self, data):
- module_obj = self.pool.get('ir.module.module')
+ pool = Pool()
+ module_obj = pool.get('ir.module.module')
module_ids = module_obj.search([
('state', 'in', ['to upgrade', 'to remove', 'to install']),
])
@@ -502,9 +507,9 @@ class ModuleInstallUpgrade(Wizard):
}
def _upgrade_module(self, data):
- module_obj = self.pool.get('ir.module.module')
- lang_obj = self.pool.get('ir.lang')
- dbname = Transaction().cursor.dbname
+ pool = Pool()
+ module_obj = pool.get('ir.module.module')
+ lang_obj = pool.get('ir.lang')
with Transaction().new_cursor() as transaction:
module_ids = module_obj.search([
('state', 'in', ['to upgrade', 'to remove', 'to install']),
@@ -515,7 +520,6 @@ class ModuleInstallUpgrade(Wizard):
lang = [x.code for x in lang_obj.browse(lang_ids)]
transaction.cursor.commit()
if module_ids:
- pool = Pool(dbname)
pool.init(update=True, lang=lang)
new_wizard = pool.get('ir.module.module.install_upgrade',
type='wizard')
@@ -563,8 +567,9 @@ class ModuleInstallUpgrade(Wizard):
}
def _menu(self, data):
- model_data_obj = self.pool.get('ir.model.data')
- act_window_obj = self.pool.get('ir.action.act_window')
+ pool = Pool()
+ model_data_obj = pool.get('ir.model.data')
+ act_window_obj = pool.get('ir.action.act_window')
act_window_id = model_data_obj.get_id('ir', 'act_menu_tree')
res = act_window_obj.read(act_window_id)
return res
@@ -592,8 +597,9 @@ class ModuleConfig(Wizard):
}
def _action_open(self, datas):
- model_data_obj = self.pool.get('ir.model.data')
- act_window_obj = self.pool.get('ir.action.act_window')
+ pool = Pool()
+ model_data_obj = pool.get('ir.model.data')
+ act_window_obj = pool.get('ir.action.act_window')
act_window_id = model_data_obj.get_id('ir', 'act_module_form')
res = act_window_obj.read(act_window_id)
return res
diff --git a/trytond/ir/property.py b/trytond/ir/property.py
index 17c8365c32..ec08703131 100644
--- a/trytond/ir/property.py
+++ b/trytond/ir/property.py
@@ -1,10 +1,10 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
-from __future__ import with_statement
from decimal import Decimal
from trytond.model import ModelView, ModelSQL, fields
from trytond.transaction import Transaction
from trytond.cache import Cache
+from trytond.pool import Pool
class Property(ModelSQL, ModelView):
@@ -32,7 +32,8 @@ class Property(ModelSQL, ModelView):
:param res_ids: a list of record ids
:return: a dictionary
"""
- model_access_obj = self.pool.get('ir.model.access')
+ pool = Pool()
+ model_access_obj = pool.get('ir.model.access')
res = {}
model_access_obj.check(model, 'read')
@@ -44,7 +45,7 @@ class Property(ModelSQL, ModelView):
if res_ids is None:
res_ids = []
- model_obj = self.pool.get(model)
+ model_obj = pool.get(model)
fields = dict((name, field)
for name, field in model_obj._columns.iteritems()
if name in names)
@@ -126,8 +127,9 @@ class Property(ModelSQL, ModelView):
:param val: the value
:return: the id of the record created
"""
- model_field_obj = self.pool.get('ir.model.field')
- model_access_obj = self.pool.get('ir.model.access')
+ pool = Pool()
+ model_field_obj = pool.get('ir.model.field')
+ model_access_obj = pool.get('ir.model.access')
model_access_obj.check(model, 'write')
@@ -135,7 +137,7 @@ class Property(ModelSQL, ModelView):
('name', '=', name),
('model.model', '=', model),
], order=[], limit=1)[0]
- model_obj = self.pool.get(model)
+ model_obj = pool.get(model)
field = model_obj._columns[name]
property_ids = self.search([
diff --git a/trytond/ir/rule.py b/trytond/ir/rule.py
index 12e61f0846..678c8d01bf 100644
--- a/trytond/ir/rule.py
+++ b/trytond/ir/rule.py
@@ -1,6 +1,5 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
-from __future__ import with_statement
import contextlib
import time
from trytond.model import ModelView, ModelSQL, fields
@@ -9,6 +8,7 @@ from trytond.pyson import Eval, Get
from trytond.transaction import Transaction
from trytond.cache import Cache
from trytond.const import OPERATORS
+from trytond.pool import Pool
class RuleGroup(ModelSQL, ModelView):
@@ -26,9 +26,9 @@ class RuleGroup(ModelSQL, ModelView):
rules = fields.One2Many('ir.rule', 'rule_group', 'Tests',
help="The rule is satisfied if at least one test is True")
groups = fields.Many2Many('ir.rule.group-res.group',
- 'rule_group_id', 'group_id', 'Groups')
+ 'rule_group', 'group', 'Groups')
users = fields.Many2Many('ir.rule.group-res.user',
- 'rule_group_id', 'user_id', 'Users')
+ 'rule_group', 'user', 'Users')
perm_read = fields.Boolean('Read Access')
perm_write = fields.Boolean('Write Access')
perm_create = fields.Boolean('Create Access')
@@ -65,19 +65,19 @@ class RuleGroup(ModelSQL, ModelView):
def delete(self, ids):
res = super(RuleGroup, self).delete(ids)
# Restart the cache on the domain_get method of ir.rule
- self.pool.get('ir.rule').domain_get.reset()
+ Pool().get('ir.rule').domain_get.reset()
return res
def create(self, vals):
res = super(RuleGroup, self).create(vals)
# Restart the cache on the domain_get method of ir.rule
- self.pool.get('ir.rule').domain_get.reset()
+ Pool().get('ir.rule').domain_get.reset()
return res
def write(self, ids, vals):
res = super(RuleGroup, self).write(ids, vals)
# Restart the cache on the domain_get method of ir.rule
- self.pool.get('ir.rule').domain_get.reset()
+ Pool().get('ir.rule').domain_get.reset()
return res
RuleGroup()
@@ -115,7 +115,7 @@ class Rule(ModelSQL, ModelView):
if not recur:
recur = []
with Transaction().set_context(language='en_US'):
- obj_fields = self.pool.get(obj_name).fields_get()
+ obj_fields = Pool().get(obj_name).fields_get()
key = obj_fields.keys()
key.sort()
for k in key:
@@ -158,12 +158,13 @@ class Rule(ModelSQL, ModelView):
with Transaction().set_user(Transaction().context['user']):
return self.domain_get(model_name)
- rule_group_obj = self.pool.get('ir.rule.group')
- model_obj = self.pool.get('ir.model')
- rule_group_user_obj = self.pool.get('ir.rule.group-res.user')
- rule_group_group_obj = self.pool.get('ir.rule.group-res.group')
- user_group_obj = self.pool.get('res.user-res.group')
- user_obj = self.pool.get('res.user')
+ pool = Pool()
+ rule_group_obj = pool.get('ir.rule.group')
+ model_obj = pool.get('ir.model')
+ rule_group_user_obj = pool.get('ir.rule.group-res.user')
+ rule_group_group_obj = pool.get('ir.rule.group-res.group')
+ user_group_obj = pool.get('res.user-res.group')
+ user_obj = pool.get('res.user')
cursor = Transaction().cursor
cursor.execute('SELECT r.id FROM "' + self._table + '" r ' \
@@ -173,21 +174,21 @@ class Rule(ModelSQL, ModelView):
"WHERE m.model = %s "
"AND g.perm_" + mode + " "
"AND (g.id IN (" \
- 'SELECT rule_group_id ' \
+ 'SELECT rule_group ' \
'FROM "' + rule_group_user_obj._table + '" ' \
- "WHERE user_id = %s " \
- "UNION SELECT rule_group_id " \
+ 'WHERE "user" = %s ' \
+ "UNION SELECT rule_group " \
'FROM "' + rule_group_group_obj._table + '" g_rel ' \
'JOIN "' + user_group_obj._table + '" u_rel ' \
- "ON (g_rel.group_id = u_rel.gid) " \
- "WHERE u_rel.uid = %s) " \
+ 'ON (g_rel."group" = u_rel."group") ' \
+ 'WHERE u_rel."user" = %s) ' \
"OR default_p " \
"OR g.global_p)",
(model_name, Transaction().user, Transaction().user))
ids = [x[0] for x in cursor.fetchall()]
if not ids:
return '', []
- obj = self.pool.get(model_name)
+ obj = pool.get(model_name)
clause = {}
clause_global = {}
operand2query = self._operand_get('res.user', level=1,
@@ -222,14 +223,14 @@ class Rule(ModelSQL, ModelView):
'WHERE m.model = %s ' \
'AND (g.id NOT IN (SELECT rule_group ' \
'FROM "' + self._table + '")) ' \
- 'AND (g.id IN (SELECT rule_group_id ' \
+ 'AND (g.id IN (SELECT rule_group ' \
'FROM "' + rule_group_user_obj._table + '" ' \
- 'WHERE user_id = %s ' \
- 'UNION SELECT rule_group_id ' \
+ 'WHERE "user" = %s ' \
+ 'UNION SELECT rule_group ' \
'FROM "' + rule_group_group_obj._table + '" g_rel ' \
'JOIN "' + user_group_obj._table + '" u_rel ' \
- 'ON g_rel.group_id = u_rel.gid ' \
- 'WHERE u_rel.uid = %s))',
+ 'ON g_rel."group" = u_rel."group" ' \
+ 'WHERE u_rel."user" = %s))',
(model_name, user_id, user_id))
fetchone = cursor.fetchone()
if fetchone:
diff --git a/trytond/ir/sequence.py b/trytond/ir/sequence.py
index aa13ef7d91..9371bb102b 100644
--- a/trytond/ir/sequence.py
+++ b/trytond/ir/sequence.py
@@ -1,6 +1,5 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
-from __future__ import with_statement
from string import Template
import datetime
import time
@@ -8,6 +7,7 @@ from trytond.model import ModelView, ModelSQL, fields
from trytond.tools import datetime_strftime
from trytond.pyson import In, Eval, Not
from trytond.transaction import Transaction
+from trytond.pool import Pool
class SequenceType(ModelSQL, ModelView):
@@ -49,7 +49,7 @@ class Sequence(ModelSQL, ModelView):
states={
'invisible': Not(In(Eval('type'), ['incremental'])),
}, depends=['type'])
- timestamp_rounding = fields.Integer('Timestamp Rounding', required=True,
+ timestamp_rounding = fields.Float('Timestamp Rounding', required=True,
states={
'invisible': Not(In(Eval('type'),
['decimal timestamp', 'hexadecimal timestamp'])),
@@ -105,7 +105,8 @@ class Sequence(ModelSQL, ModelView):
return Transaction().context.get('code', False)
def code_get(self):
- sequence_type_obj = self.pool.get('ir.sequence.type')
+ pool = Pool()
+ sequence_type_obj = pool.get('ir.sequence.type')
sequence_type_ids = sequence_type_obj.search([])
sequence_types = sequence_type_obj.browse(sequence_type_ids)
return [(x.code, x.name) for x in sequence_types]
@@ -131,7 +132,8 @@ class Sequence(ModelSQL, ModelView):
return True
def _process(self, string, date=None):
- date_obj = self.pool.get('ir.date')
+ pool = Pool()
+ date_obj = pool.get('ir.date')
if not date:
date = date_obj.today()
year = datetime_strftime(date, '%Y')
diff --git a/trytond/ir/translation.py b/trytond/ir/translation.py
index 142b9355c9..8cf89f4996 100644
--- a/trytond/ir/translation.py
+++ b/trytond/ir/translation.py
@@ -1,6 +1,5 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
-from __future__ import with_statement
import contextlib
import base64
try:
@@ -17,6 +16,7 @@ try:
from hashlib import md5
except ImportError:
from md5 import md5
+from functools import reduce
from trytond.model import ModelView, ModelSQL, fields
from trytond.model.cacheable import Cacheable
from trytond.wizard import Wizard
@@ -25,6 +25,7 @@ from trytond.tools import file_open, reduce_ids
from trytond.backend import TableHandler, FIELDS
from trytond.pyson import PYSONEncoder
from trytond.transaction import Transaction
+from trytond.pool import Pool
TRANSLATION_TYPE = [
('field', 'Field'),
@@ -143,7 +144,8 @@ class Translation(ModelSQL, ModelView, Cacheable):
return [('id', 'in', [x[0] for x in cursor.fetchall()])]
def get_language(self):
- lang_obj = self.pool.get('ir.lang')
+ pool = Pool()
+ lang_obj = pool.get('ir.lang')
lang_ids = lang_obj.search([])
langs = lang_obj.browse(lang_ids)
res = [(lang.code, lang.name) for lang in langs]
@@ -153,8 +155,9 @@ class Translation(ModelSQL, ModelView, Cacheable):
return md5((src or '').encode('utf-8')).hexdigest()
def _get_ids(self, name, ttype, lang, ids):
- model_fields_obj = self.pool.get('ir.model.field')
- model_obj = self.pool.get('ir.model')
+ pool = Pool()
+ model_fields_obj = pool.get('ir.model.field')
+ model_obj = pool.get('ir.model')
translations, to_fetch = {}, []
name = unicode(name)
@@ -220,8 +223,9 @@ class Translation(ModelSQL, ModelView, Cacheable):
return translations
def _set_ids(self, name, ttype, lang, ids, value):
- model_fields_obj = self.pool.get('ir.model.field')
- model_obj = self.pool.get('ir.model')
+ pool = Pool()
+ model_fields_obj = pool.get('ir.model.field')
+ model_obj = pool.get('ir.model')
if lang == 'en_US':
return 0
@@ -262,7 +266,7 @@ class Translation(ModelSQL, ModelView, Cacheable):
'fuzzy': False,
})
return len(ids)
- model_obj = self.pool.get(model_name)
+ model_obj = pool.get(model_name)
for record in model_obj.browse(ids):
ids2 = self.search([
('lang', '=', lang),
@@ -439,7 +443,8 @@ class Translation(ModelSQL, ModelView, Cacheable):
return super(Translation, self).write(ids, vals)
def translation_import(self, lang, module, datas):
- model_data_obj = self.pool.get('ir.model.data')
+ pool = Pool()
+ model_data_obj = pool.get('ir.model.data')
model_data_ids = model_data_obj.search([
('module', '=', module),
])
@@ -537,7 +542,8 @@ class Translation(ModelSQL, ModelView, Cacheable):
return len(translation_ids)
def translation_export(self, lang, module):
- model_data_obj = self.pool.get('ir.model.data')
+ pool = Pool()
+ model_data_obj = pool.get('ir.model.data')
model_data_ids = model_data_obj.search([
('module', '=', module),
@@ -651,7 +657,9 @@ class ReportTranslationSet(Wizard):
return strings
def _set_report_translation(self, data):
- report_obj = self.pool.get('ir.action.report')
+ pool = Pool()
+ report_obj = pool.get('ir.action.report')
+ translation_obj = pool.get('ir.translation')
with Transaction().set_context(active_test=False):
report_ids = report_obj.search([])
@@ -713,7 +721,7 @@ class ReportTranslationSet(Wizard):
strings += self._translate_report(document.documentElement)
for string in {}.fromkeys(strings).keys():
- src_md5 = self.get_src_md5(string)
+ src_md5 = translation_obj.get_src_md5(string)
done = False
if string in trans_reports:
del trans_reports[string]
@@ -807,9 +815,10 @@ class TranslationClean(Wizard):
}
def _clean_translation(self, data):
- translation_obj = self.pool.get('ir.translation')
- model_data_obj = self.pool.get('ir.model.data')
- report_obj = self.pool.get('ir.action.report')
+ pool = Pool()
+ translation_obj = pool.get('ir.translation')
+ model_data_obj = pool.get('ir.model.data')
+ report_obj = pool.get('ir.action.report')
offset = 0
cursor = Transaction().cursor
@@ -828,10 +837,10 @@ class TranslationClean(Wizard):
except ValueError:
to_delete.append(translation.id)
continue
- if model_name not in self.pool.object_name_list():
+ if model_name not in pool.object_name_list():
to_delete.append(translation.id)
continue
- model_obj = self.pool.get(model_name)
+ model_obj = pool.get(model_name)
if field_name not in model_obj._columns:
to_delete.append(translation.id)
continue
@@ -841,11 +850,11 @@ class TranslationClean(Wizard):
except ValueError:
to_delete.append(translation.id)
continue
- if model_name not in self.pool.object_name_list():
+ if model_name not in pool.object_name_list():
to_delete.append(translation.id)
continue
if translation.res_id:
- model_obj = self.pool.get(model_name)
+ model_obj = pool.get(model_name)
if field_name not in model_obj._columns:
to_delete.append(translation.id)
continue
@@ -870,10 +879,10 @@ class TranslationClean(Wizard):
except ValueError:
to_delete.append(translation.id)
continue
- if model_name not in self.pool.object_name_list():
+ if model_name not in pool.object_name_list():
to_delete.append(translation.id)
continue
- model_obj = self.pool.get(model_name)
+ model_obj = pool.get(model_name)
if field_name not in model_obj._columns:
to_delete.append(translation.id)
continue
@@ -890,7 +899,7 @@ class TranslationClean(Wizard):
continue
elif translation.type == 'view':
model_name = translation.name
- if model_name not in self.pool.object_name_list():
+ if model_name not in pool.object_name_list():
to_delete.append(translation.id)
continue
elif translation.type == 'wizard_button':
@@ -901,10 +910,10 @@ class TranslationClean(Wizard):
to_delete.append(translation.id)
continue
if wizard_name not in \
- self.pool.object_name_list(type='wizard'):
+ pool.object_name_list(type='wizard'):
to_delete.append(translation.id)
continue
- wizard = self.pool.get(wizard_name, type='wizard')
+ wizard = pool.get(wizard_name, type='wizard')
if not wizard:
to_delete.append(translation.id)
continue
@@ -925,10 +934,10 @@ class TranslationClean(Wizard):
except ValueError:
to_delete.append(translation.id)
continue
- if model_name not in self.pool.object_name_list():
+ if model_name not in pool.object_name_list():
to_delete.append(translation.id)
continue
- model_obj = self.pool.get(model_name)
+ model_obj = pool.get(model_name)
if field_name not in model_obj._columns:
to_delete.append(translation.id)
continue
@@ -961,8 +970,8 @@ class TranslationClean(Wizard):
'search_function_missing',
):
continue
- if model_name in self.pool.object_name_list():
- model_obj = self.pool.get(model_name)
+ if model_name in pool.object_name_list():
+ model_obj = pool.get(model_name)
errors = model_obj._error_messages.values() + \
model_obj._sql_error_messages.values()
for _, _, error in model_obj._sql_constraints:
@@ -970,8 +979,8 @@ class TranslationClean(Wizard):
if translation.src not in errors:
to_delete.append(translation.id)
continue
- elif model_name in self.pool.object_name_list(type='wizard'):
- wizard_obj = self.pool.get(model_name, type='wizard')
+ elif model_name in pool.object_name_list(type='wizard'):
+ wizard_obj = pool.get(model_name, type='wizard')
errors = wizard_obj._error_messages.values()
if translation.src not in errors:
to_delete.append(translation.id)
@@ -1005,7 +1014,8 @@ class TranslationUpdateInit(ModelView):
return Transaction().context.get('language', False)
def get_language(self):
- lang_obj = self.pool.get('ir.lang')
+ pool = Pool()
+ lang_obj = pool.get('ir.lang')
lang_ids = lang_obj.search([('translatable', '=', True)])
langs = lang_obj.browse(lang_ids)
res = [(lang.code, lang.name) for lang in langs if lang.code != 'en_US']
@@ -1019,7 +1029,8 @@ class TranslationUpdate(Wizard):
_name = "ir.translation.update"
def _update_translation(self, data):
- translation_obj = self.pool.get('ir.translation')
+ pool = Pool()
+ translation_obj = pool.get('ir.translation')
cursor = Transaction().cursor
cursor.execute('SELECT name, res_id, type, src, module ' \
'FROM ir_translation ' \
@@ -1107,8 +1118,9 @@ class TranslationUpdate(Wizard):
return {}
def _action_translation_open(self, data):
- model_data_obj = self.pool.get('ir.model.data')
- act_window_obj = self.pool.get('ir.action.act_window')
+ pool = Pool()
+ model_data_obj = pool.get('ir.model.data')
+ act_window_obj = pool.get('ir.action.act_window')
model_data_ids = model_data_obj.search([
('fs_id', '=', 'act_translation_form'),
@@ -1161,7 +1173,8 @@ class TranslationExportInit(ModelView):
return Transaction().context.get('language', False)
def get_language(self):
- lang_obj = self.pool.get('ir.lang')
+ pool = Pool()
+ lang_obj = pool.get('ir.lang')
lang_ids = lang_obj.search([
('translatable', '=', True),
])
@@ -1170,7 +1183,8 @@ class TranslationExportInit(ModelView):
return res
def get_module(self):
- module_obj = self.pool.get('ir.module.module')
+ pool = Pool()
+ module_obj = pool.get('ir.module.module')
module_ids = module_obj.search([
('state', 'in', ['installed', 'to upgrade', 'to remove']),
])
@@ -1195,7 +1209,8 @@ class TranslationExport(Wizard):
_name = "ir.translation.export"
def _export_translation(self, data):
- translation_obj = self.pool.get('ir.translation')
+ pool = Pool()
+ translation_obj = pool.get('ir.translation')
file_data = translation_obj.translation_export(data['form']['lang'],
data['form']['module'])
return {
diff --git a/trytond/ir/trigger.py b/trytond/ir/trigger.py
index 971f29f6c1..2e8cd12d18 100644
--- a/trytond/ir/trigger.py
+++ b/trytond/ir/trigger.py
@@ -9,6 +9,7 @@ from trytond.backend import TableHandler
from trytond.tools import reduce_ids
from trytond.transaction import Transaction
from trytond.cache import Cache
+from trytond.pool import Pool
class Trigger(ModelSQL, ModelView):
@@ -135,7 +136,8 @@ class Trigger(ModelSQL, ModelView):
:param record: a BrowseRecord of the tested model
:return: a boolean
"""
- model_obj = self.pool.get(trigger.model.model)
+ pool = Pool()
+ model_obj = pool.get(trigger.model.model)
env = {}
env['current_date'] = datetime.datetime.today()
env['time'] = time
@@ -150,9 +152,10 @@ class Trigger(ModelSQL, ModelView):
:param ids: the list of record ids triggered
:param trigger_id: the trigger id
"""
- trigger_log_obj = self.pool.get('ir.trigger.log')
+ pool = Pool()
+ trigger_log_obj = pool.get('ir.trigger.log')
trigger = self.browse(trigger_id)
- model_obj = self.pool.get(trigger.action_model.model)
+ model_obj = pool.get(trigger.action_model.model)
cursor = Transaction().cursor
# Filter on limit_number
@@ -220,11 +223,12 @@ class Trigger(ModelSQL, ModelView):
'''
Trigger time actions
'''
+ pool = Pool()
trigger_ids = self.search([
('on_time', '=', True),
])
for trigger in self.browse(trigger_ids):
- model_obj = self.pool.get(trigger.model.model)
+ model_obj = pool.get(trigger.model.model)
triggered_ids = []
# TODO add a domain
record_ids = model_obj.search([])
diff --git a/trytond/ir/ui/icon.py b/trytond/ir/ui/icon.py
index ef64c96599..fdbbd83638 100644
--- a/trytond/ir/ui/icon.py
+++ b/trytond/ir/ui/icon.py
@@ -1,6 +1,5 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of this
# repository contains the full copyright notices and license terms.
-from __future__ import with_statement
import os
diff --git a/trytond/ir/ui/menu.py b/trytond/ir/ui/menu.py
index 4e722030e5..547964e693 100644
--- a/trytond/ir/ui/menu.py
+++ b/trytond/ir/ui/menu.py
@@ -1,8 +1,8 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
-from __future__ import with_statement
from trytond.model import ModelView, ModelSQL, fields
from trytond.transaction import Transaction
+from trytond.pool import Pool
def one_in(i, j):
"""Check the presence of an element of setA in setB
@@ -58,6 +58,7 @@ CLIENT_ICONS = [(x, x) for x in (
'tryton-system',
'tryton-undo',
'tryton-web-browser')]
+SEPARATOR = ' / '
class UIMenu(ModelSQL, ModelView):
@@ -70,9 +71,9 @@ class UIMenu(ModelSQL, ModelView):
parent = fields.Many2One('ir.ui.menu', 'Parent Menu', select=1,
ondelete='CASCADE')
groups = fields.Many2Many('ir.ui.menu-res.group',
- 'menu_id', 'gid', 'Groups')
+ 'menu', 'group', 'Groups')
complete_name = fields.Function(fields.Char('Complete Name',
- order_field='name'), 'get_full_name')
+ order_field='name'), 'get_rec_name', searcher='search_rec_name')
icon = fields.Selection('list_icons', 'Icon')
action = fields.Function(fields.Reference('Action',
selection=[
@@ -86,6 +87,14 @@ class UIMenu(ModelSQL, ModelView):
def __init__(self):
super(UIMenu, self).__init__()
self._order.insert(0, ('sequence', 'ASC'))
+ self._constraints += [
+ ('check_recursion', 'recursive_menu'),
+ ('check_name', 'wrong_name'),
+ ]
+ self._error_messages.update({
+ 'recursive_menu': 'You can not create recursive menu!',
+ 'wrong_name': 'You can not use "%s" in name field!' % SEPARATOR,
+ })
def default_icon(self):
return 'tryton-open'
@@ -97,24 +106,45 @@ class UIMenu(ModelSQL, ModelView):
return True
def list_icons(self):
- icon_obj = self.pool.get('ir.ui.icon')
+ pool = Pool()
+ icon_obj = pool.get('ir.ui.icon')
return sorted(CLIENT_ICONS
+ [(name, name) for _, name in icon_obj.list_icons()])
- def get_full_name(self, ids, name):
- res = {}
+ def check_name(self, ids):
for menu in self.browse(ids):
- res[menu.id] = self._get_one_full_name(menu)
+ if SEPARATOR in menu.name:
+ return False
+ return True
+
+ def get_rec_name(self, ids, name):
+ if not ids:
+ return {}
+ res = {}
+ def _name(menu):
+ if menu.id in res:
+ return res[menu.id]
+ elif menu.parent:
+ return _name(menu.parent) + SEPARATOR + menu.name
+ else:
+ return menu.name
+ for menu in self.browse(ids):
+ res[menu.id] = _name(menu)
return res
- def _get_one_full_name(self, menu, level=6):
- if level <= 0:
- return '...'
- if menu.parent:
- parent_path = self._get_one_full_name(menu.parent, level-1) + "/"
- else:
- parent_path = ''
- return parent_path + menu.name
+ def search_rec_name(self, name, clause):
+ if isinstance(clause[2], basestring):
+ values = clause[2].split(SEPARATOR)
+ values.reverse()
+ domain = []
+ field = 'name'
+ for name in values:
+ domain.append((field, clause[1], name))
+ field = 'parent.' + field
+ ids = self.search(domain, order=[])
+ return [('id', 'in', ids)]
+ #TODO Handle list
+ return [('name',) + tuple(clause[1:])]
def search(self, domain, offset=0, limit=None, order=None, count=False,
query_string=False):
@@ -137,7 +167,8 @@ class UIMenu(ModelSQL, ModelView):
return res
def get_action(self, ids, name):
- action_keyword_obj = self.pool.get('ir.action.keyword')
+ pool = Pool()
+ action_keyword_obj = pool.get('ir.action.keyword')
res = {}
for menu_id in ids:
res[menu_id] = False
@@ -149,7 +180,7 @@ class UIMenu(ModelSQL, ModelView):
for action_keyword in action_keyword_obj.browse(action_keyword_ids):
model_id = int(
action_keyword.model.split(',')[1].split(',')[0].strip('('))
- action_obj = self.pool.get(action_keyword.action.type)
+ action_obj = pool.get(action_keyword.action.type)
with Transaction().set_context(active_test=False):
action_id = action_obj.search([
('action', '=', action_keyword.action.id),
@@ -164,7 +195,8 @@ class UIMenu(ModelSQL, ModelView):
def set_action(self, ids, name, value):
if not value:
return
- action_keyword_obj = self.pool.get('ir.action.keyword')
+ pool = Pool()
+ action_keyword_obj = pool.get('ir.action.keyword')
action_keyword_ids = []
cursor = Transaction().cursor
for i in range(0, len(ids), cursor.IN_MAX):
@@ -180,7 +212,7 @@ class UIMenu(ModelSQL, ModelView):
action_type, action_id = value.split(',')
if not int(action_id):
return
- action_obj = self.pool.get(action_type)
+ action_obj = pool.get(action_type)
action = action_obj.browse(int(action_id))
for menu_id in ids:
with Transaction().set_context(_timestamp=False):
diff --git a/trytond/ir/ui/menu.xml b/trytond/ir/ui/menu.xml
index c0dd633e41..c9db9ab078 100644
--- a/trytond/ir/ui/menu.xml
+++ b/trytond/ir/ui/menu.xml
@@ -24,7 +24,7 @@ this repository contains the full copyright notices and license terms. -->
-
+
]]>
@@ -50,7 +50,6 @@ this repository contains the full copyright notices and license terms. -->
-
]]>
@@ -69,14 +68,25 @@ this repository contains the full copyright notices and license terms. -->
+
+
+
+ action="act_menu_list" id="menu_menu_list"/>
diff --git a/trytond/ir/ui/view.py b/trytond/ir/ui/view.py
index f2d2b5e95a..9c83c7c6d6 100644
--- a/trytond/ir/ui/view.py
+++ b/trytond/ir/ui/view.py
@@ -1,6 +1,5 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
-from __future__ import with_statement
import os
import logging
from lxml import etree
@@ -11,6 +10,7 @@ from trytond.pyson import PYSONEncoder, CONTEXT, Eval, Not, Bool, Equal
from trytond.tools import safe_eval
from trytond.transaction import Transaction
from trytond.wizard import Wizard
+from trytond.pool import Pool
class View(ModelSQL, ModelView):
@@ -66,7 +66,8 @@ class View(ModelSQL, ModelView):
def check_xml(self, ids):
"Check XML"
- translation_obj = self.pool.get('ir.translation')
+ pool = Pool()
+ translation_obj = pool.get('ir.translation')
cursor = Transaction().cursor
views = self.browse(ids)
for view in views:
@@ -188,7 +189,8 @@ class View(ModelSQL, ModelView):
def delete(self, ids):
res = super(View, self).delete(ids)
# Restart the cache
- for _, model in self.pool.iterobject():
+ pool = Pool()
+ for _, model in pool.iterobject():
try:
model.fields_view_get.reset()
except Exception:
@@ -198,7 +200,8 @@ class View(ModelSQL, ModelView):
def create(self, vals):
res = super(View, self).create(vals)
# Restart the cache
- for _, model in self.pool.iterobject():
+ pool = Pool()
+ for _, model in pool.iterobject():
try:
model.fields_view_get.reset()
except Exception:
@@ -208,7 +211,8 @@ class View(ModelSQL, ModelView):
def write(self, ids, vals):
res = super(View, self).write(ids, vals)
# Restart the cache
- for _, model in self.pool.iterobject():
+ pool = Pool()
+ for _, model in pool.iterobject():
try:
model.fields_view_get.reset()
except Exception:
@@ -274,8 +278,9 @@ class AddShortcut(Wizard):
}
def _add_shortcut(self, data):
- view_sc_obj = self.pool.get('ir.ui.view_sc')
- model_obj = self.pool.get(data['model'])
+ pool = Pool()
+ view_sc_obj = pool.get('ir.ui.view_sc')
+ model_obj = pool.get(data['model'])
record = model_obj.browse(data['id'])
view_sc_obj.create({
@@ -304,8 +309,9 @@ class OpenShortcut(Wizard):
}
def _open(self, data):
- view_sc_obj = self.pool.get('ir.ui.view_sc')
- action_keyword_obj = self.pool.get('ir.action.keyword')
+ pool = Pool()
+ view_sc_obj = pool.get('ir.ui.view_sc')
+ action_keyword_obj = pool.get('ir.action.keyword')
view_sc = view_sc_obj.browse(data['id'])
models = (
@@ -323,7 +329,7 @@ class OpenShortcut(Wizard):
if not action_keyword_ids:
return {}
action_keyword = action_keyword_obj.browse(action_keyword_ids[0])
- action_obj = self.pool.get(action_keyword.action.type)
+ action_obj = pool.get(action_keyword.action.type)
action_ids = action_obj.search([
('action.id', '=', action_keyword.action.id),
])
@@ -352,37 +358,40 @@ class ViewTreeWidth(ModelSQL, ModelView):
})
def delete(self, ids):
+ pool = Pool()
if isinstance(ids, (int, long)):
ids = [ids]
views = self.browse(ids)
for view in views:
# Restart the cache
try:
- self.pool.get(view.model).fields_view_get.reset()
+ pool.get(view.model).fields_view_get.reset()
except Exception:
pass
res = super(ViewTreeWidth, self).delete(ids)
return res
def create(self, vals):
+ pool = Pool()
res = super(ViewTreeWidth, self).create(vals)
if 'model' in vals:
model = vals['model']
# Restart the cache
try:
- self.pool.get(model).fields_view_get.reset()
+ pool.get(model).fields_view_get.reset()
except Exception:
pass
return res
def write(self, ids, vals):
+ pool = Pool()
if isinstance(ids, (int, long)):
ids = [ids]
views = self.browse(ids)
for view in views:
# Restart the cache
try:
- self.pool.get(view.model).fields_view_get.reset()
+ pool.get(view.model).fields_view_get.reset()
except Exception:
pass
res = super(ViewTreeWidth, self).write(ids, vals)
@@ -390,7 +399,7 @@ class ViewTreeWidth(ModelSQL, ModelView):
for view in views:
# Restart the cache
try:
- self.pool.get(view.model).fields_view_get.reset()
+ pool.get(view.model).fields_view_get.reset()
except Exception:
pass
return res
diff --git a/trytond/model/browse.py b/trytond/model/browse.py
index 1d389efc4f..c7e5f86814 100644
--- a/trytond/model/browse.py
+++ b/trytond/model/browse.py
@@ -1,11 +1,14 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
-from __future__ import with_statement
import contextlib
-from itertools import islice, ifilter, ifilterfalse
+from itertools import islice, ifilter, ifilterfalse, chain
from trytond.model import fields
from trytond.transaction import Transaction
-from trytond.const import BROWSE_FIELD_TRESHOLD
+from trytond.pool import Pool
+from trytond.cache import LRUDict
+from trytond.const import MODEL_CACHE_SIZE, RECORD_CACHE_SIZE, \
+ BROWSE_FIELD_TRESHOLD
+
class BrowseRecordList(list):
'''
@@ -46,164 +49,177 @@ class BrowseRecord(object):
An object that represents record defined by a ORM object.
'''
- def __init__(self, record_id, model, local_cache=None):
+ def __init__(self, record_id, model, ids, local_cache):
+ assert isinstance(ids, list)
+ if ids:
+ assert record_id in ids
self._cursor = Transaction().cursor
self._user = Transaction().user
- self._id = record_id
+ self.id = record_id
self._model = model
self._model_name = self._model._name
self._context = Transaction().context
+ self._pool = Pool()
+ self._ids = ids
cache = self._cursor.get_cache(self._context)
- cache.setdefault(model._name, {})
- cache.setdefault('_language_cache', {})
+ if model._name not in cache:
+ cache[model._name] = LRUDict(RECORD_CACHE_SIZE)
self._data = cache[model._name]
- if not record_id in self._data:
- self._data[record_id] = {'id': record_id}
self._cache = cache
- if local_cache is None:
- local_cache = {}
- self._local_cache = local_cache
- self._local_cache.setdefault(model._name, {})
- self._local_cache.setdefault('_language_cache', {})
- self._local_data = self._local_cache[model._name]
- if not record_id in self._local_data:
- self._local_data[record_id] = {}
+ assert isinstance(local_cache, LRUDict)
+ self._local_data = local_cache
def __getitem__(self, name):
- if name == 'id':
- return self._id
- if name == 'setLang':
- return self.setLang
- if not self._data[self._id].has_key(name) \
- and not self._local_data[self._id].has_key(name):
- # build the list of fields we will fetch
+ # Access to LRUDict must be atomic
+ result = self._local_data.get(self.id, {}).get(name)
+ if (self.id in self._local_data
+ and name in self._local_data[self.id]):
+ return result
+ result = self._data.get(self.id, {}).get(name)
+ if self.id in self._data and name in self._data[self.id]:
+ return result
- # fetch the definition of the field which was asked for
- if name in self._model._columns:
- col = self._model._columns[name]
- elif name in self._model._inherit_fields:
- col = self._model._inherit_fields[name][2]
- elif hasattr(self._model, name):
- return getattr(self._model, name)
- else:
- raise Exception('Error', 'Programming error: field "%s" ' \
- 'does not exist in model "%s"!' \
- % (name, self._model._name))
+ # build the list of fields we will fetch
+ # fetch the definition of the field which was asked for
+ if name in self._model._columns:
+ col = self._model._columns[name]
+ elif name in self._model._inherit_fields:
+ col = self._model._inherit_fields[name][2]
+ elif hasattr(self._model, name):
+ return getattr(self._model, name)
+ else:
+ raise Exception('Error', 'Programming error: field "%s" ' \
+ 'does not exist in model "%s"!' \
+ % (name, self._model._name))
- ffields = {}
- if col.loading == 'eager':
- field_access_obj = self._model.pool.get('ir.model.field.access')
- fread_accesses = {}
- for inherit_name in self._model._inherits:
- inherit_obj = self._model.pool.get(inherit_name)
- fread_accesses.update(field_access_obj.check(inherit_name,
- inherit_obj._columns.keys(), 'read', access=True))
- fread_accesses.update(field_access_obj.check(self._model._name,
- self._model._columns.keys(), 'read', access=True))
- to_remove = set(x for x, y in fread_accesses.iteritems()
- if not y and x != name)
+ ffields = {}
+ if col.loading == 'eager':
+ field_access_obj = self._pool.get('ir.model.field.access')
+ fread_accesses = {}
+ for inherit_name in self._model._inherits:
+ inherit_obj = self._pool.get(inherit_name)
+ fread_accesses.update(field_access_obj.check(inherit_name,
+ inherit_obj._columns.keys(), 'read', access=True))
+ fread_accesses.update(field_access_obj.check(self._model._name,
+ self._model._columns.keys(), 'read', access=True))
+ to_remove = set(x for x, y in fread_accesses.iteritems()
+ if not y and x != name)
- threshold = BROWSE_FIELD_TRESHOLD
- inherit_threshold = threshold - len(self._model._columns)
+ threshold = BROWSE_FIELD_TRESHOLD
+ inherit_threshold = threshold - len(self._model._columns)
- def not_cached(item):
- fname, field = item
- return (fname not in self._data[self._id]
- and fname not in self._local_data[self._id])
- def to_load(item):
- fname, field = item
- return (field.loading == 'eager'
- and fname not in to_remove)
- def overrided(item):
- fname, field = item
- return fname in self._model._columns
+ def not_cached(item):
+ fname, field = item
+ return (fname not in self._data.get(self.id, {})
+ and fname not in self._local_data.get(self.id, {}))
+ def to_load(item):
+ fname, field = item
+ return (field.loading == 'eager'
+ and fname not in to_remove)
+ def overrided(item):
+ fname, field = item
+ return fname in self._model._columns
- if inherit_threshold > 0:
- ifields = ((fname, field)
- for fname, (_, _, field) in
- self._model._inherit_fields.iteritems())
- ifields = ifilterfalse(overrided,
- ifilter(to_load,
- ifilter(not_cached, ifields)))
- ifields = islice(ifields, 0, inherit_threshold)
- ffields.update(ifields)
- threshold -= inherit_threshold
-
- ifields = ifilter(to_load,
- ifilter(not_cached,
- self._model._columns.iteritems()))
- ifields = islice(ifields, 0, threshold)
+ if inherit_threshold > 0:
+ ifields = ((fname, field)
+ for fname, (_, _, field) in
+ self._model._inherit_fields.iteritems())
+ ifields = ifilterfalse(overrided,
+ ifilter(to_load,
+ ifilter(not_cached, ifields)))
+ ifields = islice(ifields, 0, inherit_threshold)
ffields.update(ifields)
+ threshold -= inherit_threshold
- ffields[name] = col
+ ifields = ifilter(to_load,
+ ifilter(not_cached,
+ self._model._columns.iteritems()))
+ ifields = islice(ifields, 0, threshold)
+ ffields.update(ifields)
- # add datetime_field
- for field in ffields:
- if hasattr(field, 'datetime_field') and field.datetime_field:
- if field.datetime_field in self._model._columns:
- date_field = self._model._columns[field.datetime_field]
- else:
- date_field = self._model._inherit_fields[
- field.datetime_field][2]
- ffields[field.datetime_field] = datetime_field
+ ffields[name] = col
- if len(self._data) <= self._cursor.IN_MAX:
- iterids = self._data.iterkeys()
- else:
- iterids = self._local_data.iterkeys()
- ids = [x for x in iterids \
- if not self._data.setdefault(x, {}).has_key(name) \
- and not self._local_data.setdefault(x, {}).has_key(name)]
- # read the data
- with contextlib.nested(Transaction().set_cursor(self._cursor),
- Transaction().set_user(self._user),
- Transaction().set_context(self._context)):
- datas = self._model.read(ids, ffields.keys())
+ # add datetime_field
+ for field in ffields:
+ if hasattr(field, 'datetime_field') and field.datetime_field:
+ if field.datetime_field in self._model._columns:
+ date_field = self._model._columns[field.datetime_field]
+ else:
+ date_field = self._model._inherit_fields[
+ field.datetime_field][2]
+ ffields[field.datetime_field] = datetime_field
- # create browse records for 'remote' models
- for data in datas:
- for i, j in ffields.iteritems():
- model = None
- if (hasattr(j, 'model_name') and
- j.model_name in
- self._model.pool.object_name_list()):
- model = self._model.pool.get(j.model_name)
- elif hasattr(j, 'get_target'):
- model = j.get_target(self._model.pool)
- if model and j._type in ('many2one', 'one2one'):
- if (not data[i]
- and not (isinstance(data[i], (int, long))
- and not isinstance(data[i],
- type(False)))):
- data[i] = BrowseRecordNull()
- else:
- _datetime = None
- if (hasattr(j, 'datetime_field')
- and j.datetime_field):
- _datetime = data[j.datetime_field]
- with Transaction().set_context(
- _datetime=_datetime):
- data[i] = BrowseRecord(data[i], model,
- local_cache=self._local_cache)
- elif (model
- and j._type in ('one2many', 'many2many')
- and len(data[i])):
+ def filter_(id_):
+ if (id_ in self._local_data
+ and name in self._local_data[id_]):
+ return False
+ if id_ in self._data and name in self._data[id_]:
+ return False
+ return True
+ index = self._ids.index(self.id)
+ ids = chain(islice(self._ids, index, None),
+ islice(self._ids, 0, max(index - 1, 0)))
+ ids = list(islice(ifilter(filter_, ids), self._cursor.IN_MAX))
+ model2ids = {}
+ model2cache = {}
+ # read the data
+ with contextlib.nested(Transaction().set_cursor(self._cursor),
+ Transaction().set_user(self._user),
+ Transaction().set_context(self._context)):
+ # create browse records for 'remote' models
+ for data in self._model.read(ids, ffields.keys()):
+ for i, j in ffields.iteritems():
+ model = None
+ if (hasattr(j, 'model_name') and
+ j.model_name in
+ self._pool.object_name_list()):
+ model = self._pool.get(j.model_name)
+ elif hasattr(j, 'get_target'):
+ model = j.get_target()
+ if model and j._type in ('many2one', 'one2one'):
+ if (not data[i]
+ and not (isinstance(data[i], (int, long))
+ and not isinstance(data[i],
+ type(False)))):
+ data[i] = BrowseRecordNull()
+ else:
_datetime = None
- if hasattr(j, 'datetime_field') and j.datetime_field:
+ if (hasattr(j, 'datetime_field')
+ and j.datetime_field):
_datetime = data[j.datetime_field]
with Transaction().set_context(
_datetime=_datetime):
- data[i] = BrowseRecordList(BrowseRecord(
- x, model, local_cache=self._local_cache)
- for x in data[i])
- if isinstance(j, fields.Function):
- self._local_data.setdefault(data['id'], {})[i] = data[i]
- del data[i]
- self._data[data['id']].update(data)
- if name in self._local_data[self._id]:
- return self._local_data[self._id][name]
- return self._data[self._id][name]
+ ids = model2ids.setdefault(model, [])
+ ids.append(data[i])
+ local_cache = model2cache.setdefault(model,
+ LRUDict(RECORD_CACHE_SIZE))
+ data[i] = BrowseRecord(data[i], model,
+ ids, local_cache)
+ elif (model
+ and j._type in ('one2many', 'many2many')
+ and len(data[i])):
+ _datetime = None
+ if hasattr(j, 'datetime_field') and j.datetime_field:
+ _datetime = data[j.datetime_field]
+ with Transaction().set_context(
+ _datetime=_datetime):
+ ids = model2ids.setdefault(model, [])
+ ids.extend(data[i])
+ local_cache = model2cache.setdefault(model,
+ LRUDict(RECORD_CACHE_SIZE))
+ data[i] = BrowseRecordList(
+ BrowseRecord(x, model, ids, local_cache)
+ for x in data[i])
+ if isinstance(j, fields.Function):
+ if data['id'] == self.id and i == name:
+ result = data[i]
+ self._local_data.setdefault(data['id'], {})[i] = data[i]
+ del data[i]
+ self._data.setdefault(data['id'], {}).update(data)
+ if data['id'] == self.id and name in data:
+ result = data[name]
+ return result
def __getattr__(self, name):
# TODO raise an AttributeError exception
@@ -218,16 +234,16 @@ class BrowseRecord(object):
return name in self
def __int__(self):
- return self._id
+ return self.id
def __str__(self):
- return "BrowseRecord(%s, %d)" % (self._model_name, self._id)
+ return "BrowseRecord(%s, %d)" % (self._model_name, self.id)
def __eq__(self, other):
- return (self._model_name, self._id) == (other._model_name, other._id)
+ return (self._model_name, self.id) == (other._model_name, other.id)
def __ne__(self, other):
- return (self._model_name, self._id) != (other._model_name, other._id)
+ return (self._model_name, self.id) != (other._model_name, other.id)
# we need to define __unicode__ even though we've already defined __str__
# because we have overridden __getattr__
@@ -235,7 +251,7 @@ class BrowseRecord(object):
return unicode(str(self))
def __hash__(self):
- return hash((self._model_name, self._id))
+ return hash((self._model_name, self.id))
def __nonzero__(self):
return True
@@ -246,22 +262,23 @@ class BrowseRecord(object):
self._context = self._context.copy()
prev_lang = self._context.get('language') or 'en_US'
self._context['language'] = lang
- for cache in (self._cache, self._local_cache):
- language_cache = cache['_language_cache']
+ for cache in (self._cache, self._local_data):
+ language_cache = cache.setdefault('_language_cache', {})
for model in cache:
if model == '_language_cache':
continue
for record_id in cache[model]:
language_cache.setdefault(prev_lang,
- {}).setdefault(model, {})[record_id] = \
- cache[model][record_id]
+ LRUDict(MODEL_CACHE_SIZE)).setdefault(model,
+ LRUDict(RECORD_CACHE_SIZE))[record_id] = \
+ cache[model][record_id]
if lang in language_cache \
and model in language_cache[lang] \
and record_id in language_cache[lang][model]:
cache[model][record_id] = \
language_cache[lang][model][record_id]
else:
- cache[model][record_id] = {'id': record_id}
+ cache[model][record_id] = {}
def get_eval(self, name):
res = self[name]
@@ -280,6 +297,7 @@ class EvalEnvironment(dict):
super(EvalEnvironment, self).__init__()
self._record = record
self._model = model
+ self._pool = Pool()
def __getitem__(self, item):
if item.startswith('_parent_'):
@@ -288,7 +306,7 @@ class EvalEnvironment(dict):
model_name = self._model._columns[field].model_name
else:
model_name = self._model._inherit_fields[field][2].model_name
- model = self._model.pool.get(model_name)
+ model = self._pool.get(model_name)
return EvalEnvironment(self._record[field], model)
if item in self._model._columns \
or item in self._model._inherit_fields:
diff --git a/trytond/model/fields/many2many.py b/trytond/model/fields/many2many.py
index 9a8769bfa3..64fc0182b9 100644
--- a/trytond/model/fields/many2many.py
+++ b/trytond/model/fields/many2many.py
@@ -3,6 +3,7 @@
from itertools import chain
from trytond.model.fields.field import Field
from trytond.transaction import Transaction
+from trytond.pool import Pool
class Many2Many(Field):
@@ -63,7 +64,12 @@ class Many2Many(Field):
for i in ids:
res[i] = []
- relation_obj = model.pool.get(self.relation_name)
+ if self.order is None:
+ order = [(self.target, 'ASC')]
+ else:
+ order = self.order
+
+ relation_obj = Pool().get(self.relation_name)
relation_ids = []
for i in range(0, len(ids), Transaction().cursor.IN_MAX):
@@ -71,12 +77,11 @@ class Many2Many(Field):
relation_ids.append(relation_obj.search([
(self.origin, 'in', sub_ids),
(self.target + '.id', '!=', False),
- ], order=self.order))
+ ], order=order))
relation_ids = list(chain(*relation_ids))
- for relation in relation_obj.read(relation_ids,
- [self.origin, self.target]):
- res[relation[self.origin]].append(relation[self.target])
+ for relation in relation_obj.browse(relation_ids):
+ res[relation[self.origin].id].append(relation[self.target].id)
return res
def set(self, ids, model, name, values):
@@ -97,8 +102,8 @@ class Many2Many(Field):
'''
if not values:
return
- relation_obj = model.pool.get(self.relation_name)
- target_obj = self.get_target(model.pool)
+ relation_obj = Pool().get(self.relation_name)
+ target_obj = self.get_target()
for act in values:
if act[0] == 'create':
for record_id in ids:
@@ -173,20 +178,19 @@ class Many2Many(Field):
else:
raise Exception('Bad arguments')
- def get_target(self, pool):
+ def get_target(self):
'''
Return the target model.
- :param pool: The pool
:return: A Model
'''
- relation_obj = pool.get(self.relation_name)
+ relation_obj = Pool().get(self.relation_name)
if not self.target:
return relation_obj
if self.target in relation_obj._columns:
- target_obj = pool.get(
+ target_obj = Pool().get(
relation_obj._columns[self.target].model_name)
else:
- target_obj = pool.get(
+ target_obj = Pool().get(
relation_obj._inherit_fields[self.target][2].model_name)
return target_obj
diff --git a/trytond/model/fields/one2many.py b/trytond/model/fields/one2many.py
index a02a4f9da3..9f4a72b33b 100644
--- a/trytond/model/fields/one2many.py
+++ b/trytond/model/fields/one2many.py
@@ -3,6 +3,7 @@
from itertools import chain
from trytond.model.fields.field import Field
from trytond.transaction import Transaction
+from trytond.pool import Pool
def add_remove_validate(value):
if value:
@@ -70,13 +71,14 @@ class One2Many(Field):
:param values: a dictionary with the read values
:return: a dictionary with ids as key and values as value
'''
+ pool = Pool()
res = {}
for i in ids:
res[i] = []
ids2 = []
for i in range(0, len(ids), Transaction().cursor.IN_MAX):
sub_ids = ids[i:i + Transaction().cursor.IN_MAX]
- ids2.append(model.pool.get(self.model_name).search([
+ ids2.append(pool.get(self.model_name).search([
(self.field, 'in', sub_ids),
], order=self.order))
@@ -91,12 +93,12 @@ class One2Many(Field):
ids3.append(i)
if ids3:
- for i in model.pool.get(self.model_name).read(ids3, [self.field]):
+ for i in pool.get(self.model_name).read(ids3, [self.field]):
res[i[self.field]].append(i['id'])
index_of_ids2 = dict((i, index) for index, i in enumerate(chain(*ids2)))
for val in res.values():
- val.sort(lambda x, y: cmp(index_of_ids2[x], index_of_ids2[y]))
+ val.sort(key=lambda x: index_of_ids2[x])
return res
def set(self, ids, model, name, values):
@@ -116,9 +118,10 @@ class One2Many(Field):
(``unlink_all``),
(``set``, ````)
'''
+ pool = Pool()
if not values:
return
- model = model.pool.get(self.model_name)
+ model = pool.get(self.model_name)
for act in values:
if act[0] == 'create':
for record_id in ids:
diff --git a/trytond/model/fields/one2one.py b/trytond/model/fields/one2one.py
index d3ad640e68..6e0133fb7d 100644
--- a/trytond/model/fields/one2one.py
+++ b/trytond/model/fields/one2one.py
@@ -3,6 +3,7 @@
from trytond.model.fields.many2many import Many2Many
from trytond.transaction import Transaction
+from trytond.pool import Pool
class One2One(Many2Many):
@@ -35,7 +36,8 @@ class One2One(Many2Many):
:param name: A string with the name of the field
:param value: The id to link
'''
- relation_obj = model.pool.get(self.relation_name)
+ pool = Pool()
+ relation_obj = pool.get(self.relation_name)
relation_ids = relation_obj.search([
(self.origin, 'in', ids),
])
diff --git a/trytond/model/fields/property.py b/trytond/model/fields/property.py
index a566c61295..cb493c3e08 100644
--- a/trytond/model/fields/property.py
+++ b/trytond/model/fields/property.py
@@ -6,6 +6,7 @@ from trytond.model.fields.function import Function
from trytond.model.fields.field import Field
from trytond import backend
from trytond.transaction import Transaction
+from trytond.pool import Pool
class Property(Function):
@@ -34,7 +35,8 @@ class Property(Function):
:param values:
:return: a dictionary with ids as key and values as value
'''
- property_obj = model.pool.get('ir.property')
+ pool = Pool()
+ property_obj = pool.get('ir.property')
res = property_obj.get(name, model._name, ids)
return res
@@ -48,7 +50,8 @@ class Property(Function):
:param name: The name of the field.
:param value: The value to set.
'''
- property_obj = model.pool.get('ir.property')
+ pool = Pool()
+ property_obj = pool.get('ir.property')
return property_obj.set(name, model._name, ids,
(value and getattr(self, 'model_name', '') + ',' + str(value)) or
False)
@@ -60,10 +63,11 @@ class Property(Function):
:param clause: The search domain clause. See ModelStorage.search
:return: New list of domain.
'''
- rule_obj = model.pool.get('ir.rule')
- property_obj = model.pool.get('ir.property')
- model_obj = model.pool.get('ir.model')
- field_obj = model.pool.get('ir.model.field')
+ pool = Pool()
+ rule_obj = pool.get('ir.rule')
+ property_obj = pool.get('ir.property')
+ model_obj = pool.get('ir.model')
+ field_obj = pool.get('ir.model.field')
cursor = Transaction().cursor
field_class = backend.FIELDS[self._type]
diff --git a/trytond/model/fields/reference.py b/trytond/model/fields/reference.py
index db66492985..c185829627 100644
--- a/trytond/model/fields/reference.py
+++ b/trytond/model/fields/reference.py
@@ -1,10 +1,10 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
-from __future__ import with_statement
import contextlib
from trytond.model.fields.field import Field
from trytond.tools import safe_eval
from trytond.transaction import Transaction
+from trytond.pool import Pool
class Reference(Field):
@@ -42,6 +42,7 @@ class Reference(Field):
:param values: a dictionary with the read values
:return: a dictionary with ids as key and values as value
'''
+ pool = Pool()
if values is None:
values = {}
res = {}
@@ -76,10 +77,10 @@ class Reference(Field):
with contextlib.nested(Transaction().set_context(active_test=False),
Transaction().set_user(0)):
for ref_model, (ref_ids, ids) in ref_to_check.iteritems():
- if ref_model not in model.pool.object_name_list():
+ if ref_model not in pool.object_name_list():
res.update(dict((i, False) for i in ids))
continue
- ref_obj = model.pool.get(ref_model)
+ ref_obj = pool.get(ref_model)
ref_ids = ref_obj.search([
('id', 'in', list(ref_ids)),
], order=[])
diff --git a/trytond/model/model.py b/trytond/model/model.py
index 716752a003..d9415a832b 100644
--- a/trytond/model/model.py
+++ b/trytond/model/model.py
@@ -2,6 +2,7 @@
#this repository contains the full copyright notices and license terms.
import copy
+import collections
from trytond.model import fields
from trytond.error import WarningErrorMixin
from trytond.pool import Pool
@@ -16,7 +17,6 @@ class Model(WarningErrorMixin):
_name = None
_inherits = {}
_description = ''
- pool = None #XXX change to avoid collision with field
__columns = None
__xxx2many_targets = None
__defaults = None
@@ -54,7 +54,7 @@ class Model(WarningErrorMixin):
fields_names += self._inherit_fields.keys()
for field_name in fields_names:
default_method = getattr(self, 'default_%s' % field_name, False)
- if callable(default_method):
+ if isinstance(default_method, collections.Callable):
res[field_name] = default_method
self.__defaults = res
return res
@@ -98,16 +98,19 @@ class Model(WarningErrorMixin):
for attribute in ('on_change', 'on_change_with', 'autocomplete'):
function_name = '%s_%s' % (attribute, field_name)
if (getattr(field, attribute, False)
- and callable(getattr(self, function_name, False))):
+ and isinstance(getattr(self, function_name, False),
+ collections.Callable)):
self._rpc.setdefault(function_name, False)
def __getattr__(self, name):
+ pool = Pool()
# Search if a function exists in inherits parents
for model_name, field_name in self._inherits.iteritems():
- model_obj = self.pool.get(model_name)
- if hasattr(model_obj, name) and \
- callable(getattr(model_obj, name)):
- return getattr(model_obj, name)
+ model_obj = pool.get(model_name)
+ if (hasattr(model_obj, name)
+ and isinstance(getattr(model_obj, name),
+ collections.Callable)):
+ return getattr(model_obj, name)
raise AttributeError(name)
def _inherits_reload(self):
@@ -115,19 +118,20 @@ class Model(WarningErrorMixin):
Reconstruct _inherit_fields
"""
res = {}
+ pool = Pool()
for model in self._inherits:
- res.update(self.pool.get(model)._inherit_fields)
- for field_name in self.pool.get(model)._columns.keys():
+ res.update(pool.get(model)._inherit_fields)
+ for field_name in pool.get(model)._columns.keys():
res[field_name] = (model, self._inherits[model],
- self.pool.get(model)._columns[field_name])
- for field_name in self.pool.get(model)._inherit_fields.keys():
+ pool.get(model)._columns[field_name])
+ for field_name in pool.get(model)._inherit_fields.keys():
res[field_name] = (model, self._inherits[model],
- self.pool.get(model)._inherit_fields[field_name][2])
+ pool.get(model)._inherit_fields[field_name][2])
self._inherit_fields = res
self._reset_xxx2many_targets()
# Update objects that uses this one to update their _inherits fields
- for obj_name in self.pool.object_name_list():
- obj = self.pool.get(obj_name)
+ for obj_name in pool.object_name_list():
+ obj = pool.get(obj_name)
if self._name in obj._inherits:
obj._inherits_reload()
@@ -148,7 +152,7 @@ class Model(WarningErrorMixin):
continue
if hasattr(field, 'get_target'):
try:
- model_name = field.get_target(self.pool)._name
+ model_name = field.get_target()._name
except KeyError:
to_cache = False
continue
@@ -166,7 +170,7 @@ class Model(WarningErrorMixin):
continue
if hasattr(field, 'get_target'):
try:
- model_name = field.get_target(self.pool)._name
+ model_name = field.get_target()._name
except KeyError:
to_cache = False
continue
@@ -186,7 +190,7 @@ class Model(WarningErrorMixin):
:param module_name: the module name
"""
- translation_obj = self.pool.get('ir.translation')
+ translation_obj = Pool().get('ir.translation')
cursor = Transaction().cursor
# Add model in ir_model
@@ -388,7 +392,8 @@ class Model(WarningErrorMixin):
:return: a dictionary with field name as key
and default value as value
'''
- property_obj = self.pool.get('ir.property')
+ pool = Pool()
+ property_obj = pool.get('ir.property')
value = {}
# get the default values defined in the object
@@ -407,7 +412,7 @@ class Model(WarningErrorMixin):
if (with_rec_name
and field._type in ('many2one',)
and value.get(field_name)):
- obj = self.pool.get(field.model_name)
+ obj = pool.get(field.model_name)
if 'rec_name' in obj._columns:
value[field_name + '.rec_name'] = obj.browse(
value[field_name]).rec_name
@@ -427,10 +432,11 @@ class Model(WarningErrorMixin):
:param value: a dictionnary with the default value
:return: a new dictionnary of default value
"""
+ pool = Pool()
res = value.copy()
val = {}
for i in self._inherits.keys():
- val.update(self.pool.get(i)._default_on_change(value))
+ val.update(pool.get(i)._default_on_change(value))
for field in value.keys():
if field in self._columns:
if self._columns[field].on_change:
@@ -443,7 +449,7 @@ class Model(WarningErrorMixin):
args[arg] = args[arg][0]
val.update(getattr(self, 'on_change_' + field)(args))
if self._columns[field]._type in ('one2many',):
- obj = self.pool.get(self._columns[field].model_name)
+ obj = pool.get(self._columns[field].model_name)
for val2 in res[field]:
val2.update(obj._default_on_change(val2))
res.update(val)
@@ -457,12 +463,13 @@ class Model(WarningErrorMixin):
:return: a dictionary with field name as key and definition as value
"""
res = {}
- translation_obj = self.pool.get('ir.translation')
- model_access_obj = self.pool.get('ir.model.access')
- field_access_obj = self.pool.get('ir.model.field.access')
+ pool = Pool()
+ translation_obj = pool.get('ir.translation')
+ model_access_obj = pool.get('ir.model.access')
+ field_access_obj = pool.get('ir.model.field.access')
for parent in self._inherits:
- res.update(self.pool.get(parent).fields_get(fields_names))
+ res.update(pool.get(parent).fields_get(fields_names))
write_access = model_access_obj.check(self._name, 'write',
raise_exception=False)
@@ -576,8 +583,8 @@ class Model(WarningErrorMixin):
if hasattr(self._columns[field], 'model_name'):
relation = copy.copy(self._columns[field].model_name)
else:
- relation = copy.copy(self._columns[field].get_target(
- self.pool)._name)
+ relation = copy.copy(
+ self._columns[field].get_target()._name)
res[field]['relation'] = relation
res[field]['domain'] = copy.copy(self._columns[field].domain)
res[field]['context'] = copy.copy(self._columns[field].context)
diff --git a/trytond/model/modelsql.py b/trytond/model/modelsql.py
index 7c0d204fec..89e7cba646 100644
--- a/trytond/model/modelsql.py
+++ b/trytond/model/modelsql.py
@@ -1,17 +1,20 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
-from __future__ import with_statement
import contextlib
import datetime
import re
+from functools import reduce
from decimal import Decimal
+from itertools import islice
from trytond.model import ModelStorage
from trytond.model import fields
from trytond.backend import FIELDS, TableHandler
from trytond.backend import DatabaseIntegrityError, Database
from trytond.tools import reduce_ids
-from trytond.const import OPERATORS
+from trytond.const import OPERATORS, RECORD_CACHE_SIZE
from trytond.transaction import Transaction
+from trytond.pool import Pool
+from trytond.cache import LRUDict
_RE_UNIQUE = re.compile('UNIQUE\s*\((.*)\)', re.I)
_RE_CHECK = re.compile('CHECK\s*\((.*)\)', re.I)
@@ -43,6 +46,8 @@ class ModelSQL(ModelStorage):
if self.table_query():
return
+ pool = Pool()
+
# create/update table in the database
table = TableHandler(Transaction().cursor, self, module_name)
if self._history:
@@ -133,7 +138,7 @@ class ModelSQL(ModelStorage):
if field.model_name in ('res.user', 'res.group'):
ref = field.model_name.replace('.','_')
else:
- ref = self.pool.get(field.model_name)._table
+ ref = pool.get(field.model_name)._table
table.add_fk(field_name, ref, field.ondelete)
table.index_action(
@@ -202,10 +207,11 @@ class ModelSQL(ModelStorage):
'''
Return the default value for sequence field.
'''
+ pool = Pool()
table = self._table
if 'sequence' not in self._columns:
for model in self._inherits:
- model_obj = self.pool.get(model)
+ model_obj = pool.get(model)
if 'sequence' in model_obj._columns:
table = model_obj._table
break
@@ -229,6 +235,7 @@ class ModelSQL(ModelStorage):
def create(self, values):
super(ModelSQL, self).create(values)
cursor = Transaction().cursor
+ pool = Pool()
if self.table_query():
return False
@@ -284,11 +291,10 @@ class ModelSQL(ModelStorage):
continue
if inherits in tocreate:
tocreate[inherits][i] = values[i]
- if i not in self._columns:
- del values[i]
+ del values[i]
for inherits in tocreate:
- inherits_obj = self.pool.get(inherits)
+ inherits_obj = pool.get(inherits)
inherits_id = inherits_obj.create(tocreate[inherits])
values[self._inherits[inherits]] = inherits_id
@@ -354,7 +360,7 @@ class ModelSQL(ModelStorage):
field_name))
if isinstance(field, fields.Many2One) \
and values.get(field_name):
- model_obj = self.pool.get(field.model_name)
+ model_obj = pool.get(field.model_name)
create_records = Transaction().create_records.get(
field.model_name, set())
delete_records = Transaction().delete_records.get(
@@ -375,7 +381,7 @@ class ModelSQL(ModelStorage):
self.raise_user_error(error)
raise
- domain1, domain2 = self.pool.get('ir.rule').domain_get(self._name,
+ domain1, domain2 = pool.get('ir.rule').domain_get(self._name,
mode='create')
if domain1:
cursor.execute('SELECT id FROM "' + self._table + '" ' \
@@ -388,7 +394,7 @@ class ModelSQL(ModelStorage):
for field in values:
if getattr(self._columns[field], 'translate', False):
- self.pool.get('ir.translation')._set_ids(
+ pool.get('ir.translation')._set_ids(
self._name + ',' + field, 'model',
Transaction().language, [id_new], values[field])
@@ -417,8 +423,9 @@ class ModelSQL(ModelStorage):
return id_new
def read(self, ids, fields_names=None):
- rule_obj = self.pool.get('ir.rule')
- translation_obj = self.pool.get('ir.translation')
+ pool = Pool()
+ rule_obj = pool.get('ir.rule')
+ translation_obj = pool.get('ir.translation')
super(ModelSQL, self).read(ids, fields_names=fields_names)
cursor = Transaction().cursor
@@ -561,7 +568,7 @@ class ModelSQL(ModelStorage):
for field_related in fields_related[inherit_field]:
inherit_related_fields.append(
inherit_field + '.' + field_related)
- res2 = self.pool.get(table).read([x[field] for x in res],
+ res2 = pool.get(table).read([x[field] for x in res],
fields_names=inherits_fields + inherit_related_fields)
res3 = {}
@@ -633,9 +640,9 @@ class ModelSQL(ModelStorage):
fields_related2values.setdefault(field, {})
if self._columns[field]._type in ('many2one', 'one2one'):
if hasattr(self._columns[field], 'model_name'):
- obj = self.pool.get(self._columns[field].model_name)
+ obj = pool.get(self._columns[field].model_name)
else:
- obj = self._columns[field].get_target(self.pool)
+ obj = self._columns[field].get_target()
if hasattr(self._columns[field], 'datetime_field') \
and self._columns[field].datetime_field:
for record in res:
@@ -669,7 +676,7 @@ class ModelSQL(ModelStorage):
record_id = int(record_id)
if not record_id:
continue
- obj = self.pool.get(model_name)
+ obj = pool.get(model_name)
record2 = obj.read(record_id, fields_related[field])
del record2['id']
fields_related2values[field][record_id] = record2
@@ -708,6 +715,8 @@ class ModelSQL(ModelStorage):
def write(self, ids, values):
cursor = Transaction().cursor
+ pool = Pool()
+ translation_obj = pool.get('ir.translation')
# Call before cursor cache cleaning
trigger_eligibles = self.trigger_write_get_eligibles(
@@ -757,7 +766,7 @@ class ModelSQL(ModelStorage):
cursor.execute("SELECT id " \
'FROM "' + self._table + '" ' \
'WHERE ' + ' OR '.join(
- (clause,) * (len(args)/2)), args)
+ (clause,) * (len(args) // 2)), args)
if cursor.fetchone():
raise Exception('ConcurrencyException',
'Records were modified in the meanwhile')
@@ -816,7 +825,7 @@ class ModelSQL(ModelStorage):
upd1.append(Transaction().user)
upd1.append(datetime.datetime.now())
- domain1, domain2 = self.pool.get('ir.rule').domain_get(self._name,
+ domain1, domain2 = pool.get('ir.rule').domain_get(self._name,
mode='write')
if domain1:
domain1 = ' AND (' + domain1 + ') '
@@ -869,7 +878,7 @@ class ModelSQL(ModelStorage):
field_name))
if isinstance(field, fields.Many2One) \
and values[field_name]:
- model_obj = self.pool.get(field.model_name)
+ model_obj = pool.get(field.model_name)
create_records = Transaction().create_records.get(
field.model_name, set())
delete_records = Transaction().delete_records.get(
@@ -892,7 +901,7 @@ class ModelSQL(ModelStorage):
for field in direct:
if getattr(self._columns[field], 'translate', False):
- self.pool.get('ir.translation')._set_ids(
+ translation_obj._set_ids(
self._name + ',' + field, 'model',
Transaction().language, ids, values[field])
@@ -925,7 +934,7 @@ class ModelSQL(ModelStorage):
for val in updend:
if self._inherit_fields[val][0] == table:
values2[val] = values[val]
- self.pool.get(table).write(nids, values2)
+ pool.get(table).write(nids, values2)
self._validate(ids)
@@ -951,6 +960,7 @@ class ModelSQL(ModelStorage):
def delete(self, ids):
cursor = Transaction().cursor
+ pool = Pool()
if not ids:
return True
@@ -1006,7 +1016,7 @@ class ModelSQL(ModelStorage):
foreign_keys_tocheck = []
foreign_keys_toupdate = []
foreign_keys_todelete = []
- for _, model in self.pool.iterobject():
+ for _, model in pool.iterobject():
if hasattr(model, 'table_query') \
and model.table_query():
continue
@@ -1027,7 +1037,7 @@ class ModelSQL(ModelStorage):
Transaction().delete.setdefault(self._name, set()).update(ids)
- domain1, domain2 = self.pool.get('ir.rule').domain_get(self._name,
+ domain1, domain2 = pool.get('ir.rule').domain_get(self._name,
mode='delete')
if domain1:
domain1 = ' AND (' + domain1 + ') '
@@ -1123,7 +1133,8 @@ class ModelSQL(ModelStorage):
def search(self, domain, offset=0, limit=None, order=None, count=False,
query_string=False):
- rule_obj = self.pool.get('ir.rule')
+ pool = Pool()
+ rule_obj = pool.get('ir.rule')
cursor = Transaction().cursor
# Get domain clauses
@@ -1196,11 +1207,11 @@ class ModelSQL(ModelStorage):
datas = cursor.dictfetchmany(cursor.IN_MAX)
cache = cursor.get_cache()
- cache.setdefault(self._name, {})
+ cache.setdefault(self._name, LRUDict(RECORD_CACHE_SIZE))
delete_records = Transaction().delete_records.setdefault(self._name,
set())
keys = None
- for data in datas:
+ for data in islice(datas, 0, cache.size_limit):
if data['id'] in delete_records:
continue
if not keys:
@@ -1215,8 +1226,7 @@ class ModelSQL(ModelStorage):
continue
for k in keys:
del data[k]
- cache[self._name].setdefault(data['id'], {})
- cache[self._name][data['id']].update(data)
+ cache[self._name].setdefault(data['id'], {}).update(data)
if len(datas) >= cursor.IN_MAX:
select_fields2 = [select_fields[0]]
@@ -1284,6 +1294,7 @@ class ModelSQL(ModelStorage):
return qu1, qu2, tables, tables_args
def __search_domain_oper(self, domain, tables, tables_args):
+ pool = Pool()
operator = 'AND'
if len(domain) and isinstance(domain[0], basestring):
if domain[0] not in ('AND', 'OR'):
@@ -1323,6 +1334,7 @@ class ModelSQL(ModelStorage):
return qu1, qu2
def __search_domain_calc(self, domain, tables, tables_args):
+ pool = Pool()
domain = domain[:]
cursor = Transaction().cursor
@@ -1336,7 +1348,7 @@ class ModelSQL(ModelStorage):
table = self
fargs = domain[i][0].split('.', 1)
if fargs[0] in self._inherit_fields:
- itable = self.pool.get(self._inherit_fields[fargs[0]][0])
+ itable = pool.get(self._inherit_fields[fargs[0]][0])
table_query = ''
table_arg = []
if itable.table_query():
@@ -1344,7 +1356,7 @@ class ModelSQL(ModelStorage):
table_query = '(' + table_query + ') AS '
table_join = 'LEFT JOIN ' + table_query + \
'"' + itable._table + '" ON ' \
- '"%s".id = "%s".%s' % (itable._table, self._table,
+ '"%s".id = "%s"."%s"' % (itable._table, self._table,
self._inherits[itable._name])
if table_join not in tables:
tables.append(table_join)
@@ -1354,11 +1366,11 @@ class ModelSQL(ModelStorage):
if not fargs[0] in self._inherit_fields:
raise Exception('ValidateError', 'Field "%s" doesn\'t ' \
'exist on "%s"' % (fargs[0], self._name))
- table = self.pool.get(self._inherit_fields[fargs[0]][0])
+ table = pool.get(self._inherit_fields[fargs[0]][0])
field = table._columns.get(fargs[0], False)
if len(fargs) > 1:
if field._type == 'many2one':
- target_obj = self.pool.get(field.model_name)
+ target_obj = pool.get(field.model_name)
if hasattr(field, 'search'):
domain.extend([(fargs[0], 'in', target_obj.search([
(fargs[1], domain[i][1], domain[i][2]),
@@ -1372,11 +1384,11 @@ class ModelSQL(ModelStorage):
continue
elif field._type in ('one2one', 'many2many', 'one2many'):
if hasattr(field, 'model_name'):
- target_obj = self.pool.get(field.model_name)
+ target_obj = pool.get(field.model_name)
else:
- target_obj = field.get_target(self.pool)
+ target_obj = field.get_target()
if hasattr(field, 'relation_name'):
- relation_obj = self.pool.get(field.relation_name)
+ relation_obj = pool.get(field.relation_name)
origin, target = field.origin, field.target
else:
relation_obj = target_obj
@@ -1403,7 +1415,7 @@ class ModelSQL(ModelStorage):
clause = domain.pop(i)
domain.extend(field.search(table, clause[0], clause))
elif field._type == 'one2many':
- field_obj = self.pool.get(field.model_name)
+ field_obj = pool.get(field.model_name)
if isinstance(domain[i][2], basestring):
# get the ids of the records of the "distant" resource
@@ -1459,9 +1471,9 @@ class ModelSQL(ModelStorage):
elif field._type in ('many2many', 'one2one'):
# XXX must find a solution for long id list
if hasattr(field, 'model_name'):
- target_obj = self.pool.get(field.model_name)
+ target_obj = pool.get(field.model_name)
else:
- target_obj = field.get_target(self.pool)
+ target_obj = field.get_target()
if domain[i][1] in ('child_of', 'not child_of'):
if isinstance(domain[i][2], basestring):
ids2 = [x[0] for x in target_obj.search([
@@ -1489,7 +1501,7 @@ class ModelSQL(ModelStorage):
ids2 = target_obj.search([
(domain[i][3], 'child_of', ids2),
], order=[])
- relation_obj = self.pool.get(field.relation_name)
+ relation_obj = pool.get(field.relation_name)
red_sql, red_ids = reduce_ids('"' + field.target + '"',
ids2)
query1 = 'SELECT "' + field.origin + '" ' \
@@ -1510,7 +1522,7 @@ class ModelSQL(ModelStorage):
table, domain[i][0]))
else:
if isinstance(domain[i][2], bool):
- relation_obj = self.pool.get(field.relation_name)
+ relation_obj = pool.get(field.relation_name)
query1 = 'SELECT "' + field.origin + '" ' \
'FROM "' + relation_obj._table + '" '\
'WHERE "' + field.origin + '" IS NOT NULL'
@@ -1524,7 +1536,7 @@ class ModelSQL(ModelStorage):
target_field = 'rec_name'
else:
target_field = 'id'
- relation_obj = self.pool.get(field.relation_name)
+ relation_obj = pool.get(field.relation_name)
query1, query2 = target_obj.search([
(target_field, domain[i][1], domain[i][2]),
@@ -1539,7 +1551,7 @@ class ModelSQL(ModelStorage):
# XXX must find a solution for long id list
if domain[i][1] in ('child_of', 'not child_of'):
if isinstance(domain[i][2], basestring):
- field_obj = self.pool.get(field.model_name)
+ field_obj = pool.get(field.model_name)
ids2 = [x[0] for x in field_obj.search([
('rec_name', 'like', domain[i][2]),
], order=[])]
@@ -1562,7 +1574,7 @@ class ModelSQL(ModelStorage):
raise Exception('Error', 'Programming error: ' \
'child_of on field "%s" is not allowed!' % \
(domain[i][0],))
- ids2 = self.pool.get(field.model_name).search([
+ ids2 = pool.get(field.model_name).search([
(domain[i][3], 'child_of', ids2),
], order=[])
if domain[i][1] == 'child_of':
@@ -1599,9 +1611,9 @@ class ModelSQL(ModelStorage):
ids2, table, domain[i][0]), table)
else:
if isinstance(domain[i][2], basestring):
- field_obj = self.pool.get(field.model_name)
+ field_obj = pool.get(field.model_name)
res_ids = field_obj.search([
- (field_obj._rec_name, domain[i][1], domain[i][2]),
+ ('rec_name', domain[i][1], domain[i][2]),
], order=[])
domain[i] = (domain[i][0], 'in', res_ids, table)
else:
@@ -1652,7 +1664,7 @@ class ModelSQL(ModelStorage):
table_query, table_args = table.table_query()
table_query = '(' + table_query + ') AS '
- translation_obj = self.pool.get('ir.translation')
+ translation_obj = pool.get('ir.translation')
qu1, qu2, tables, table_args = \
translation_obj.search_domain([
@@ -1803,6 +1815,7 @@ class ModelSQL(ModelStorage):
return qu1, qu2
def _order_calc(self, field, otype):
+ pool = Pool()
order_by = []
tables = []
tables_args = {}
@@ -1820,7 +1833,7 @@ class ModelSQL(ModelStorage):
field_name = self._columns[field].order_field
if isinstance(self._columns[field], fields.Many2One):
- obj = self.pool.get(self._columns[field].model_name)
+ obj = pool.get(self._columns[field].model_name)
table_name = obj._table
link_field = field
field_name = None
@@ -1836,7 +1849,7 @@ class ModelSQL(ModelStorage):
otype)
table_join = 'LEFT JOIN "' + table_name + '" AS ' \
'"' + table_name + '.' + link_field + '" ON ' \
- '"%s.%s".id = "%s".%s' % (table_name, link_field,
+ '"%s.%s".id = "%s"."%s"' % (table_name, link_field,
self._table, link_field)
for i in range(len(order_by)):
if table_name in order_by[i]:
@@ -1855,12 +1868,12 @@ class ModelSQL(ModelStorage):
obj2 = None
if obj._rec_name in obj._inherit_fields.keys():
- obj2 = self.pool.get(
+ obj2 = pool.get(
obj._inherit_fields[obj._rec_name][0])
field_name = obj._rec_name
if obj._order_name in obj._inherit_fields.keys():
- obj2 = self.pool.get(
+ obj2 = pool.get(
obj._inherit_fields[obj._order_name][0])
field_name = obj._order_name
@@ -1872,7 +1885,7 @@ class ModelSQL(ModelStorage):
table_join = 'LEFT JOIN "' + table_name + '" AS ' \
'"' + table_name + '.' + link_field + '" ON ' \
- '"%s.%s".id = "%s".%s' % \
+ '"%s.%s".id = "%s"."%s"' % \
(table_name, link_field, self._table, link_field)
for i in range(len(order_by)):
if table_name in order_by[i]:
@@ -1891,7 +1904,7 @@ class ModelSQL(ModelStorage):
table_join2 = 'LEFT JOIN "' + table_name2 + '" AS ' \
'"' + table_name2 + '.' + link_field2 + '" ON ' \
- '"%s.%s".id = "%s.%s".%s' % \
+ '"%s.%s".id = "%s.%s"."%s"' % \
(table_name2, link_field2, table_name, link_field,
link_field2)
for i in range(len(order_by)):
@@ -1994,18 +2007,19 @@ class ModelSQL(ModelStorage):
return order_by, tables, tables_args
if field in self._inherit_fields.keys():
- obj = self.pool.get(self._inherit_fields[field][0])
+ obj = pool.get(self._inherit_fields[field][0])
table_name = obj._table
link_field = self._inherits[obj._name]
order_by, tables, tables_args = obj._order_calc(field, otype)
table_join = 'LEFT JOIN "' + table_name + '" ON ' \
- '"%s".id = "%s".%s' % \
+ '"%s".id = "%s"."%s"' % \
(table_name, self._table, link_field)
if table_join not in tables:
tables.insert(0, table_join)
return order_by, tables, tables_args
- raise Exception('Error', 'Wrong field name (%s) in order!' % field)
+ raise Exception('Error', 'Wrong field name (%s) for %s in order!' %
+ (field, self._name))
def _rebuild_tree(self, parent, parent_id, left):
'''
diff --git a/trytond/model/modelstorage.py b/trytond/model/modelstorage.py
index b21b40df1d..43039a2d3e 100644
--- a/trytond/model/modelstorage.py
+++ b/trytond/model/modelstorage.py
@@ -1,7 +1,6 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
-from __future__ import with_statement
import datetime
import time
from decimal import Decimal
@@ -12,6 +11,9 @@ try:
except ImportError:
import StringIO
import csv
+from functools import reduce
+import traceback
+import sys
from trytond.model import Model
from trytond.model import fields
from trytond.model.browse import BrowseRecordList, BrowseRecord, \
@@ -19,8 +21,10 @@ from trytond.model.browse import BrowseRecordList, BrowseRecord, \
from trytond.model.browse import EvalEnvironment
from trytond.tools import safe_eval, reduce_domain
from trytond.pyson import PYSONEncoder, PYSONDecoder, PYSON
-from trytond.const import OPERATORS
+from trytond.const import OPERATORS, RECORD_CACHE_SIZE
from trytond.transaction import Transaction
+from trytond.pool import Pool
+from trytond.cache import LRUDict
class ModelStorage(Model):
@@ -62,7 +66,7 @@ class ModelStorage(Model):
def __clean_xxx2many_cache(self):
# Clean cursor cache
to_clean = [(model._name, field_name)
- for model_name, model in self.pool.iterobject(type='model')
+ for model_name, model in Pool().iterobject(type='model')
for field_name, target_name in model._xxx2many_targets
if target_name == self._name]
for cache in Transaction().cursor.cache.values():
@@ -81,8 +85,9 @@ class ModelStorage(Model):
and created values as value
:return: the id of the created record
'''
- model_access_obj = self.pool.get('ir.model.access')
- model_field_access_obj = self.pool.get('ir.model.field.access')
+ pool = Pool()
+ model_access_obj = pool.get('ir.model.access')
+ model_field_access_obj = pool.get('ir.model.field.access')
model_access_obj.check(self._name, 'create')
model_field_access_obj.check(self._name,
@@ -96,7 +101,7 @@ class ModelStorage(Model):
:param id: the created id
'''
- trigger_obj = self.pool.get('ir.trigger')
+ trigger_obj = Pool().get('ir.trigger')
trigger_ids = trigger_obj.get_triggers(self._name, 'create')
if not trigger_ids:
return
@@ -116,8 +121,9 @@ class ModelStorage(Model):
the dictionnaries will have fields names as key
and fields value as value. The list will not be in the same order.
'''
- model_access_obj = self.pool.get('ir.model.access')
- model_field_access_obj = self.pool.get('ir.model.field.access')
+ pool = Pool()
+ model_access_obj = pool.get('ir.model.access')
+ model_field_access_obj = pool.get('ir.model.field.access')
model_access_obj.check(self._name, 'read')
model_field_access_obj.check(self._name,
@@ -135,8 +141,9 @@ class ModelStorage(Model):
and written values as value
:return: True if succeed
'''
- model_access_obj = self.pool.get('ir.model.access')
- model_field_access_obj = self.pool.get('ir.model.field.access')
+ pool = Pool()
+ model_access_obj = pool.get('ir.model.access')
+ model_field_access_obj = pool.get('ir.model.field.access')
model_access_obj.check(self._name, 'write')
model_field_access_obj.check(self._name,
@@ -165,7 +172,7 @@ class ModelStorage(Model):
:param ids: a list of ids
:return: a dictionary of the lists of eligible ids by triggers
'''
- trigger_obj = self.pool.get('ir.trigger')
+ trigger_obj = Pool().get('ir.trigger')
trigger_ids = trigger_obj.get_triggers(self._name, 'write')
if not trigger_ids:
return {}
@@ -185,11 +192,11 @@ class ModelStorage(Model):
:param eligibles: a dictionary of the lists of eligible ids by triggers
'''
- trigger_obj = self.pool.get('ir.trigger')
+ trigger_obj = Pool().get('ir.trigger')
trigger_ids = eligibles.keys()
if not trigger_ids:
return
- records = self.browse(chain(*eligibles.values()))
+ records = self.browse(list(chain(*eligibles.values())))
id2record = dict((x.id, x) for x in records)
triggers = trigger_obj.browse(trigger_ids)
for trigger in triggers:
@@ -208,7 +215,7 @@ class ModelStorage(Model):
:param ids: a list of ids or an id
:return: True if succeed
'''
- model_access_obj = self.pool.get('ir.model.access')
+ model_access_obj = Pool().get('ir.model.access')
model_access_obj.check(self._name, 'delete')
if not self.check_xml_record(ids, None):
@@ -234,7 +241,7 @@ class ModelStorage(Model):
:param ids: the deleted ids
'''
- trigger_obj = self.pool.get('ir.trigger')
+ trigger_obj = Pool().get('ir.trigger')
trigger_ids = trigger_obj.get_triggers(self._name, 'delete')
if not trigger_ids:
return
@@ -257,7 +264,8 @@ class ModelStorage(Model):
new value for the field as value
:return: a list of new ids or the new id
'''
- lang_obj = self.pool.get('ir.lang')
+ pool = Pool()
+ lang_obj = pool.get('ir.lang')
if default is None:
default = {}
@@ -314,7 +322,7 @@ class ModelStorage(Model):
data, data_o2m = convert_data(field_defs, data)
new_ids[data_id] = self.create(data)
for field_name in data_o2m:
- relation_model = self.pool.get(
+ relation_model = pool.get(
field_defs[field_name]['relation'])
relation_field = field_defs[field_name]['relation_field']
if relation_field:
@@ -511,13 +519,14 @@ class ModelStorage(Model):
:param ids: a list of ids or an id
:return: a BrowseRecordList or a BrowseRecord
'''
+ local_cache = LRUDict(RECORD_CACHE_SIZE)
if isinstance(ids, (int, long)):
- return BrowseRecord(ids, self)
- local_cache = {}
- return BrowseRecordList((BrowseRecord(x, self, local_cache=local_cache)
+ return BrowseRecord(ids, self, [ids], local_cache)
+ return BrowseRecordList((BrowseRecord(x, self, ids, local_cache)
for x in ids))
def __export_row(self, record, fields_names):
+ pool = Pool()
lines = []
data = ['' for x in range(len(fields_names))]
done = []
@@ -531,7 +540,7 @@ class ModelStorage(Model):
if not isinstance(value, BrowseRecord):
break
field_name = fields_tree[i]
- model_obj = self.pool.get(value._model_name)
+ model_obj = pool.get(value._model_name)
field = model_obj._columns[field_name]
if field.states and 'invisible' in field.states:
pyson_invisible = PYSONEncoder().encode(
@@ -603,6 +612,7 @@ class ModelStorage(Model):
- the exception if failed
- the warning if failed
'''
+ pool = Pool()
def process_lines(self, datas, prefix, fields_def, position=0):
def warn(msgname, *args):
@@ -625,7 +635,7 @@ class ModelStorage(Model):
def get_many2one(relation, value):
if not value:
return False
- relation_obj = self.pool.get(relation)
+ relation_obj = pool.get(relation)
res = relation_obj.search([
('rec_name', '=', value),
], limit=2)
@@ -643,7 +653,7 @@ class ModelStorage(Model):
if not value:
return False
res = []
- relation_obj = self.pool.get(relation)
+ relation_obj = pool.get(relation)
for word in csv.reader(StringIO.StringIO(value), delimiter=',',
quoting=csv.QUOTE_NONE, escapechar='\\').next():
res2 = relation_obj.search([
@@ -670,7 +680,7 @@ class ModelStorage(Model):
except Exception:
warn('reference_syntax_error', value, '/'.join(field))
return False
- relation_obj = self.pool.get(relation)
+ relation_obj = pool.get(relation)
res = relation_obj.search([
('rec_name', '=', value),
], limit=2)
@@ -775,7 +785,7 @@ class ModelStorage(Model):
# Import one2many fields
nbrmax = 1
for field in todo:
- newfd = self.pool.get(fields_def[field]['relation']
+ newfd = pool.get(fields_def[field]['relation']
).fields_get()
res = process_lines(self, datas, prefix + [field], newfd,
position)
@@ -806,7 +816,7 @@ class ModelStorage(Model):
datas.pop(0)
return (row, nbrmax, translate)
- ir_model_data_obj = self.pool.get('ir.model.data')
+ ir_model_data_obj = pool.get('ir.model.data')
# logger for collecting warnings for the client
warnings = logging.Logger("import")
@@ -840,7 +850,9 @@ class ModelStorage(Model):
logger.error(exp)
# XXX should raise Exception
Transaction().cursor.rollback()
- warning = '\n'.join(map(str, exp[1:]) + [warning])
+ tb_s = reduce(lambda x, y: x + y,
+ traceback.format_exception(*sys.exc_info()))
+ warning = '%s\n%s' % (tb_s, warning)
return (-1, res, exp, warning)
done += 1
return (done, 0, 0, 0)
@@ -861,7 +873,7 @@ class ModelStorage(Model):
written values as value
:return: True or False
"""
- model_data_obj = self.pool.get('ir.model.data')
+ model_data_obj = Pool().get('ir.model.data')
# Allow root user to update/delete
if Transaction().user == 0:
return True
@@ -934,7 +946,8 @@ class ModelStorage(Model):
return True
def _get_error_args(self, field_name):
- model_field_obj = self.pool.get('ir.model.field')
+ pool = Pool()
+ model_field_obj = pool.get('ir.model.field')
error_args = (field_name, self._name)
if model_field_obj:
model_field_ids = model_field_obj.search([
@@ -949,6 +962,7 @@ class ModelStorage(Model):
def _validate(self, ids):
+ pool = Pool()
if (Transaction().user == 0
and Transaction().context.get('user')):
with Transaction().set_user(Transaction().context.get('user')):
@@ -958,12 +972,12 @@ class ModelStorage(Model):
if not getattr(self, field[0])(ids):
self.raise_user_error(field[1])
- if not 'res.user' in self.pool.object_name_list() \
+ if not 'res.user' in pool.object_name_list() \
or Transaction().user == 0:
ctx_pref = {
}
else:
- user_obj = self.pool.get('res.user')
+ user_obj = pool.get('res.user')
ctx_pref = user_obj.get_preferences(context_only=True)
def is_pyson(test):
@@ -989,9 +1003,9 @@ class ModelStorage(Model):
('many2one', 'many2many', 'one2many', 'one2one')
and field.domain):
if field._type in ('many2one', 'one2many'):
- relation_obj = self.pool.get(field.model_name)
+ relation_obj = pool.get(field.model_name)
else:
- relation_obj = field.get_target(self.pool)
+ relation_obj = field.get_target()
if is_pyson(field.domain):
pyson_domain = PYSONEncoder().encode(field.domain)
for record in records:
@@ -1110,6 +1124,7 @@ class ModelStorage(Model):
field_name)
def _clean_defaults(self, defaults):
+ pool = Pool()
vals = {}
for field in defaults.keys():
fld_def = (field in self._columns) and self._columns[field] \
@@ -1120,7 +1135,7 @@ class ModelStorage(Model):
else:
vals[field] = defaults[field]
elif fld_def._type in ('one2many',):
- obj = self.pool.get(fld_def.model_name)
+ obj = pool.get(fld_def.model_name)
vals[field] = []
for defaults2 in defaults[field]:
vals2 = obj._clean_defaults(defaults2)
@@ -1139,8 +1154,9 @@ class ModelStorage(Model):
:param ids: a list of id or an id
'''
- trigger_obj = self.pool.get('workflow.trigger')
- instance_obj = self.pool.get('workflow.instance')
+ pool = Pool()
+ trigger_obj = pool.get('workflow.trigger')
+ instance_obj = pool.get('workflow.instance')
if isinstance(ids, (int, long)):
ids = [ids]
diff --git a/trytond/model/modelview.py b/trytond/model/modelview.py
index 0eef68278d..6f976ca230 100644
--- a/trytond/model/modelview.py
+++ b/trytond/model/modelview.py
@@ -12,6 +12,7 @@ from trytond.tools import safe_eval
from trytond.pyson import PYSONEncoder, CONTEXT
from trytond.transaction import Transaction
from trytond.cache import Cache
+from trytond.pool import Pool
def _find(tree, element):
if element.tag == 'xpath':
@@ -112,6 +113,7 @@ class ModelView(Model):
- md5: the check sum of the dictionary without this checksum
'''
result = {'model': self._name}
+ pool = Pool()
test = True
model = True
@@ -149,7 +151,7 @@ class ModelView(Model):
# Check if view is not from an inherited model
if sql_res[5] != self._name:
- inherit_obj = self.pool.get(sql_res[5])
+ inherit_obj = pool.get(sql_res[5])
result['arch'] = inherit_obj.fields_view_get(
result['view_id'])['arch']
view_id = inherit_view_id
@@ -164,9 +166,8 @@ class ModelView(Model):
raise_p = False
while True:
try:
- sql_inherit.sort(lambda x, y: \
- cmp(self._modules_list.index(x[2] or None),
- self._modules_list.index(y[2] or None)))
+ sql_inherit.sort(key=lambda x:
+ self._modules_list.index(x[2] or None))
break
except ValueError:
if raise_p:
@@ -261,7 +262,8 @@ class ModelView(Model):
return value
def _view_look_dom_arch(self, tree, type, field_children=None):
- field_access_obj = self.pool.get('ir.model.field.access')
+ pool = Pool()
+ field_access_obj = pool.get('ir.model.field.access')
fields_width = {}
tree_root = tree.getroottree().getroot()
@@ -278,7 +280,7 @@ class ModelView(Model):
# Find field inherited without read access
for inherit_name in self._inherits:
- inherit_obj = self.pool.get(inherit_name)
+ inherit_obj = pool.get(inherit_name)
fread_accesses = field_access_obj.check(inherit_obj._name,
inherit_obj._columns.keys(), 'read', access=True)
fields_to_remove += list(x for x, y in fread_accesses.iteritems()
@@ -301,7 +303,7 @@ class ModelView(Model):
parent.remove(element)
if type == 'tree':
- viewtreewidth_obj = self.pool.get('ir.ui.view_tree_width')
+ viewtreewidth_obj = pool.get('ir.ui.view_tree_width')
viewtreewidth_ids = viewtreewidth_obj.search([
('model', '=', self._name),
('user', '=', Transaction().user),
@@ -345,7 +347,8 @@ class ModelView(Model):
return arch, fields2
def __view_look_dom(self, element, type, fields_width=None):
- translation_obj = self.pool.get('ir.translation')
+ pool = Pool()
+ translation_obj = pool.get('ir.translation')
if fields_width is None:
fields_width = {}
@@ -366,7 +369,7 @@ class ModelView(Model):
if hasattr(field, 'model_name'):
relation = field.model_name
else:
- relation = field.get_target(self.pool)._name
+ relation = field.get_target()._name
except Exception:
relation = False
if relation and element.tag == 'field':
@@ -396,7 +399,7 @@ class ModelView(Model):
if Transaction().language != 'en_US':
_translate_field(field2)
- relation_obj = self.pool.get(relation)
+ relation_obj = pool.get(relation)
if hasattr(relation_obj, '_view_look_dom_arch'):
xarch, xfields = \
relation_obj._view_look_dom_arch(
diff --git a/trytond/model/modelworkflow.py b/trytond/model/modelworkflow.py
index bcd2d51da0..fb2f2fd815 100644
--- a/trytond/model/modelworkflow.py
+++ b/trytond/model/modelworkflow.py
@@ -1,8 +1,8 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
-from __future__ import with_statement
from trytond.model import ModelStorage
from trytond.transaction import Transaction
+from trytond.pool import Pool
class ModelWorkflow(ModelStorage):
@@ -27,7 +27,7 @@ class ModelWorkflow(ModelStorage):
return res
def delete(self, ids):
- instance_obj = self.pool.get('workflow.instance')
+ instance_obj = Pool().get('workflow.instance')
if isinstance(ids, (int, long)):
ids = [ids]
@@ -49,8 +49,9 @@ class ModelWorkflow(ModelStorage):
:param ids: a list of id or an id
'''
- workflow_obj = self.pool.get('workflow')
- instance_obj = self.pool.get('workflow.instance')
+ pool = Pool()
+ workflow_obj = pool.get('workflow')
+ instance_obj = pool.get('workflow.instance')
if isinstance(ids, (int, long)):
ids = [ids]
@@ -75,7 +76,7 @@ class ModelWorkflow(ModelStorage):
:param ids: a list of id or an id
'''
- instance_obj = self.pool.get('workflow.instance')
+ instance_obj = Pool().get('workflow.instance')
if isinstance(ids, (int, long)):
ids = [ids]
@@ -96,7 +97,7 @@ class ModelWorkflow(ModelStorage):
:param ids: a list of id or an id
'''
- instance_obj = self.pool.get('workflow.instance')
+ instance_obj = Pool().get('workflow.instance')
if isinstance(ids, (int, long)):
ids = [ids]
@@ -119,7 +120,7 @@ class ModelWorkflow(ModelStorage):
'''
if self._name == 'workflow.instance':
return
- instance_obj = self.pool.get('workflow.instance')
+ instance_obj = Pool().get('workflow.instance')
if isinstance(ids, (int, long)):
ids = [ids]
diff --git a/trytond/modules/__init__.py b/trytond/modules/__init__.py
index d3082b0f1b..11ba764384 100644
--- a/trytond/modules/__init__.py
+++ b/trytond/modules/__init__.py
@@ -1,11 +1,13 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
-from __future__ import with_statement
import os, sys, imp
import itertools
import traceback
import logging
import contextlib
+from functools import reduce
+import imp
+import operator
from trytond.backend import Database
import trytond.tools as tools
from trytond.config import CONFIG
@@ -91,7 +93,7 @@ class Node(Singleton):
for attr in ('init', 'update'):
if hasattr(self, attr):
setattr(node, attr, True)
- self.childs.sort(lambda x, y: cmp(x.name, y.name))
+ self.childs.sort(key=operator.attrgetter('name'))
def all_childs(self):
res = []
@@ -334,7 +336,7 @@ def register_classes(reload_p=False):
if i.startswith(module) \
and i != module:
del sys.modules[i]
- reload(sys.modules[module])
+ imp.reload(sys.modules[module])
logger = logging.getLogger('modules')
@@ -352,7 +354,7 @@ def register_classes(reload_p=False):
and i != 'trytond.modules.' + module \
and getattr(sys.modules[i], '_TRYTON_RELOAD', True):
del sys.modules[i]
- reload(sys.modules['trytond.modules.' + module])
+ imp.reload(sys.modules['trytond.modules.' + module])
continue
if os.path.isdir(OPJ(MODULES_PATH, module)):
diff --git a/trytond/pool.py b/trytond/pool.py
index 867b03858f..d19d144b44 100644
--- a/trytond/pool.py
+++ b/trytond/pool.py
@@ -1,8 +1,10 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
-from trytond.modules import load_modules, register_classes
from threading import RLock
import logging
+from trytond.modules import load_modules, register_classes
+from trytond.transaction import Transaction
+import __builtin__
class Pool(object):
@@ -15,32 +17,27 @@ class Pool(object):
_lock = RLock()
_locks = {}
_pool = {}
- _pools = {}
test = False
+ _instances = {}
- def __new__(cls, database_name):
- cls._lock.acquire()
- lock = cls._locks.setdefault(database_name, RLock())
- cls._lock.release()
- lock.acquire()
- if database_name in cls._pools:
- res = cls._pools[database_name]
- lock.release()
- return res
- lock.release()
- return object.__new__(cls)
+ def __new__(cls, database_name=None):
+ if database_name is None:
+ database_name = Transaction().cursor.database_name
+ result = cls._instances.get(database_name)
+ if result:
+ return result
+ lock = cls._locks.get(database_name)
+ if not lock:
+ with cls._lock:
+ lock = cls._locks.setdefault(database_name, RLock())
+ with lock:
+ return cls._instances.setdefault(database_name,
+ super(Pool, cls).__new__(cls))
- def __init__(self, database_name):
+ def __init__(self, database_name=None):
+ if database_name is None:
+ database_name = Transaction().cursor.database_name
self.database_name = database_name
- self._lock.acquire()
- lock = self._locks.setdefault(database_name, RLock())
- self._lock.release()
- lock.acquire()
- self._pool.setdefault(database_name, {})
- for type in self.classes.keys():
- self._pool[database_name].setdefault(type, {})
- self._pools.setdefault(database_name, self)
- lock.release()
@staticmethod
def register(klass, type='model'):
@@ -57,53 +54,47 @@ class Pool(object):
if module:
Pool.classes[type].setdefault(module, []).append(klass)
- @staticmethod
- def start():
+ @classmethod
+ def start(cls):
'''
Start/restart the Pool
'''
- Pool._lock.acquire()
- try:
+ with cls._lock:
reload_p = False
prev_classes = {}
- for type in Pool.classes:
- if Pool.classes[type]:
+ for type in cls.classes:
+ if cls.classes.get(type):
reload_p = True
- prev_classes[type] = Pool.classes[type]
- Pool.classes[type] = {}
+ prev_classes[type] = cls.classes.get(type)
+ cls.classes[type] = {}
try:
register_classes(reload_p=reload_p)
except Exception:
if not reload_p:
raise
for type in prev_classes:
- Pool.classes[type] = prev_classes[type]
- for db_name in Pool.database_list():
- Pool(db_name).init()
- finally:
- Pool._lock.release()
+ cls.classes[type] = prev_classes[type]
+ for db_name in cls.database_list():
+ cls(db_name).init()
- @staticmethod
- def stop(database_name):
+ @classmethod
+ def stop(cls, database_name):
'''
Stop the Pool
'''
- Pool._lock.acquire()
- lock = Pool._locks.setdefault(database_name, RLock())
- Pool._lock.release()
- lock.acquire()
- if database_name in Pool._pool:
- del Pool._pool[database_name]
- if database_name in Pool._pools:
- del Pool._pools[database_name]
- lock.release()
+ with cls._lock:
+ if database_name in cls._instances:
+ del cls._instances[database_name]
+ with cls._locks[database_name]:
+ if database_name in cls._pool:
+ del cls._pool[database_name]
- @staticmethod
- def database_list():
+ @classmethod
+ def database_list(cls):
'''
:return: database list
'''
- return Pool._pool.keys()
+ return cls._pool.keys()
def init(self, update=False, lang=None):
'''
@@ -115,11 +106,8 @@ class Pool(object):
'''
logger = logging.getLogger('pool')
logger.info('init pool for "%s"' % self.database_name)
- self._lock.acquire()
- lock = self._locks.setdefault(self.database_name, RLock())
- self._lock.release()
- lock.acquire()
- try:
+ with self._locks[self.database_name]:
+ self._pool.setdefault(self.database_name, {})
#Clean the _pool before loading modules
for type in self.classes.keys():
self._pool[self.database_name][type] = {}
@@ -127,8 +115,6 @@ class Pool(object):
lang=lang)
if restart:
self.init()
- finally:
- lock.release()
def get(self, name, type='model'):
'''
@@ -142,7 +128,19 @@ class Pool(object):
for type in self.classes.keys():
if name in self._pool[self.database_name][type]:
break
- return self._pool[self.database_name][type][name]
+ try:
+ return self._pool[self.database_name][type][name]
+ except KeyError:
+ if type == 'report':
+ from trytond.report import Report
+ # Keyword argument 'type' conflicts with builtin function
+ cls = __builtin__.type(str(name), (Report,), {})
+ obj = object.__new__(cls)
+ obj._name = name
+ self.add(obj, type)
+ obj.__init__()
+ return obj
+ raise
def add(self, obj, type='model'):
'''
@@ -151,8 +149,8 @@ class Pool(object):
:param obj: the object
:param type: the type
'''
- self._pool[self.database_name][type][obj._name] = obj
- obj.pool = self
+ with self._locks[self.database_name]:
+ self._pool[self.database_name][type][obj._name] = obj
def object_name_list(self, type='model'):
'''
diff --git a/trytond/protocols/datatype.py b/trytond/protocols/datatype.py
index ce2e9a84c9..5c55a19e28 100644
--- a/trytond/protocols/datatype.py
+++ b/trytond/protocols/datatype.py
@@ -55,6 +55,9 @@ class Float(float):
return self.decimal.__eq__(other)
return super(Float, self).__eq__(other)
+ def __hash__(self):
+ return super(Float, self).__hash__()
+
def __floordiv__(self, other, context=None):
if isinstance(other, Decimal):
return self.decimal.__floordiv__(other, context=context)
diff --git a/trytond/protocols/dispatcher.py b/trytond/protocols/dispatcher.py
index 3767b2799d..4a185ddb96 100644
--- a/trytond/protocols/dispatcher.py
+++ b/trytond/protocols/dispatcher.py
@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
-from __future__ import with_statement
import traceback
import logging
import time
diff --git a/trytond/protocols/jsonrpc.py b/trytond/protocols/jsonrpc.py
index 0f0b0f7ed7..8933d6b438 100644
--- a/trytond/protocols/jsonrpc.py
+++ b/trytond/protocols/jsonrpc.py
@@ -22,10 +22,7 @@ import posixpath
import urllib
import datetime
from decimal import Decimal
-if sys.version_info < (2, 6):
- import simplejson as json
-else:
- import json
+import json
def object_hook(dct):
if '__class__' in dct:
@@ -279,8 +276,6 @@ class SimpleThreadedJSONRPCServer(SocketServer.ThreadingMixIn,
timeout = 1
def server_bind(self):
- # Python < 2.6 doesn't handle self.timeout
- self.socket.settimeout(1)
self.socket.setsockopt(socket.SOL_SOCKET,
socket.SO_REUSEADDR, 1)
SimpleJSONRPCServer.server_bind(self)
diff --git a/trytond/protocols/webdav.py b/trytond/protocols/webdav.py
index f55d98ec3c..b0be2a9010 100644
--- a/trytond/protocols/webdav.py
+++ b/trytond/protocols/webdav.py
@@ -81,11 +81,6 @@ class BaseThreadedHTTPServer(SocketServer.ThreadingMixIn,
max_children = CONFIG['max_thread']
def server_bind(self):
- # Python < 2.6 doesn't handle self.timeout
- if sys.version_info < (2, 6):
- # breaks the socket under BSD, see http://bugs.python.org/issue7995
- self.socket.settimeout(1)
-
self.socket.setsockopt(socket.SOL_SOCKET,
socket.SO_REUSEADDR, 1)
BaseHTTPServer.HTTPServer.server_bind(self)
diff --git a/trytond/protocols/xmlrpc.py b/trytond/protocols/xmlrpc.py
index e786f39c80..ded00e6c3d 100644
--- a/trytond/protocols/xmlrpc.py
+++ b/trytond/protocols/xmlrpc.py
@@ -204,8 +204,6 @@ class SimpleThreadedXMLRPCServer(SocketServer.ThreadingMixIn,
timeout = 1
def server_bind(self):
- # Python < 2.6 doesn't handle self.timeout
- self.socket.settimeout(1)
self.socket.setsockopt(socket.SOL_SOCKET,
socket.SO_REUSEADDR, 1)
SimpleXMLRPCServer.SimpleXMLRPCServer.server_bind(self)
diff --git a/trytond/pysocket.py b/trytond/pysocket.py
index 0f160cab35..abbcf7daae 100644
--- a/trytond/pysocket.py
+++ b/trytond/pysocket.py
@@ -12,6 +12,12 @@ try:
except ImportError:
ssl = None
import gzip
+try:
+ import hashlib
+except ImportError:
+ hashlib = None
+ import sha
+import os
DNS_CACHE = {}
MAX_SIZE = 999999999
@@ -32,7 +38,7 @@ def checkfunction(module, klass):
class PySocket:
- def __init__(self, sock=None):
+ def __init__(self, sock=None, fingerprints=None, ca_certs=None):
self.sock = sock
self.host = None
self.hostname = None
@@ -41,6 +47,11 @@ class PySocket:
self.ssl_sock = None
self.connected = False
self.buffer = ''
+ self.fingerprints = fingerprints
+ if ca_certs and os.path.isfile(ca_certs):
+ self.ca_certs = ca_certs
+ else:
+ self.ca_certs = None
def connect(self, host, port=False):
if not port:
@@ -57,7 +68,7 @@ class PySocket:
socket.SOCK_STREAM)
self.sock.settimeout(CONNECT_TIMEOUT)
self.sock.connect((host, int(port)))
- except Exception:
+ except socket.error:
self.sock = None
if self.sock is None:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@@ -72,31 +83,54 @@ class PySocket:
sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
sock.settimeout(CONNECT_TIMEOUT)
sock.connect((host, int(port)))
- except Exception:
+ except socket.error:
sock = None
if sock is None:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(CONNECT_TIMEOUT)
sock.connect((host, int(port)))
if ssl:
- ssl.wrap_socket(sock)
+ ssl_sock = ssl.wrap_socket(sock)
self.ssl = True
elif hasattr(socket, 'ssl'):
- socket.ssl(sock)
+ ssl_sock = socket.ssl(sock)
self.ssl = True
- except Exception:
+ except socket.error:
pass
self.sock.settimeout(TIMEOUT)
+ fingerprint = None
if self.ssl:
if ssl:
- self.ssl_sock = ssl.wrap_socket(self.sock)
+ self.ssl_sock = ssl.wrap_socket(self.sock,
+ ca_certs=self.ca_certs, cert_reqs=(self.ca_certs and
+ ssl.CERT_REQUIRED or ssl.CERT_NONE))
elif hasattr(socket, 'ssl'):
- self.ssl_sock = socket.ssl(self.sock)
+ self.ssl_sock = socket.ssl(self.sock, certfile=self.ca_certs)
+ try:
+ peercert = self.ssl_sock.getpeercert(True)
+ except socket.error:
+ peercert = None
+ def format_hash(value):
+ return reduce(lambda x, y: x + y[1].upper() +
+ ((y[0] % 2 and y[0] + 1 < len(value)) and ':' or ''),
+ enumerate(value), '')
+ if peercert and hashlib:
+ fingerprint = format_hash(hashlib.sha1(peercert).hexdigest())
+ elif peercert:
+ fingerprint = format_hash(sha1.new(peercert).hexdigest())
self.host = host
self.hostname = hostname
self.port = port
self.connected = True
self.buffer = ''
+ if self.fingerprints is not None:
+ key = (self.hostname, str(self.port))
+ if key in self.fingerprints:
+ if self.fingerprints[key] != fingerprint:
+ self.disconnect()
+ raise Exception('BadFingerprint')
+ elif fingerprint:
+ self.fingerprints[key] = fingerprint
def disconnect(self):
try:
@@ -111,10 +145,10 @@ class PySocket:
sock.sock_shutdown(shutdown_value)
else:
sock.shutdown(shutdown_value)
- except Exception:
+ except socket.error:
pass
sock.close()
- except Exception:
+ except socket.error:
pass
self.sock = None
self.ssl = False
@@ -123,20 +157,20 @@ class PySocket:
self.buffer = ''
def reconnect(self):
- if self.host and self.port:
+ if self.hostname and self.port:
self.disconnect()
- self.connect(self.host, self.port)
+ self.connect(self.hostname, self.port)
def send(self, msg, exception=False, traceback=None):
msg = cPickle.dumps([msg, traceback], protocol=2)
gzip_p = False
if len(msg) > GZIP_THRESHOLD:
- buf = StringIO.StringIO()
- output = gzip.GzipFile(mode='wb', fileobj=buf)
+ buffer = StringIO.StringIO()
+ output = gzip.GzipFile(mode='wb', fileobj=buffer)
output.write(msg)
output.close()
- buf.seek(0)
- msg = buf.getvalue()
+ buffer.seek(0)
+ msg = buffer.getvalue()
gzip_p = True
size = len(msg)
msg = str(size) + ' ' + (exception and "1" or "0") \
@@ -148,14 +182,14 @@ class PySocket:
while totalsent < size:
sent = self.ssl_sock.write(msg[totalsent:])
if sent == 0:
- raise RuntimeError, "socket connection broken"
+ raise RuntimeError("socket connection broken")
totalsent = totalsent + sent
else:
self.sock.sendall(msg)
def receive(self):
buf = self.buffer
- buf_list = []
+ L = []
size_remaining = MAX_LENGHT
while size_remaining:
chunk_size = min(size_remaining, 4096)
@@ -164,18 +198,18 @@ class PySocket:
else:
chunk = self.sock.recv(chunk_size)
if chunk == '':
- raise RuntimeError, "socket connection broken"
- buf_list.append(chunk)
+ raise RuntimeError("socket connection broken")
+ L.append(chunk)
size_remaining -= len(chunk)
if ' ' in chunk:
break
if size_remaining < 0:
- raise RuntimeError, "socket connection broken"
- buf += ''.join(buf_list)
+ raise RuntimeError("socket connection broken")
+ buf += ''.join(L)
size, msg = buf.split(' ', 1)
size = int(size)
if size > MAX_SIZE:
- raise RuntimeError, "socket connection broken"
+ raise RuntimeError("socket connection broken")
while len(msg) < 2:
chunk_size = min(size + 2, 4096)
if self.ssl:
@@ -183,11 +217,11 @@ class PySocket:
else:
msg += self.sock.recv(chunk_size)
if msg == '':
- raise RuntimeError, "socket connection broken"
+ raise RuntimeError("socket connection broken")
exception = msg[0] != "0"
gzip_p = msg[1] != "0"
- buf_list = [msg[2:]]
- size_remaining = size - len(buf_list[0])
+ L = [msg[2:]]
+ size_remaining = size - len(L[0])
while size_remaining:
chunk_size = min(size_remaining, 4096)
if self.ssl:
@@ -195,10 +229,10 @@ class PySocket:
else:
chunk = self.sock.recv(chunk_size)
if chunk == '':
- raise RuntimeError, "socket connection broken"
- buf_list.append(chunk)
+ raise RuntimeError("socket connection broken")
+ L.append(chunk)
size_remaining -= len(chunk)
- msg = ''.join(buf_list)
+ msg = ''.join(L)
if len(msg) > size:
self.buffer = msg[size:]
msg = msg[:size]
diff --git a/trytond/pyson.py b/trytond/pyson.py
index b3d375bb30..79a2fa49ae 100644
--- a/trytond/pyson.py
+++ b/trytond/pyson.py
@@ -1,11 +1,9 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
import sys
-if sys.version_info < (2, 6):
- import simplejson as json
-else:
- import json
+import json
import datetime
+from functools import reduce
class PYSON(object):
@@ -405,7 +403,7 @@ class Date(PYSON):
date = date.replace(year=year)
if dct['dM']:
month = date.month + dct['dM']
- year = date.year + month / 12
+ year = date.year + month // 12
month = month % 12
date = date.replace(year=year, month=month)
if dct['dd']:
diff --git a/trytond/report/report.py b/trytond/report/report.py
index 2138023bef..b3988d4c8c 100644
--- a/trytond/report/report.py
+++ b/trytond/report/report.py
@@ -104,7 +104,8 @@ class Report(object):
a boolean to direct print,
the report name
'''
- action_report_obj = self.pool.get('ir.action.report')
+ pool = Pool()
+ action_report_obj = pool.get('ir.action.report')
action_report_ids = action_report_obj.search([
('report_name', '=', self._name)
])
@@ -119,7 +120,8 @@ class Report(object):
action_report.name)
def _get_objects(self, ids, model, datas):
- model_obj = self.pool.get(model)
+ pool = Pool()
+ model_obj = pool.get(model)
return model_obj.browse(ids)
def parse(self, report, objects, datas, localcontext):
@@ -135,8 +137,9 @@ class Report(object):
report type
report
'''
+ pool = Pool()
localcontext['datas'] = datas
- localcontext['user'] = self.pool.get('res.user'
+ localcontext['user'] = pool.get('res.user'
).browse(Transaction().user)
localcontext['formatLang'] = lambda *args, **kargs: \
self.format_lang(*args, **kargs)
@@ -147,7 +150,7 @@ class Report(object):
localcontext['context'] = Transaction().context
translate = TranslateFactory(self._name, Transaction().language,
- self.pool.get('ir.translation'))
+ pool.get('ir.translation'))
localcontext['setLang'] = lambda language: translate.set_language(language)
if not report.report_content:
@@ -297,7 +300,8 @@ class Report(object):
def format_lang(self, value, lang, digits=2, grouping=True, monetary=False,
date=False, currency=None, symbol=True):
- lang_obj = self.pool.get('ir.lang')
+ pool = Pool()
+ lang_obj = pool.get('ir.lang')
if date:
if lang:
diff --git a/trytond/res/group.py b/trytond/res/group.py
index d5407f1509..a38a5a3edd 100644
--- a/trytond/res/group.py
+++ b/trytond/res/group.py
@@ -4,12 +4,13 @@
from itertools import chain
from trytond.model import ModelView, ModelSQL, fields
from trytond.transaction import Transaction
+from trytond.pool import Pool
class MenuMany2Many(fields.Many2Many):
def get(self, ids, model, name, values=None):
- menu_obj = self.get_target(model.pool)
+ menu_obj = self.get_target()
res = super(MenuMany2Many, self).get(ids, model, name,
values=values)
menu_ids = list(set(chain(*res.values())))
@@ -36,10 +37,10 @@ class Group(ModelSQL, ModelView):
field_access = fields.One2Many('ir.model.field.access', 'group',
'Access Field')
rule_groups = fields.Many2Many('ir.rule.group-res.group',
- 'group_id', 'rule_group_id', 'Rules',
+ 'group', 'rule_group', 'Rules',
domain=[('global_p', '!=', True), ('default_p', '!=', True)])
menu_access = MenuMany2Many('ir.ui.menu-res.group',
- 'gid', 'menu_id', 'Access Menu')
+ 'group', 'menu', 'Access Menu')
def __init__(self):
super(Group, self).__init__()
@@ -72,32 +73,35 @@ class Group(ModelSQL, ModelView):
def create(self, vals):
res = super(Group, self).create(vals)
+ pool = Pool()
# Restart the cache on the domain_get method
- self.pool.get('ir.rule').domain_get.reset()
+ pool.get('ir.rule').domain_get.reset()
# Restart the cache for get_groups
- self.pool.get('res.user').get_groups.reset()
+ pool.get('res.user').get_groups.reset()
# Restart the cache for get_preferences
- self.pool.get('res.user').get_preferences.reset()
+ pool.get('res.user').get_preferences.reset()
return res
def write(self, ids, vals):
res = super(Group, self).write(ids, vals)
+ pool = Pool()
# Restart the cache on the domain_get method
- self.pool.get('ir.rule').domain_get.reset()
+ pool.get('ir.rule').domain_get.reset()
# Restart the cache for get_groups
- self.pool.get('res.user').get_groups.reset()
+ pool.get('res.user').get_groups.reset()
# Restart the cache for get_preferences
- self.pool.get('res.user').get_preferences.reset()
+ pool.get('res.user').get_preferences.reset()
return res
def delete(self, ids):
res = super(Group, self).delete(ids)
+ pool = Pool()
# Restart the cache on the domain_get method
- self.pool.get('ir.rule').domain_get.reset()
+ pool.get('ir.rule').domain_get.reset()
# Restart the cache for get_groups
- self.pool.get('res.user').get_groups.reset()
+ pool.get('res.user').get_groups.reset()
# Restart the cache for get_preferences
- self.pool.get('res.user').get_preferences.reset()
+ pool.get('res.user').get_preferences.reset()
return res
Group()
diff --git a/trytond/res/group.xml b/trytond/res/group.xml
index 6a17c65d96..7994141574 100644
--- a/trytond/res/group.xml
+++ b/trytond/res/group.xml
@@ -16,15 +16,12 @@ this repository contains the full copyright notices and license terms. -->
-
-
-
@@ -60,8 +57,13 @@ this repository contains the full copyright notices and license terms. -->
-
+
+
diff --git a/trytond/res/ir.py b/trytond/res/ir.py
index 3aea3a3087..be83198305 100644
--- a/trytond/res/ir.py
+++ b/trytond/res/ir.py
@@ -3,15 +3,16 @@
from trytond.model import ModelView, ModelSQL, fields
from trytond.backend import TableHandler
from trytond.transaction import Transaction
+from trytond.pool import Pool
class UIMenuGroup(ModelSQL):
"UI Menu - Group"
_name = 'ir.ui.menu-res.group'
_description = __doc__
- menu_id = fields.Many2One('ir.ui.menu', 'Menu', ondelete='CASCADE',
+ menu = fields.Many2One('ir.ui.menu', 'Menu', ondelete='CASCADE',
select=1, required=True)
- gid = fields.Many2One('res.group', 'Group', ondelete='CASCADE',
+ group = fields.Many2One('res.group', 'Group', ondelete='CASCADE',
select=1, required=True)
def init(self, module_name):
@@ -20,24 +21,28 @@ class UIMenuGroup(ModelSQL):
TableHandler.table_rename(cursor, 'ir_ui_menu_group_rel', self._table)
TableHandler.sequence_rename(cursor, 'ir_ui_menu_group_rel_id_seq',
self._table + '_id_seq')
+ # Migration from 2.0 menu_id and gid renamed into menu group
+ table = TableHandler(cursor, self, module_name)
+ table.column_rename('menu_id', 'menu')
+ table.column_rename('gid', 'group')
super(UIMenuGroup, self).init(module_name)
def create(self, vals):
res = super(UIMenuGroup, self).create(vals)
# Restart the cache on the domain_get method
- self.pool.get('ir.rule').domain_get.reset()
+ Pool().get('ir.rule').domain_get.reset()
return res
def write(self, ids, vals):
res = super(UIMenuGroup, self).write(ids, vals)
# Restart the cache on the domain_get method
- self.pool.get('ir.rule').domain_get.reset()
+ Pool().get('ir.rule').domain_get.reset()
return res
def delete(self, ids):
res = super(UIMenuGroup, self).delete(ids)
# Restart the cache on the domain_get method
- self.pool.get('ir.rule').domain_get.reset()
+ Pool().get('ir.rule').domain_get.reset()
return res
UIMenuGroup()
@@ -47,9 +52,9 @@ class ActionGroup(ModelSQL):
"Action - Group"
_name = 'ir.action-res.group'
_description = __doc__
- action_id = fields.Many2One('ir.action', 'Action', ondelete='CASCADE',
+ action = fields.Many2One('ir.action', 'Action', ondelete='CASCADE',
select=1, required=True)
- gid = fields.Many2One('res.group', 'Group', ondelete='CASCADE',
+ group = fields.Many2One('res.group', 'Group', ondelete='CASCADE',
select=1, required=True)
def init(self, module_name):
@@ -58,24 +63,36 @@ class ActionGroup(ModelSQL):
TableHandler.table_rename(cursor, 'ir_action_group_rel', self._table)
TableHandler.sequence_rename(cursor, 'ir_action_group_rel_id_seq',
self._table + '_id_seq')
+ # Migration from 2.0 action_id and gid renamed into action and group
+ table = TableHandler(cursor, self, module_name)
+ table.column_rename('action_id', 'action')
+ table.column_rename('gid', 'group')
super(ActionGroup, self).init(module_name)
def create(self, vals):
+ action_obj = Pool().get('ir.action')
+ if vals.get('action'):
+ vals = vals.copy()
+ vals['action'] = action_obj.get_action_id(vals['action'])
res = super(ActionGroup, self).create(vals)
# Restart the cache on the domain_get method
- self.pool.get('ir.rule').domain_get.reset()
+ Pool().get('ir.rule').domain_get.reset()
return res
def write(self, ids, vals):
+ action_obj = Pool().get('ir.action')
+ if vals.get('action'):
+ vals = vals.copy()
+ vals['action'] = action_obj.get_action_id(vals['action'])
res = super(ActionGroup, self).write(ids, vals)
# Restart the cache on the domain_get method
- self.pool.get('ir.rule').domain_get.reset()
+ Pool().get('ir.rule').domain_get.reset()
return res
def delete(self, ids):
res = super(ActionGroup, self).delete(ids)
# Restart the cache on the domain_get method
- self.pool.get('ir.rule').domain_get.reset()
+ Pool().get('ir.rule').domain_get.reset()
return res
ActionGroup()
@@ -105,9 +122,9 @@ class RuleGroupGroup(ModelSQL):
"Rule Group - Group"
_name = 'ir.rule.group-res.group'
_description = __doc__
- rule_group_id = fields.Many2One('ir.rule.group', 'Rule Group',
+ rule_group = fields.Many2One('ir.rule.group', 'Rule Group',
ondelete='CASCADE', select=1, required=True)
- group_id = fields.Many2One('res.group', 'Group', ondelete='CASCADE',
+ group = fields.Many2One('res.group', 'Group', ondelete='CASCADE',
select=1, required=True)
def init(self, module_name):
@@ -116,6 +133,11 @@ class RuleGroupGroup(ModelSQL):
TableHandler.table_rename(cursor, 'group_rule_group_rel', self._table)
TableHandler.sequence_rename(cursor, 'group_rule_group_rel_id_seq',
self._table + '_id_seq')
+ # Migration from 2.0 rule_group_id and group_id renamed into rule_group
+ # and group
+ table = TableHandler(cursor, self, module_name)
+ table.column_rename('rule_group_id', 'rule_group')
+ table.column_rename('group_id', 'group')
super(RuleGroupGroup, self).init(module_name)
RuleGroupGroup()
@@ -125,9 +147,9 @@ class RuleGroupUser(ModelSQL):
"Rule Group - User"
_name = 'ir.rule.group-res.user'
_description = __doc__
- rule_group_id = fields.Many2One('ir.rule.group', 'Rule Group',
+ rule_group = fields.Many2One('ir.rule.group', 'Rule Group',
ondelete='CASCADE', select=1, required=True)
- user_id = fields.Many2One('res.user', 'User', ondelete='CASCADE',
+ user = fields.Many2One('res.user', 'User', ondelete='CASCADE',
select=1, required=True)
def init(self, module_name):
@@ -136,6 +158,11 @@ class RuleGroupUser(ModelSQL):
TableHandler.table_rename(cursor, 'user_rule_group_rel', self._table)
TableHandler.sequence_rename(cursor, 'user_rule_group_rel_id_seq',
self._table + '_id_seq')
+ # Migration from 2.0 rule_group_id and user_id renamed into rule_group
+ # and user
+ table = TableHandler(cursor, self, module_name)
+ table.column_rename('rule_group_id', 'rule_group')
+ table.column_rename('user_id', 'user')
super(RuleGroupUser, self).init(module_name)
RuleGroupUser()
@@ -147,7 +174,7 @@ class Lang(ModelSQL, ModelView):
def write(self, ids, vals):
res = super(Lang, self).write(ids, vals)
# Restart the cache for get_preferences
- self.pool.get('res.user').get_preferences.reset()
+ Pool().get('res.user').get_preferences.reset()
return res
Lang()
@@ -172,21 +199,21 @@ class SequenceTypeGroup(ModelSQL):
ondelete='CASCADE', select=1, required=True)
def delete(self, ids):
- rule_obj = self.pool.get('ir.rule')
+ rule_obj = Pool().get('ir.rule')
res = super(SequenceTypeGroup, self).delete(ids)
# Restart the cache on the domain_get method of ir.rule
rule_obj.domain_get.reset()
return res
def create(self, vals):
- rule_obj = self.pool.get('ir.rule')
+ rule_obj = Pool().get('ir.rule')
res = super(SequenceTypeGroup, self).create(vals)
# Restart the cache on the domain_get method of ir.rule
rule_obj.domain_get.reset()
return res
def write(self, ids, vals):
- rule_obj = self.pool.get('ir.rule')
+ rule_obj = Pool().get('ir.rule')
res = super(SequenceTypeGroup, self).write(ids, vals)
# Restart the cache on the domain_get method
rule_obj.domain_get.reset()
@@ -201,7 +228,7 @@ class Sequence(ModelSQL, ModelView):
'User Groups'), 'get_groups', searcher='search_groups')
def get_groups(self, ids, name):
- sequence_type_obj = self.pool.get('ir.sequence.type')
+ sequence_type_obj = Pool().get('ir.sequence.type')
sequences= self.browse(ids)
code2seq = {}
for sequence in sequences:
@@ -221,7 +248,7 @@ class Sequence(ModelSQL, ModelView):
return res
def search_groups(self, name, clause):
- sequence_type_obj = self.pool.get('ir.sequence.type')
+ sequence_type_obj = Pool().get('ir.sequence.type')
ids = sequence_type_obj.search([clause], order=[])
seq_types = sequence_type_obj.browse(ids)
codes = set(st.code for st in seq_types)
diff --git a/trytond/res/ir.xml b/trytond/res/ir.xml
index eb98952099..c7e2d0c63e 100644
--- a/trytond/res/ir.xml
+++ b/trytond/res/ir.xml
@@ -425,141 +425,186 @@ this repository contains the full copyright notices and license terms. -->
False
-
-
+
+
+
-
-
+
+
+
-
-
+
+
+
-