[MERGE] convert BaseModel and expression to the new API
This commit is contained in:
@@ -363,7 +363,7 @@ class AccountAssetAsset(models.Model):
|
||||
if default is None:
|
||||
default = {}
|
||||
default['name'] = self.name + _(' (copy)')
|
||||
return super(AccountAssetAsset, self).copy_data(default)[0]
|
||||
return super(AccountAssetAsset, self).copy_data(default)
|
||||
|
||||
@api.multi
|
||||
def _compute_entries(self, date, group_entries=False):
|
||||
|
||||
@@ -501,7 +501,7 @@ class ir_import(orm.TransientModel):
|
||||
def _convert_import_data(self, record, fields, options, context=None):
|
||||
""" Extracts the input browse_record and fields list (with
|
||||
``False``-y placeholders for fields to *not* import) into a
|
||||
format Model.import_data can use: a fields list without holes
|
||||
format Model.load can use: a fields list without holes
|
||||
and the precisely matching data matrix
|
||||
|
||||
:param browse_record record:
|
||||
|
||||
@@ -358,7 +358,7 @@ class test_preview(TransactionCase):
|
||||
|
||||
class test_convert_import_data(TransactionCase):
|
||||
""" Tests conversion of base_import.import input into data which
|
||||
can be fed to Model.import_data
|
||||
can be fed to Model.load
|
||||
"""
|
||||
def test_all(self):
|
||||
Import = self.registry('base_import.import')
|
||||
|
||||
@@ -65,11 +65,12 @@ class Users(models.Model):
|
||||
self.env['mail.channel'].search([('group_ids', 'in', user_group_ids)])._subscribe_users()
|
||||
return write_res
|
||||
|
||||
def copy_data(self, *args, **kwargs):
|
||||
data = super(Users, self).copy_data(*args, **kwargs)
|
||||
@api.multi
|
||||
def copy_data(self, default=None):
|
||||
data = super(Users, self).copy_data(default)[0]
|
||||
if data and data.get('alias_name'):
|
||||
data['alias_name'] = data['login']
|
||||
return data
|
||||
return [data]
|
||||
|
||||
def _create_welcome_message(self):
|
||||
self.ensure_one()
|
||||
|
||||
@@ -316,12 +316,12 @@ class ResConfigInstaller(models.TransientModel, ResConfigModuleInstallationMixin
|
||||
return dict(defaults, **dict.fromkeys(self.already_installed(), True))
|
||||
|
||||
@api.model
|
||||
def fields_get(self, fields=None, write_access=True, attributes=None):
|
||||
def fields_get(self, fields=None, attributes=None):
|
||||
""" If an addon is already installed, set it to readonly as
|
||||
res.config.installer doesn't handle uninstallations of already
|
||||
installed addons
|
||||
"""
|
||||
fields = super(ResConfigInstaller, self).fields_get(fields, write_access=write_access, attributes=attributes)
|
||||
fields = super(ResConfigInstaller, self).fields_get(fields, attributes=attributes)
|
||||
|
||||
for name in self.already_installed():
|
||||
if name not in fields:
|
||||
|
||||
@@ -876,8 +876,8 @@ class UsersView(models.Model):
|
||||
values[f] = selected and selected[-1] or False
|
||||
|
||||
@api.model
|
||||
def fields_get(self, allfields=None, write_access=True, attributes=None):
|
||||
res = super(UsersView, self).fields_get(allfields=allfields, write_access=write_access, attributes=attributes)
|
||||
def fields_get(self, allfields=None, attributes=None):
|
||||
res = super(UsersView, self).fields_get(allfields, attributes=attributes)
|
||||
# add reified groups fields
|
||||
if not self.env.user._is_admin():
|
||||
return res
|
||||
|
||||
@@ -400,6 +400,10 @@ class TestPartnerRecursion(TransactionCase):
|
||||
self.p2 = res_partner.create({'name': 'Elmtree Child 1', 'parent_id': self.p1.id})
|
||||
self.p3 = res_partner.create({'name': 'Elmtree Grand-Child 1.1', 'parent_id': self.p2.id})
|
||||
|
||||
def test_100_res_partner_recursion(self):
|
||||
self.assertTrue(self.p3._check_recursion())
|
||||
self.assertTrue((self.p1 + self.p2 + self.p3)._check_recursion())
|
||||
|
||||
# split 101, 102, 103 tests to force SQL rollback between them
|
||||
|
||||
def test_101_res_partner_recursion(self):
|
||||
@@ -511,3 +515,15 @@ class TestGroups(TransactionCase):
|
||||
|
||||
groups = all_groups.search([('full_name', 'in', ['Administration / Access Rights','Contact Creation'])])
|
||||
self.assertTrue(groups, "did not match search for 'Administration / Access Rights' and 'Contact Creation'")
|
||||
|
||||
def test_res_group_recursion(self):
|
||||
# four groups with no cycle, check them all together
|
||||
a = self.env['res.groups'].create({'name': 'A'})
|
||||
b = self.env['res.groups'].create({'name': 'B'})
|
||||
c = self.env['res.groups'].create({'name': 'G', 'implied_ids': [(6, 0, (a + b).ids)]})
|
||||
d = self.env['res.groups'].create({'name': 'D', 'implied_ids': [(6, 0, c.ids)]})
|
||||
self.assertTrue((a + b + c + d)._check_m2m_recursion('implied_ids'))
|
||||
|
||||
# create a cycle and check
|
||||
a.implied_ids = d
|
||||
self.assertFalse(a._check_m2m_recursion('implied_ids'))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import test_export, test_import, test_load
|
||||
from . import test_export, test_load
|
||||
|
||||
|
||||
@@ -1,877 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import openerp.modules.registry
|
||||
import openerp
|
||||
|
||||
from openerp.tests import common
|
||||
from openerp.tools.misc import mute_logger
|
||||
|
||||
def ok(n):
|
||||
""" Successful import of ``n`` records
|
||||
|
||||
:param int n: number of records which should have been imported
|
||||
"""
|
||||
return n, 0, 0, 0
|
||||
|
||||
def error(row, message, record=None, **kwargs):
|
||||
""" Failed import of the record ``record`` at line ``row``, with the error
|
||||
message ``message``
|
||||
|
||||
:param str message:
|
||||
:param dict record:
|
||||
"""
|
||||
return (
|
||||
-1, dict(record or {}, **kwargs),
|
||||
"Line %d : %s" % (row, message),
|
||||
'')
|
||||
|
||||
def values(seq, field='value'):
|
||||
return [item[field] for item in seq]
|
||||
|
||||
class ImporterCase(common.TransactionCase):
|
||||
model_name = False
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ImporterCase, self).__init__(*args, **kwargs)
|
||||
self.model = None
|
||||
|
||||
def setUp(self):
|
||||
super(ImporterCase, self).setUp()
|
||||
self.model = self.registry(self.model_name)
|
||||
|
||||
def import_(self, fields, rows, context=None):
|
||||
return self.model.import_data(
|
||||
self.cr, openerp.SUPERUSER_ID, fields, rows, context=context)
|
||||
def read(self, fields=('value',), domain=(), context=None):
|
||||
return self.model.read(
|
||||
self.cr, openerp.SUPERUSER_ID,
|
||||
self.model.search(self.cr, openerp.SUPERUSER_ID, domain, context=context),
|
||||
fields=fields, context=context)
|
||||
def browse(self, domain=(), context=None):
|
||||
return self.model.browse(
|
||||
self.cr, openerp.SUPERUSER_ID,
|
||||
self.model.search(self.cr, openerp.SUPERUSER_ID, domain, context=context),
|
||||
context=context)
|
||||
|
||||
def xid(self, record):
|
||||
ModelData = self.registry('ir.model.data')
|
||||
|
||||
ids = ModelData.search(
|
||||
self.cr, openerp.SUPERUSER_ID,
|
||||
[('model', '=', record._name), ('res_id', '=', record.id)])
|
||||
if ids:
|
||||
d = ModelData.read(
|
||||
self.cr, openerp.SUPERUSER_ID, ids, ['name', 'module'])[0]
|
||||
if d['module']:
|
||||
return '%s.%s' % (d['module'], d['name'])
|
||||
return d['name']
|
||||
|
||||
name = record.name_get()[0][1]
|
||||
# fix dotted name_get results, otherwise xid lookups blow up
|
||||
name = name.replace('.', '-')
|
||||
ModelData.create(self.cr, openerp.SUPERUSER_ID, {
|
||||
'name': name,
|
||||
'model': record._name,
|
||||
'res_id': record.id,
|
||||
'module': '__test__'
|
||||
})
|
||||
return '__test__.' + name
|
||||
|
||||
class test_ids_stuff(ImporterCase):
|
||||
model_name = 'export.integer'
|
||||
|
||||
def test_create_with_id(self):
|
||||
self.assertEqual(
|
||||
self.import_(['.id', 'value'], [['42', '36']]),
|
||||
error(1, u"Unknown database identifier '42'"))
|
||||
def test_create_with_xid(self):
|
||||
self.assertEqual(
|
||||
self.import_(['id', 'value'], [['somexmlid', '42']]),
|
||||
ok(1))
|
||||
self.assertEqual(
|
||||
'somexmlid',
|
||||
self.xid(self.browse()[0]))
|
||||
|
||||
def test_update_with_id(self):
|
||||
id = self.model.create(self.cr, openerp.SUPERUSER_ID, {'value': 36})
|
||||
self.assertEqual(
|
||||
36,
|
||||
self.model.browse(self.cr, openerp.SUPERUSER_ID, id).value)
|
||||
|
||||
self.assertEqual(
|
||||
self.import_(['.id', 'value'], [[str(id), '42']]),
|
||||
ok(1))
|
||||
self.assertEqual(
|
||||
[42], # updated value to imported
|
||||
values(self.read()))
|
||||
|
||||
def test_update_with_xid(self):
|
||||
self.import_(['id', 'value'], [['somexmlid', '36']])
|
||||
self.assertEqual([36], values(self.read()))
|
||||
|
||||
self.import_(['id', 'value'], [['somexmlid', '1234567']])
|
||||
self.assertEqual([1234567], values(self.read()))
|
||||
|
||||
def test_wrong_format(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [['50%']]),
|
||||
error(1, u"'50%' does not seem to be an integer for field 'unknown'"))
|
||||
|
||||
|
||||
class test_boolean_field(ImporterCase):
|
||||
model_name = 'export.boolean'
|
||||
|
||||
def test_empty(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value'], []),
|
||||
ok(0))
|
||||
|
||||
def test_exported(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [
|
||||
['False'],
|
||||
['True'],
|
||||
]),
|
||||
ok(2))
|
||||
records = self.read()
|
||||
self.assertEqual([
|
||||
False,
|
||||
True,
|
||||
], values(records))
|
||||
|
||||
def test_falses(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [
|
||||
[u'0'],
|
||||
[u'no'],
|
||||
[u'false'],
|
||||
[u'FALSE'],
|
||||
[u''],
|
||||
]),
|
||||
ok(5))
|
||||
self.assertEqual([
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
],
|
||||
values(self.read()))
|
||||
|
||||
def test_trues(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [
|
||||
['off'],
|
||||
['None'],
|
||||
['nil'],
|
||||
['()'],
|
||||
['f'],
|
||||
['#f'],
|
||||
# Problem: OpenOffice (and probably excel) output localized booleans
|
||||
['VRAI'],
|
||||
[u'OFF'],
|
||||
[u'是的'],
|
||||
['!&%#${}'],
|
||||
['%(field)s'],
|
||||
]),
|
||||
ok(11))
|
||||
self.assertEqual(
|
||||
[True] * 11,
|
||||
values(self.read()))
|
||||
|
||||
class test_integer_field(ImporterCase):
|
||||
model_name = 'export.integer'
|
||||
|
||||
def test_none(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value'], []),
|
||||
ok(0))
|
||||
|
||||
def test_empty(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [['']]),
|
||||
ok(1))
|
||||
self.assertEqual(
|
||||
[False],
|
||||
values(self.read()))
|
||||
|
||||
def test_zero(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [['0']]),
|
||||
ok(1))
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [['-0']]),
|
||||
ok(1))
|
||||
self.assertEqual([False, False], values(self.read()))
|
||||
|
||||
def test_positives(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [
|
||||
['1'],
|
||||
['42'],
|
||||
[str(2**31-1)],
|
||||
['12345678']
|
||||
]),
|
||||
ok(4))
|
||||
self.assertEqual([
|
||||
1, 42, 2**31-1, 12345678
|
||||
], values(self.read()))
|
||||
|
||||
def test_negatives(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [
|
||||
['-1'],
|
||||
['-42'],
|
||||
[str(-(2**31 - 1))],
|
||||
[str(-(2**31))],
|
||||
['-12345678']
|
||||
]),
|
||||
ok(5))
|
||||
self.assertEqual([
|
||||
-1, -42, -(2**31 - 1), -(2**31), -12345678
|
||||
], values(self.read()))
|
||||
|
||||
@mute_logger('openerp.sql_db')
|
||||
def test_out_of_range(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [[str(2**31)]]),
|
||||
error(1, "integer out of range\n"))
|
||||
# auto-rollbacks if error is in process_liness, but not during
|
||||
# ir.model.data write. Can differentiate because former ends lines
|
||||
# error lines with "!"
|
||||
self.cr.rollback()
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [[str(-2**32)]]),
|
||||
error(1, "integer out of range\n"))
|
||||
|
||||
|
||||
def test_nonsense(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [['zorglub']]),
|
||||
error(1, u"'zorglub' does not seem to be an integer for field 'unknown'"))
|
||||
|
||||
class test_float_field(ImporterCase):
|
||||
model_name = 'export.float'
|
||||
def test_none(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value'], []),
|
||||
ok(0))
|
||||
|
||||
def test_empty(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [['']]),
|
||||
ok(1))
|
||||
self.assertEqual(
|
||||
[False],
|
||||
values(self.read()))
|
||||
|
||||
def test_zero(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [['0']]),
|
||||
ok(1))
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [['-0']]),
|
||||
ok(1))
|
||||
self.assertEqual([False, False], values(self.read()))
|
||||
|
||||
def test_positives(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [
|
||||
['1'],
|
||||
['42'],
|
||||
[str(2**31-1)],
|
||||
['12345678'],
|
||||
[str(2**33)],
|
||||
['0.000001'],
|
||||
]),
|
||||
ok(6))
|
||||
self.assertEqual([
|
||||
1, 42, 2**31-1, 12345678, 2.0**33, .000001
|
||||
], values(self.read()))
|
||||
|
||||
def test_negatives(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [
|
||||
['-1'],
|
||||
['-42'],
|
||||
[str(-2**31 + 1)],
|
||||
[str(-2**31)],
|
||||
['-12345678'],
|
||||
[str(-2**33)],
|
||||
['-0.000001'],
|
||||
]),
|
||||
ok(7))
|
||||
self.assertEqual([
|
||||
-1, -42, -(2**31 - 1), -(2**31), -12345678, -2.0**33, -.000001
|
||||
], values(self.read()))
|
||||
|
||||
def test_nonsense(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [['foobar']]),
|
||||
error(1, u"'foobar' does not seem to be a number for field 'unknown'"))
|
||||
|
||||
class test_string_field(ImporterCase):
|
||||
model_name = 'export.string.bounded'
|
||||
|
||||
def test_empty(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [['']]),
|
||||
ok(1))
|
||||
self.assertEqual([False], values(self.read()))
|
||||
|
||||
def test_imported(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [
|
||||
[u'foobar'],
|
||||
[u'foobarbaz'],
|
||||
[u'Með suð í eyrum við spilum endalaust'],
|
||||
[u"People 'get' types. They use them all the time. Telling "
|
||||
u"someone he can't pound a nail with a banana doesn't much "
|
||||
u"surprise him."]
|
||||
]),
|
||||
ok(4))
|
||||
self.assertEqual([
|
||||
u"foobar",
|
||||
u"foobarbaz",
|
||||
u"Með suð í eyrum ",
|
||||
u"People 'get' typ",
|
||||
], values(self.read()))
|
||||
|
||||
class test_unbound_string_field(ImporterCase):
|
||||
model_name = 'export.string'
|
||||
|
||||
def test_imported(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [
|
||||
[u'í dag viðrar vel til loftárása'],
|
||||
# ackbar.jpg
|
||||
[u"If they ask you about fun, you tell them – fun is a filthy"
|
||||
u" parasite"]
|
||||
]),
|
||||
ok(2))
|
||||
self.assertEqual([
|
||||
u"í dag viðrar vel til loftárása",
|
||||
u"If they ask you about fun, you tell them – fun is a filthy parasite"
|
||||
], values(self.read()))
|
||||
|
||||
class test_text(ImporterCase):
|
||||
model_name = 'export.text'
|
||||
|
||||
def test_empty(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [['']]),
|
||||
ok(1))
|
||||
self.assertEqual([False], values(self.read()))
|
||||
|
||||
def test_imported(self):
|
||||
s = (u"Breiðskífa er notað um útgefna hljómplötu sem inniheldur "
|
||||
u"stúdíóupptökur frá einum flytjanda. Breiðskífur eru oftast "
|
||||
u"milli 25-80 mínútur og er lengd þeirra oft miðuð við 33⅓ "
|
||||
u"snúninga 12 tommu vínylplötur (sem geta verið allt að 30 mín "
|
||||
u"hvor hlið).\n\nBreiðskífur eru stundum tvöfaldar og eru þær þá"
|
||||
u" gefnar út á tveimur geisladiskum eða tveimur vínylplötum.")
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [[s]]),
|
||||
ok(1))
|
||||
self.assertEqual([s], values(self.read()))
|
||||
|
||||
class test_selection(ImporterCase):
|
||||
model_name = 'export.selection'
|
||||
translations_fr = [
|
||||
("Qux", "toto"),
|
||||
("Bar", "titi"),
|
||||
("Foo", "tete"),
|
||||
]
|
||||
|
||||
def test_imported(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [
|
||||
['Qux'],
|
||||
['Bar'],
|
||||
['Foo'],
|
||||
['2'],
|
||||
]),
|
||||
ok(4))
|
||||
self.assertEqual([3, 2, 1, 2], values(self.read()))
|
||||
|
||||
def test_imported_translated(self):
|
||||
self.registry('res.lang').load_lang(self.cr, openerp.SUPERUSER_ID, 'fr_FR')
|
||||
Translations = self.registry('ir.translation')
|
||||
for source, value in self.translations_fr:
|
||||
Translations.create(self.cr, openerp.SUPERUSER_ID, {
|
||||
'name': 'export.selection,value',
|
||||
'lang': 'fr_FR',
|
||||
'type': 'selection',
|
||||
'src': source,
|
||||
'value': value
|
||||
})
|
||||
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [
|
||||
['toto'],
|
||||
['tete'],
|
||||
['titi'],
|
||||
], context={'lang': 'fr_FR'}),
|
||||
ok(3))
|
||||
self.assertEqual([3, 1, 2], values(self.read()))
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [['Foo']], context={'lang': 'fr_FR'}),
|
||||
ok(1))
|
||||
|
||||
def test_invalid(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [['Baz']]),
|
||||
error(1, u"Value 'Baz' not found in selection field 'unknown'"))
|
||||
self.cr.rollback()
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [[42]]),
|
||||
error(1, u"Value '42' not found in selection field 'unknown'"))
|
||||
|
||||
class test_selection_function(ImporterCase):
|
||||
model_name = 'export.selection.function'
|
||||
translations_fr = [
|
||||
("Corge", "toto"),
|
||||
("Grault", "titi"),
|
||||
("Wheee", "tete"),
|
||||
("Moog", "tutu"),
|
||||
]
|
||||
|
||||
def test_imported(self):
|
||||
""" import uses fields_get, so translates import label (may or may not
|
||||
be good news) *and* serializes the selection function to reverse it:
|
||||
import does not actually know that the selection field uses a function
|
||||
"""
|
||||
# NOTE: conflict between a value and a label => ?
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [
|
||||
['3'],
|
||||
["Grault"],
|
||||
]),
|
||||
ok(2))
|
||||
self.assertEqual(
|
||||
[3, 1],
|
||||
values(self.read()))
|
||||
|
||||
def test_translated(self):
|
||||
""" Expects output of selection function returns translated labels
|
||||
"""
|
||||
self.registry('res.lang').load_lang(self.cr, openerp.SUPERUSER_ID, 'fr_FR')
|
||||
Translations = self.registry('ir.translation')
|
||||
for source, value in self.translations_fr:
|
||||
Translations.create(self.cr, openerp.SUPERUSER_ID, {
|
||||
'name': 'export.selection,value',
|
||||
'lang': 'fr_FR',
|
||||
'type': 'selection',
|
||||
'src': source,
|
||||
'value': value
|
||||
})
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [
|
||||
['toto'],
|
||||
['tete'],
|
||||
], context={'lang': 'fr_FR'}),
|
||||
ok(2))
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [['Wheee']], context={'lang': 'fr_FR'}),
|
||||
ok(1))
|
||||
|
||||
class test_m2o(ImporterCase):
|
||||
model_name = 'export.many2one'
|
||||
|
||||
def test_by_name(self):
|
||||
# create integer objects
|
||||
integer_id1 = self.registry('export.integer').create(
|
||||
self.cr, openerp.SUPERUSER_ID, {'value': 42})
|
||||
integer_id2 = self.registry('export.integer').create(
|
||||
self.cr, openerp.SUPERUSER_ID, {'value': 36})
|
||||
# get its name
|
||||
name1 = dict(self.registry('export.integer').name_get(
|
||||
self.cr, openerp.SUPERUSER_ID,[integer_id1]))[integer_id1]
|
||||
name2 = dict(self.registry('export.integer').name_get(
|
||||
self.cr, openerp.SUPERUSER_ID,[integer_id2]))[integer_id2]
|
||||
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [
|
||||
# import by name_get
|
||||
[name1],
|
||||
[name1],
|
||||
[name2],
|
||||
]),
|
||||
ok(3))
|
||||
# correct ids assigned to corresponding records
|
||||
self.assertEqual([
|
||||
(integer_id1, name1),
|
||||
(integer_id1, name1),
|
||||
(integer_id2, name2),],
|
||||
values(self.read()))
|
||||
|
||||
def test_by_xid(self):
|
||||
ExportInteger = self.registry('export.integer')
|
||||
integer_id = ExportInteger.create(
|
||||
self.cr, openerp.SUPERUSER_ID, {'value': 42})
|
||||
xid = self.xid(ExportInteger.browse(
|
||||
self.cr, openerp.SUPERUSER_ID, [integer_id])[0])
|
||||
|
||||
self.assertEqual(
|
||||
self.import_(['value/id'], [[xid]]),
|
||||
ok(1))
|
||||
b = self.browse()
|
||||
self.assertEqual(42, b[0].value.value)
|
||||
|
||||
def test_by_id(self):
|
||||
integer_id = self.registry('export.integer').create(
|
||||
self.cr, openerp.SUPERUSER_ID, {'value': 42})
|
||||
self.assertEqual(
|
||||
self.import_(['value/.id'], [[integer_id]]),
|
||||
ok(1))
|
||||
b = self.browse()
|
||||
self.assertEqual(42, b[0].value.value)
|
||||
|
||||
def test_by_names(self):
|
||||
integer_id1 = self.registry('export.integer').create(
|
||||
self.cr, openerp.SUPERUSER_ID, {'value': 42})
|
||||
integer_id2 = self.registry('export.integer').create(
|
||||
self.cr, openerp.SUPERUSER_ID, {'value': 42})
|
||||
name1 = dict(self.registry('export.integer').name_get(
|
||||
self.cr, openerp.SUPERUSER_ID,[integer_id1]))[integer_id1]
|
||||
name2 = dict(self.registry('export.integer').name_get(
|
||||
self.cr, openerp.SUPERUSER_ID,[integer_id2]))[integer_id2]
|
||||
# names should be the same
|
||||
self.assertEqual(name1, name2)
|
||||
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [[name2]]),
|
||||
ok(1))
|
||||
self.assertEqual([
|
||||
(integer_id1, name1)
|
||||
], values(self.read()))
|
||||
|
||||
def test_fail_by_implicit_id(self):
|
||||
""" Can't implicitly import records by id
|
||||
"""
|
||||
# create integer objects
|
||||
integer_id1 = self.registry('export.integer').create(
|
||||
self.cr, openerp.SUPERUSER_ID, {'value': 42})
|
||||
integer_id2 = self.registry('export.integer').create(
|
||||
self.cr, openerp.SUPERUSER_ID, {'value': 36})
|
||||
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [
|
||||
# import by id, without specifying it
|
||||
[integer_id1],
|
||||
[integer_id2],
|
||||
[integer_id1],
|
||||
]),
|
||||
error(1, u"No matching record found for name '%s' in field 'unknown'" % integer_id1))
|
||||
|
||||
def test_sub_field(self):
|
||||
""" Does not implicitly create the record, does not warn that you can't
|
||||
import m2o subfields (at all)...
|
||||
"""
|
||||
self.assertEqual(
|
||||
self.import_(['value/value'], [['42']]),
|
||||
error(1, u"Can not create Many-To-One records indirectly, import the field separately"))
|
||||
|
||||
def test_fail_noids(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [['nameisnoexist:3']]),
|
||||
error(1, u"No matching record found for name 'nameisnoexist:3' in field 'unknown'"))
|
||||
self.cr.rollback()
|
||||
self.assertEqual(
|
||||
self.import_(['value/id'], [['noxidhere']]),
|
||||
error(1, u"No matching record found for external id 'noxidhere' in field 'unknown'"))
|
||||
self.cr.rollback()
|
||||
self.assertEqual(
|
||||
self.import_(['value/.id'], [[66]]),
|
||||
error(1, u"No matching record found for database id '66' in field 'unknown'"))
|
||||
|
||||
class test_m2m(ImporterCase):
|
||||
model_name = 'export.many2many'
|
||||
|
||||
# apparently, one and only thing which works is a
|
||||
# csv_internal_sep-separated list of ids, xids, or names (depending if
|
||||
# m2m/.id, m2m/id or m2m[/anythingelse]
|
||||
def test_ids(self):
|
||||
id1 = self.registry('export.many2many.other').create(
|
||||
self.cr, openerp.SUPERUSER_ID, {'value': 3, 'str': 'record0'})
|
||||
id2 = self.registry('export.many2many.other').create(
|
||||
self.cr, openerp.SUPERUSER_ID, {'value': 44, 'str': 'record1'})
|
||||
id3 = self.registry('export.many2many.other').create(
|
||||
self.cr, openerp.SUPERUSER_ID, {'value': 84, 'str': 'record2'})
|
||||
id4 = self.registry('export.many2many.other').create(
|
||||
self.cr, openerp.SUPERUSER_ID, {'value': 9, 'str': 'record3'})
|
||||
id5 = self.registry('export.many2many.other').create(
|
||||
self.cr, openerp.SUPERUSER_ID, {'value': 99, 'str': 'record4'})
|
||||
|
||||
self.assertEqual(
|
||||
self.import_(['value/.id'], [
|
||||
['%d,%d' % (id1, id2)],
|
||||
['%d,%d,%d' % (id1, id3, id4)],
|
||||
['%d,%d,%d' % (id1, id2, id3)],
|
||||
['%d' % id5]
|
||||
]),
|
||||
ok(4))
|
||||
ids = lambda records: [record.id for record in records]
|
||||
|
||||
b = self.browse()
|
||||
self.assertEqual(ids(b[0].value), [id1, id2])
|
||||
self.assertEqual(values(b[0].value), [3, 44])
|
||||
|
||||
self.assertEqual(ids(b[2].value), [id1, id2, id3])
|
||||
self.assertEqual(values(b[2].value), [3, 44, 84])
|
||||
|
||||
def test_noids(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value/.id'], [['42']]),
|
||||
error(1, u"No matching record found for database id '42' in field 'unknown'"))
|
||||
|
||||
def test_xids(self):
|
||||
M2O_o = self.registry('export.many2many.other')
|
||||
id1 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 3, 'str': 'record0'})
|
||||
id2 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 44, 'str': 'record1'})
|
||||
id3 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 84, 'str': 'record2'})
|
||||
id4 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 9, 'str': 'record3'})
|
||||
records = M2O_o.browse(self.cr, openerp.SUPERUSER_ID, [id1, id2, id3, id4])
|
||||
|
||||
self.assertEqual(
|
||||
self.import_(['value/id'], [
|
||||
['%s,%s' % (self.xid(records[0]), self.xid(records[1]))],
|
||||
['%s' % self.xid(records[3])],
|
||||
['%s,%s' % (self.xid(records[2]), self.xid(records[1]))],
|
||||
]),
|
||||
ok(3))
|
||||
|
||||
b = self.browse()
|
||||
self.assertEqual(values(b[0].value), [3, 44])
|
||||
self.assertEqual(values(b[2].value), [44, 84])
|
||||
def test_noxids(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value/id'], [['noxidforthat']]),
|
||||
error(1, u"No matching record found for external id 'noxidforthat' in field 'unknown'"))
|
||||
|
||||
def test_names(self):
|
||||
M2O_o = self.registry('export.many2many.other')
|
||||
id1 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 3, 'str': 'record0'})
|
||||
id2 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 44, 'str': 'record1'})
|
||||
id3 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 84, 'str': 'record2'})
|
||||
id4 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 9, 'str': 'record3'})
|
||||
records = M2O_o.browse(self.cr, openerp.SUPERUSER_ID, [id1, id2, id3, id4])
|
||||
|
||||
name = lambda record: record.name_get()[0][1]
|
||||
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [
|
||||
['%s,%s' % (name(records[1]), name(records[2]))],
|
||||
['%s,%s,%s' % (name(records[0]), name(records[1]), name(records[2]))],
|
||||
['%s,%s' % (name(records[0]), name(records[3]))],
|
||||
]),
|
||||
ok(3))
|
||||
|
||||
b = self.browse()
|
||||
self.assertEqual(values(b[1].value), [3, 44, 84])
|
||||
self.assertEqual(values(b[2].value), [3, 9])
|
||||
|
||||
def test_nonames(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value'], [['wherethem2mhavenonames']]),
|
||||
error(1, u"No matching record found for name 'wherethem2mhavenonames' in field 'unknown'"))
|
||||
|
||||
def test_import_to_existing(self):
|
||||
M2O_o = self.registry('export.many2many.other')
|
||||
id1 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 3, 'str': 'record0'})
|
||||
id2 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 44, 'str': 'record1'})
|
||||
id3 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 84, 'str': 'record2'})
|
||||
id4 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 9, 'str': 'record3'})
|
||||
|
||||
xid = 'myxid'
|
||||
self.assertEqual(
|
||||
self.import_(['id', 'value/.id'], [[xid, '%d,%d' % (id1, id2)]]),
|
||||
ok(1))
|
||||
self.assertEqual(
|
||||
self.import_(['id', 'value/.id'], [[xid, '%d,%d' % (id3, id4)]]),
|
||||
ok(1))
|
||||
|
||||
b = self.browse()
|
||||
self.assertEqual(len(b), 1)
|
||||
# TODO: replacement of existing m2m values is correct?
|
||||
self.assertEqual(values(b[0].value), [84, 9])
|
||||
|
||||
class test_o2m(ImporterCase):
|
||||
model_name = 'export.one2many'
|
||||
|
||||
def test_name_get(self):
|
||||
s = u'Java is a DSL for taking large XML files and converting them to' \
|
||||
u' stack traces'
|
||||
self.assertEqual(
|
||||
self.import_(
|
||||
['const', 'value'],
|
||||
[['5', s]]),
|
||||
error(1, u"No matching record found for name '%s' in field 'unknown'" % s))
|
||||
|
||||
def test_single(self):
|
||||
self.assertEqual(
|
||||
self.import_(['const', 'value/value'], [
|
||||
['5', '63']
|
||||
]),
|
||||
ok(1))
|
||||
|
||||
(b,) = self.browse()
|
||||
self.assertEqual(b.const, 5)
|
||||
self.assertEqual(values(b.value), [63])
|
||||
|
||||
def test_multicore(self):
|
||||
self.assertEqual(
|
||||
self.import_(['const', 'value/value'], [
|
||||
['5', '63'],
|
||||
['6', '64'],
|
||||
]),
|
||||
ok(2))
|
||||
|
||||
b1, b2 = self.browse()
|
||||
self.assertEqual(b1.const, 5)
|
||||
self.assertEqual(values(b1.value), [63])
|
||||
self.assertEqual(b2.const, 6)
|
||||
self.assertEqual(values(b2.value), [64])
|
||||
|
||||
def test_multisub(self):
|
||||
self.assertEqual(
|
||||
self.import_(['const', 'value/value'], [
|
||||
['5', '63'],
|
||||
['', '64'],
|
||||
['', '65'],
|
||||
['', '66'],
|
||||
]),
|
||||
ok(4))
|
||||
|
||||
(b,) = self.browse()
|
||||
self.assertEqual(values(b.value), [63, 64, 65, 66])
|
||||
|
||||
def test_multi_subfields(self):
|
||||
self.assertEqual(
|
||||
self.import_(['value/str', 'const', 'value/value'], [
|
||||
['this', '5', '63'],
|
||||
['is', '', '64'],
|
||||
['the', '', '65'],
|
||||
['rhythm', '', '66'],
|
||||
]),
|
||||
ok(4))
|
||||
|
||||
(b,) = self.browse()
|
||||
self.assertEqual(values(b.value), [63, 64, 65, 66])
|
||||
self.assertEqual(
|
||||
values(b.value, 'str'),
|
||||
'this is the rhythm'.split())
|
||||
|
||||
def test_link_inline(self):
|
||||
id1 = self.registry('export.one2many.child').create(self.cr, openerp.SUPERUSER_ID, {
|
||||
'str': 'Bf', 'value': 109
|
||||
})
|
||||
id2 = self.registry('export.one2many.child').create(self.cr, openerp.SUPERUSER_ID, {
|
||||
'str': 'Me', 'value': 262
|
||||
})
|
||||
|
||||
try:
|
||||
self.import_(['const', 'value/.id'], [
|
||||
['42', '%d,%d' % (id1, id2)]
|
||||
])
|
||||
except ValueError, e:
|
||||
# should be Exception(Database ID doesn't exist: export.one2many.child : $id1,$id2)
|
||||
self.assertIs(type(e), ValueError)
|
||||
self.assertEqual(
|
||||
e.args[0],
|
||||
"invalid literal for int() with base 10: '%d,%d'" % (id1, id2))
|
||||
|
||||
def test_link(self):
|
||||
id1 = self.registry('export.one2many.child').create(self.cr, openerp.SUPERUSER_ID, {
|
||||
'str': 'Bf', 'value': 109
|
||||
})
|
||||
id2 = self.registry('export.one2many.child').create(self.cr, openerp.SUPERUSER_ID, {
|
||||
'str': 'Me', 'value': 262
|
||||
})
|
||||
|
||||
self.assertEqual(
|
||||
self.import_(['const', 'value/.id'], [
|
||||
['42', str(id1)],
|
||||
['', str(id2)],
|
||||
]),
|
||||
ok(2))
|
||||
|
||||
[b] = self.browse()
|
||||
self.assertEqual(b.const, 42)
|
||||
# automatically forces link between core record and o2ms
|
||||
self.assertEqual(values(b.value), [109, 262])
|
||||
self.assertEqual(values(b.value, field='parent_id'), [b, b])
|
||||
|
||||
def test_link_2(self):
|
||||
O2M_c = self.registry('export.one2many.child')
|
||||
id1 = O2M_c.create(self.cr, openerp.SUPERUSER_ID, {
|
||||
'str': 'Bf', 'value': 109
|
||||
})
|
||||
id2 = O2M_c.create(self.cr, openerp.SUPERUSER_ID, {
|
||||
'str': 'Me', 'value': 262
|
||||
})
|
||||
|
||||
self.assertEqual(
|
||||
self.import_(['const', 'value/.id', 'value/value'], [
|
||||
['42', str(id1), '1'],
|
||||
['', str(id2), '2'],
|
||||
]),
|
||||
ok(2))
|
||||
|
||||
[b] = self.browse()
|
||||
self.assertEqual(b.const, 42)
|
||||
self.assertEqual(values(b.value), [1, 2])
|
||||
self.assertEqual(values(b.value, field='parent_id'), [b, b])
|
||||
|
||||
class test_o2m_multiple(ImporterCase):
|
||||
model_name = 'export.one2many.multiple'
|
||||
|
||||
def test_multi_mixed(self):
|
||||
self.assertEqual(
|
||||
self.import_(['const', 'child1/value', 'child2/value'], [
|
||||
['5', '11', '21'],
|
||||
['', '12', '22'],
|
||||
['', '13', '23'],
|
||||
['', '14', ''],
|
||||
]),
|
||||
ok(4))
|
||||
|
||||
[b] = self.browse()
|
||||
self.assertEqual(values(b.child1), [11, 12, 13, 14])
|
||||
self.assertEqual(values(b.child2), [21, 22, 23])
|
||||
|
||||
def test_multi(self):
|
||||
self.assertEqual(
|
||||
self.import_(['const', 'child1/value', 'child2/value'], [
|
||||
['5', '11', '21'],
|
||||
['', '12', ''],
|
||||
['', '13', ''],
|
||||
['', '14', ''],
|
||||
['', '', '22'],
|
||||
['', '', '23'],
|
||||
]),
|
||||
ok(6))
|
||||
|
||||
[b] = self.browse()
|
||||
self.assertEqual(values(b.child1), [11, 12, 13, 14])
|
||||
self.assertEqual(values(b.child2), [21, 22, 23])
|
||||
|
||||
def test_multi_fullsplit(self):
|
||||
self.assertEqual(
|
||||
self.import_(['const', 'child1/value', 'child2/value'], [
|
||||
['5', '11', ''],
|
||||
['', '12', ''],
|
||||
['', '13', ''],
|
||||
['', '14', ''],
|
||||
['', '', '21'],
|
||||
['', '', '22'],
|
||||
['', '', '23'],
|
||||
]),
|
||||
ok(7))
|
||||
|
||||
[b] = self.browse()
|
||||
self.assertEqual(b.const, 5)
|
||||
self.assertEqual(values(b.child1), [11, 12, 13, 14])
|
||||
self.assertEqual(values(b.child2), [21, 22, 23])
|
||||
|
||||
# function, related, reference: written to db as-is...
|
||||
# => function uses @type for value coercion/conversion
|
||||
@@ -248,6 +248,10 @@ class MixedModel(models.Model):
|
||||
lang = fields.Selection(string='Language', selection='_get_lang')
|
||||
reference = fields.Reference(string='Related Document',
|
||||
selection='_reference_models')
|
||||
comment1 = fields.Html(sanitize=False)
|
||||
comment2 = fields.Html(sanitize=True, strip_classes=False)
|
||||
comment3 = fields.Html(sanitize=True, strip_classes=True)
|
||||
comment4 = fields.Html(sanitize=True, strip_style=True)
|
||||
|
||||
@api.one
|
||||
def _compute_now(self):
|
||||
|
||||
@@ -125,15 +125,15 @@ class TestHtmlField(common.TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestHtmlField, self).setUp()
|
||||
self.partner = self.registry('res.partner')
|
||||
self.model = self.env['test_new_api.mixed']
|
||||
|
||||
def test_00_sanitize(self):
|
||||
cr, uid, context = self.cr, self.uid, {}
|
||||
old_columns = self.partner._columns
|
||||
self.partner._columns = dict(old_columns)
|
||||
self.partner._columns.update({
|
||||
'comment': fields.html('Secure Html', sanitize=False),
|
||||
})
|
||||
self.assertEqual(self.model._fields['comment1'].sanitize, False)
|
||||
self.assertEqual(self.model._fields['comment2'].sanitize, True)
|
||||
self.assertEqual(self.model._fields['comment2'].strip_classes, False)
|
||||
self.assertEqual(self.model._fields['comment3'].sanitize, True)
|
||||
self.assertEqual(self.model._fields['comment3'].strip_classes, True)
|
||||
|
||||
some_ugly_html = """<p>Oops this should maybe be sanitized
|
||||
% if object.some_field and not object.oriented:
|
||||
<table>
|
||||
@@ -151,40 +151,22 @@ class TestHtmlField(common.TransactionCase):
|
||||
<p>Youpie</p>
|
||||
%endif"""
|
||||
|
||||
pid = self.partner.create(cr, uid, {
|
||||
'name': 'Raoul Poilvache',
|
||||
'comment': some_ugly_html,
|
||||
}, context=context)
|
||||
partner = self.partner.browse(cr, uid, pid, context=context)
|
||||
self.assertEqual(partner.comment, some_ugly_html, 'Error in HTML field: content was sanitized but field has sanitize=False')
|
||||
|
||||
self.partner._columns.update({
|
||||
'comment': fields.html('Unsecure Html', sanitize=True, strip_classes=False),
|
||||
record = self.model.create({
|
||||
'comment1': some_ugly_html,
|
||||
'comment2': some_ugly_html,
|
||||
'comment3': some_ugly_html,
|
||||
'comment4': some_ugly_html,
|
||||
})
|
||||
self.partner.write(cr, uid, [pid], {
|
||||
'comment': some_ugly_html,
|
||||
}, context=context)
|
||||
partner = self.partner.browse(cr, uid, pid, context=context)
|
||||
# classes are kept
|
||||
self.assertIn('<tr class="', partner.comment)
|
||||
|
||||
self.partner._columns.update({
|
||||
'comment': fields.html('Unsecure Html', sanitize=True, strip_classes=True),
|
||||
})
|
||||
self.partner.write(cr, uid, [pid], {
|
||||
'comment': some_ugly_html,
|
||||
}, context=context)
|
||||
partner = self.partner.browse(cr, uid, pid, context=context)
|
||||
self.assertEqual(record.comment1, some_ugly_html, 'Error in HTML field: content was sanitized but field has sanitize=False')
|
||||
|
||||
self.assertIn('<tr class="', record.comment2)
|
||||
|
||||
# sanitize should have closed tags left open in the original html
|
||||
self.assertIn('</table>', partner.comment, 'Error in HTML field: content does not seem to have been sanitized despise sanitize=True')
|
||||
self.assertIn('</td>', partner.comment, 'Error in HTML field: content does not seem to have been sanitized despise sanitize=True')
|
||||
self.assertIn('<tr style="', partner.comment, 'Style attr should not have been stripped')
|
||||
self.assertIn('</table>', record.comment3, 'Error in HTML field: content does not seem to have been sanitized despise sanitize=True')
|
||||
self.assertIn('</td>', record.comment3, 'Error in HTML field: content does not seem to have been sanitized despise sanitize=True')
|
||||
self.assertIn('<tr style="', record.comment3, 'Style attr should not have been stripped')
|
||||
# sanitize does not keep classes if asked to
|
||||
self.assertNotIn('<tr class="', partner.comment)
|
||||
self.assertNotIn('<tr class="', record.comment3)
|
||||
|
||||
self.partner._columns['comment'] = fields.html('Stripped Html', sanitize=True, strip_style=True)
|
||||
self.partner.write(cr, uid, [pid], {'comment': some_ugly_html}, context=context)
|
||||
partner = self.partner.browse(cr, uid, pid, context=context)
|
||||
self.assertNotIn('<tr style="', partner.comment, 'Style attr should have been stripped')
|
||||
|
||||
self.partner._columns = old_columns
|
||||
self.assertNotIn('<tr style="', record.comment4, 'Style attr should have been stripped')
|
||||
|
||||
+22
-11
@@ -45,11 +45,9 @@ __all__ = [
|
||||
]
|
||||
|
||||
import logging
|
||||
import operator
|
||||
|
||||
from inspect import currentframe, getargspec
|
||||
from collections import defaultdict, MutableMapping
|
||||
from collections import defaultdict, Mapping
|
||||
from contextlib import contextmanager
|
||||
from inspect import currentframe, getargspec
|
||||
from pprint import pformat
|
||||
from weakref import WeakSet
|
||||
from werkzeug.local import Local, release_local
|
||||
@@ -293,10 +291,10 @@ def get_upgrade(method):
|
||||
return upgrade
|
||||
elif model == 'self':
|
||||
return lambda self, *args, **kwargs: self.browse(args[0])
|
||||
else:
|
||||
elif model:
|
||||
return lambda self, *args, **kwargs: self.env[model].browse(args[0])
|
||||
else:
|
||||
return lambda self, *args, **kwargs: args[0]
|
||||
|
||||
return lambda self, *args, **kwargs: args[0]
|
||||
|
||||
|
||||
def get_aggregate(method):
|
||||
@@ -309,10 +307,10 @@ def get_aggregate(method):
|
||||
model, _, _ = spec
|
||||
if model == 'self':
|
||||
return lambda self, value: sum(value, self.browse())
|
||||
else:
|
||||
elif model:
|
||||
return lambda self, value: sum(value, self.env[model].browse())
|
||||
else:
|
||||
return lambda self, value: value
|
||||
|
||||
return lambda self, value: value
|
||||
|
||||
|
||||
def get_context_split(method):
|
||||
@@ -813,7 +811,7 @@ def expected(decorator, func):
|
||||
|
||||
|
||||
|
||||
class Environment(object):
|
||||
class Environment(Mapping):
|
||||
""" An environment wraps data for ORM records:
|
||||
|
||||
- :attr:`cr`, the current database cursor;
|
||||
@@ -871,6 +869,10 @@ class Environment(object):
|
||||
envs.add(self)
|
||||
return self
|
||||
|
||||
#
|
||||
# Mapping methods
|
||||
#
|
||||
|
||||
def __contains__(self, model_name):
|
||||
""" Test whether the given model exists. """
|
||||
return model_name in self.registry
|
||||
@@ -887,6 +889,15 @@ class Environment(object):
|
||||
""" Return the size of the model registry. """
|
||||
return len(self.registry)
|
||||
|
||||
def __eq__(self, other):
|
||||
return self is other
|
||||
|
||||
def __ne__(self, other):
|
||||
return self is not other
|
||||
|
||||
def __hash__(self):
|
||||
return object.__hash__(self)
|
||||
|
||||
def __call__(self, cr=None, user=None, context=None):
|
||||
""" Return an environment based on ``self`` with modified parameters.
|
||||
|
||||
|
||||
+1550
-1792
File diff suppressed because it is too large
Load Diff
@@ -357,7 +357,7 @@ def init_models(models, cr, context):
|
||||
model._auto_end()
|
||||
cr.commit()
|
||||
for _, func, args in sorted(context['todo'], key=itemgetter(0)):
|
||||
func(cr, *args)
|
||||
func(*args)
|
||||
if models:
|
||||
models[0].recompute()
|
||||
cr.commit()
|
||||
|
||||
+64
-69
@@ -423,7 +423,6 @@ def select_from_where(cr, select_field, from_table, where_field, where_ids, wher
|
||||
res.extend([r[0] for r in cr.fetchall()])
|
||||
return res
|
||||
|
||||
|
||||
def select_distinct_from_where_not_null(cr, select_field, from_table):
|
||||
cr.execute('SELECT distinct("%s") FROM "%s" where "%s" is not null' % (select_field, from_table, select_field))
|
||||
return [r[0] for r in cr.fetchall()]
|
||||
@@ -611,6 +610,7 @@ def create_substitution_leaf(leaf, new_elements, new_model=None, internal=False)
|
||||
new_leaf = ExtendedLeaf(new_elements, new_model, join_context=new_join_context, internal=internal)
|
||||
return new_leaf
|
||||
|
||||
|
||||
class expression(object):
|
||||
""" Parse a domain expression
|
||||
Use a real polish notation
|
||||
@@ -618,12 +618,12 @@ class expression(object):
|
||||
For more info: http://christophe-simonis-at-tiny.blogspot.com/2008/08/new-new-domain-notation.html
|
||||
"""
|
||||
|
||||
def __init__(self, cr, uid, exp, table, context):
|
||||
def __init__(self, domain, model):
|
||||
""" Initialize expression object and automatically parse the expression
|
||||
right after initialization.
|
||||
|
||||
:param exp: expression (using domain ('foo', '=', 'bar' format))
|
||||
:param table: root model
|
||||
:param domain: expression (using domain ('foo', '=', 'bar' format))
|
||||
:param model: root model
|
||||
|
||||
:attr list result: list that will hold the result of the parsing
|
||||
as a list of ExtendedLeaf
|
||||
@@ -633,15 +633,15 @@ class expression(object):
|
||||
:attr list expression: the domain expression, that will be normalized
|
||||
and prepared
|
||||
"""
|
||||
self._unaccent = get_unaccent_wrapper(cr)
|
||||
self._unaccent = get_unaccent_wrapper(model._cr)
|
||||
self.joins = []
|
||||
self.root_model = table
|
||||
self.root_model = model
|
||||
|
||||
# normalize and prepare the expression for parsing
|
||||
self.expression = distribute_not(normalize_domain(exp))
|
||||
self.expression = distribute_not(normalize_domain(domain))
|
||||
|
||||
# parse the domain expression
|
||||
self.parse(cr, uid, context=context)
|
||||
self.parse()
|
||||
|
||||
# ----------------------------------------
|
||||
# Leafs management
|
||||
@@ -663,7 +663,7 @@ class expression(object):
|
||||
# Parsing
|
||||
# ----------------------------------------
|
||||
|
||||
def parse(self, cr, uid, context):
|
||||
def parse(self):
|
||||
""" Transform the leaves of the expression
|
||||
|
||||
The principle is to pop elements from a leaf stack one at a time.
|
||||
@@ -685,8 +685,9 @@ class expression(object):
|
||||
:var obj comodel: relational model of field (field.comodel)
|
||||
(res_partner.bank_ids -> res.partner.bank)
|
||||
"""
|
||||
cr, uid, context = self.root_model.env.args
|
||||
|
||||
def to_ids(value, comodel, context=None, limit=None):
|
||||
def to_ids(value, comodel):
|
||||
""" Normalize a single id or name, or a list of those, into a list of ids
|
||||
:param {int,long,basestring,list,tuple} value:
|
||||
if int, long -> return [value]
|
||||
@@ -703,11 +704,14 @@ class expression(object):
|
||||
elif isinstance(value, (int, long)):
|
||||
return [value]
|
||||
if names:
|
||||
name_get_list = [name_get[0] for name in names for name_get in comodel.name_search(cr, uid, name, [], 'ilike', context=context, limit=limit)]
|
||||
return list(set(name_get_list))
|
||||
return list({
|
||||
rid
|
||||
for name in names
|
||||
for rid, rname in comodel.name_search(name, [], 'ilike', limit=None)
|
||||
})
|
||||
return list(value)
|
||||
|
||||
def child_of_domain(left, ids, left_model, parent=None, prefix='', context=None):
|
||||
def child_of_domain(left, ids, left_model, parent=None, prefix=''):
|
||||
""" Return a domain implementing the child_of operator for [(left,child_of,ids)],
|
||||
either as a range using the parent_left/right tree lookup fields
|
||||
(when available), or as an expanded [(left,in,child_ids)] """
|
||||
@@ -715,46 +719,43 @@ class expression(object):
|
||||
return FALSE_DOMAIN
|
||||
if left_model._parent_store and (not left_model.pool._init):
|
||||
# TODO: Improve where joins are implemented for many with '.', replace by:
|
||||
# doms += ['&',(prefix+'.parent_left','<',o.parent_right),(prefix+'.parent_left','>=',o.parent_left)]
|
||||
# doms += ['&',(prefix+'.parent_left','<',rec.parent_right),(prefix+'.parent_left','>=',rec.parent_left)]
|
||||
doms = []
|
||||
for o in left_model.browse(cr, uid, ids, context=context):
|
||||
for rec in left_model.browse(ids):
|
||||
if doms:
|
||||
doms.insert(0, OR_OPERATOR)
|
||||
doms += [AND_OPERATOR, ('parent_left', '<', o.parent_right), ('parent_left', '>=', o.parent_left)]
|
||||
doms += [AND_OPERATOR, ('parent_left', '<', rec.parent_right), ('parent_left', '>=', rec.parent_left)]
|
||||
if prefix:
|
||||
return [(left, 'in', left_model.search(cr, uid, doms, context=context))]
|
||||
return [(left, 'in', left_model.search(doms).ids)]
|
||||
return doms
|
||||
else:
|
||||
def recursive_children(ids, model, parent_field):
|
||||
if not ids:
|
||||
return []
|
||||
ids2 = model.search(cr, uid, [(parent_field, 'in', ids)], context=context)
|
||||
return ids + recursive_children(ids2, model, parent_field)
|
||||
return [(left, 'in', recursive_children(ids, left_model, parent or left_model._parent_name))]
|
||||
parent_name = parent or left_model._parent_name
|
||||
child_ids = set(ids)
|
||||
while ids:
|
||||
ids = left_model.search([(parent_name, 'in', ids)]).ids
|
||||
child_ids.update(ids)
|
||||
return [(left, 'in', list(child_ids))]
|
||||
|
||||
def parent_of_domain(left, ids, left_model, parent=None, prefix='', context=None):
|
||||
def parent_of_domain(left, ids, left_model, parent=None, prefix=''):
|
||||
""" Return a domain implementing the parent_of operator for [(left,parent_of,ids)],
|
||||
either as a range using the parent_left/right tree lookup fields
|
||||
(when available), or as an expanded [(left,in,parent_ids)] """
|
||||
if left_model._parent_store and (not left_model.pool._init):
|
||||
doms = []
|
||||
for node in left_model.browse(cr, uid, ids, context=context):
|
||||
for rec in left_model.browse(ids):
|
||||
if doms:
|
||||
doms.insert(0, OR_OPERATOR)
|
||||
doms += [AND_OPERATOR, ('parent_right', '>', node.parent_left), ('parent_left', '<=', node.parent_left)]
|
||||
doms += [AND_OPERATOR, ('parent_right', '>', rec.parent_left), ('parent_left', '<=', rec.parent_left)]
|
||||
if prefix:
|
||||
return [(left, 'in', left_model.search(cr, uid, doms, context=context))]
|
||||
return [(left, 'in', left_model.search(doms).ids)]
|
||||
return doms
|
||||
else:
|
||||
def get_parent_ids(record, parent_field):
|
||||
ids = set([record.id])
|
||||
while record[parent_field]:
|
||||
record = record[parent_field]
|
||||
ids.add(record.id)
|
||||
return ids
|
||||
parent_name = parent or left_model._parent_name
|
||||
parent_ids = set()
|
||||
for node in left_model.browse(cr, uid, ids, context=context):
|
||||
parent_ids |= get_parent_ids(node, parent or left_model._parent_name)
|
||||
for record in left_model.browse(ids):
|
||||
while record:
|
||||
parent_ids.add(record.id)
|
||||
record = record[parent_name]
|
||||
return [(left, 'in', list(parent_ids))]
|
||||
|
||||
HIERARCHY_FUNCS = {'child_of': child_of_domain,
|
||||
@@ -794,8 +795,8 @@ class expression(object):
|
||||
|
||||
model = leaf.model
|
||||
field = model._fields.get(path[0])
|
||||
column = model._columns.get(path[0])
|
||||
comodel = model.pool.get(getattr(field, 'comodel_name', None))
|
||||
column = getattr(field, 'column', None)
|
||||
comodel = model.env.get(getattr(field, 'comodel_name', None))
|
||||
|
||||
# ----------------------------------------
|
||||
# SIMPLE CASE
|
||||
@@ -818,16 +819,20 @@ class expression(object):
|
||||
# -> else: crash
|
||||
# ----------------------------------------
|
||||
|
||||
elif not column and path[0] in model._inherit_fields:
|
||||
elif not field:
|
||||
raise ValueError("Invalid field %r in leaf %r" % (left, str(leaf)))
|
||||
|
||||
elif field.inherited:
|
||||
# comments about inherits'd fields
|
||||
# { 'field_name': ('parent_model', 'm2o_field_to_reach_parent',
|
||||
# field_column_obj, origina_parent_model), ... }
|
||||
next_model = model.pool[model._inherit_fields[path[0]][0]]
|
||||
leaf.add_join_context(next_model, model._inherits[next_model._name], 'id', model._inherits[next_model._name])
|
||||
parent_model = model.env[field.related_field.model_name]
|
||||
parent_fname = model._inherits[parent_model._name]
|
||||
leaf.add_join_context(parent_model, parent_fname, 'id', parent_fname)
|
||||
push(leaf)
|
||||
|
||||
elif left == 'id' and operator in HIERARCHY_FUNCS:
|
||||
ids2 = to_ids(right, model, context)
|
||||
ids2 = to_ids(right, model)
|
||||
dom = HIERARCHY_FUNCS[operator](left, ids2, model)
|
||||
for dom_leaf in reversed(dom):
|
||||
new_leaf = create_substitution_leaf(leaf, dom_leaf, model)
|
||||
@@ -836,9 +841,6 @@ class expression(object):
|
||||
elif not column and path[0] in MAGIC_COLUMNS:
|
||||
push_result(leaf)
|
||||
|
||||
elif not field:
|
||||
raise ValueError("Invalid field %r in leaf %r" % (left, str(leaf)))
|
||||
|
||||
# ----------------------------------------
|
||||
# PATH SPOTTED
|
||||
# -> many2one or one2many with _auto_join:
|
||||
@@ -860,7 +862,7 @@ class expression(object):
|
||||
elif len(path) > 1 and column and column._type == 'one2many' and column._auto_join:
|
||||
# res_partner.id = res_partner__bank_ids.partner_id
|
||||
leaf.add_join_context(comodel, 'id', column._fields_id, path[0])
|
||||
domain = column._domain(model) if callable(column._domain) else column._domain
|
||||
domain = field.domain(model) if callable(field.domain) else field.domain
|
||||
push(create_substitution_leaf(leaf, (path[1], operator, right), comodel))
|
||||
if domain:
|
||||
domain = normalize_domain(domain)
|
||||
@@ -872,14 +874,14 @@ class expression(object):
|
||||
raise NotImplementedError('_auto_join attribute not supported on many2many column %s' % left)
|
||||
|
||||
elif len(path) > 1 and column and column._type == 'many2one':
|
||||
right_ids = comodel.search(cr, uid, [(path[1], operator, right)], context=dict(context, active_test=False))
|
||||
right_ids = comodel.with_context(active_test=False).search([('.'.join(path[1:]), operator, right)]).ids
|
||||
leaf.leaf = (path[0], 'in', right_ids)
|
||||
push(leaf)
|
||||
|
||||
# Making search easier when there is a left operand as column.o2m or column.m2m
|
||||
elif len(path) > 1 and column and column._type in ['many2many', 'one2many']:
|
||||
right_ids = comodel.search(cr, uid, [(path[1], operator, right)], context=context)
|
||||
table_ids = model.search(cr, uid, [(path[0], 'in', right_ids)], context=dict(context, active_test=False))
|
||||
right_ids = comodel.search([('.'.join(path[1:]), operator, right)]).ids
|
||||
table_ids = model.with_context(active_test=False).search([(path[0], 'in', right_ids)]).ids
|
||||
leaf.leaf = ('id', 'in', table_ids)
|
||||
push(leaf)
|
||||
|
||||
@@ -895,12 +897,9 @@ class expression(object):
|
||||
else:
|
||||
# Let the field generate a domain.
|
||||
if len(path) > 1:
|
||||
right = comodel.search(
|
||||
cr, uid, [(path[1], operator, right)],
|
||||
context=context)
|
||||
right = comodel.search([('.'.join(path[1:]), operator, right)]).ids
|
||||
operator = 'in'
|
||||
recs = model.browse(cr, uid, [], context=context)
|
||||
domain = field.determine_domain(recs, operator, right)
|
||||
domain = field.determine_domain(model, operator, right)
|
||||
|
||||
if not domain:
|
||||
leaf.leaf = TRUE_LEAF
|
||||
@@ -928,7 +927,7 @@ class expression(object):
|
||||
# ignore it: generate a dummy leaf
|
||||
fct_domain = []
|
||||
else:
|
||||
fct_domain = column.search(cr, uid, model, left, [leaf.leaf], context=context)
|
||||
fct_domain = column.search(cr, uid, model._model, left, [leaf.leaf], context=context)
|
||||
|
||||
if not fct_domain:
|
||||
leaf.leaf = TRUE_LEAF
|
||||
@@ -947,7 +946,7 @@ class expression(object):
|
||||
|
||||
# Applying recursivity on field(one2many)
|
||||
elif column._type == 'one2many' and operator in HIERARCHY_FUNCS:
|
||||
ids2 = to_ids(right, comodel, context)
|
||||
ids2 = to_ids(right, comodel)
|
||||
if column._obj != model._name:
|
||||
dom = HIERARCHY_FUNCS[operator](left, ids2, comodel, prefix=column._obj)
|
||||
else:
|
||||
@@ -961,7 +960,7 @@ class expression(object):
|
||||
if right is not False:
|
||||
if isinstance(right, basestring):
|
||||
op = {'!=': '=', 'not like': 'like', 'not ilike': 'ilike'}.get(operator, operator)
|
||||
ids2 = [x[0] for x in comodel.name_search(cr, uid, right, [], op, context=context, limit=None)]
|
||||
ids2 = [x[0] for x in comodel.name_search(right, [], op, limit=None)]
|
||||
if ids2:
|
||||
operator = 'not in' if operator in NEGATIVE_TERM_OPERATORS else 'in'
|
||||
elif isinstance(right, collections.Iterable):
|
||||
@@ -979,7 +978,7 @@ class expression(object):
|
||||
if comodel._fields[column._fields_id].store:
|
||||
ids1 = select_from_where(cr, column._fields_id, comodel._table, 'id', ids2, operator)
|
||||
else:
|
||||
recs = comodel.browse(cr, SUPERUSER_ID, ids2, {'prefetch_fields': False})
|
||||
recs = comodel.browse(ids2).sudo().with_context(prefetch_fields=False)
|
||||
ids1 = recs.mapped(column._fields_id).ids
|
||||
if ids1:
|
||||
call_null = False
|
||||
@@ -1003,16 +1002,16 @@ class expression(object):
|
||||
return ids
|
||||
return select_from_where(cr, rel_id1, rel_table, rel_id2, ids, operator)
|
||||
|
||||
ids2 = to_ids(right, comodel, context)
|
||||
ids2 = to_ids(right, comodel)
|
||||
dom = HIERARCHY_FUNCS[operator]('id', ids2, comodel)
|
||||
ids2 = comodel.search(cr, uid, dom, context=context)
|
||||
ids2 = comodel.search(dom).ids
|
||||
push(create_substitution_leaf(leaf, ('id', 'in', _rec_convert(ids2)), model))
|
||||
else:
|
||||
call_null_m2m = True
|
||||
if right is not False:
|
||||
if isinstance(right, basestring):
|
||||
op = {'!=': '=', 'not like': 'like', 'not ilike': 'ilike'}.get(operator, operator)
|
||||
res_ids = [x[0] for x in comodel.name_search(cr, uid, right, [], op, context=context, limit=None)]
|
||||
res_ids = [x[0] for x in comodel.name_search(right, [], op, limit=None)]
|
||||
if res_ids:
|
||||
operator = 'not in' if operator in NEGATIVE_TERM_OPERATORS else 'in'
|
||||
else:
|
||||
@@ -1038,7 +1037,7 @@ class expression(object):
|
||||
|
||||
elif column._type == 'many2one':
|
||||
if operator in HIERARCHY_FUNCS:
|
||||
ids2 = to_ids(right, comodel, context)
|
||||
ids2 = to_ids(right, comodel)
|
||||
if column._obj != model._name:
|
||||
dom = HIERARCHY_FUNCS[operator](left, ids2, comodel, prefix=column._obj)
|
||||
else:
|
||||
@@ -1046,11 +1045,7 @@ class expression(object):
|
||||
for dom_leaf in reversed(dom):
|
||||
push(create_substitution_leaf(leaf, dom_leaf, model))
|
||||
else:
|
||||
def _get_expression(comodel, cr, uid, left, right, operator, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
c = context.copy()
|
||||
c['active_test'] = False
|
||||
def _get_expression(comodel, left, right, operator):
|
||||
#Special treatment to ill-formed domains
|
||||
operator = (operator in ['<', '>', '<=', '>=']) and 'in' or operator
|
||||
|
||||
@@ -1061,14 +1056,14 @@ class expression(object):
|
||||
operator = dict_op[operator]
|
||||
elif isinstance(right, list) and operator in ['!=', '=']: # for domain (FIELD,'=',['value1','value2'])
|
||||
operator = dict_op[operator]
|
||||
res_ids = [x[0] for x in comodel.name_search(cr, uid, right, [], operator, limit=None, context=c)]
|
||||
res_ids = [x[0] for x in comodel.with_context(active_test=False).name_search(right, [], operator, limit=None)]
|
||||
if operator in NEGATIVE_TERM_OPERATORS:
|
||||
res_ids.append(False) # TODO this should not be appended if False was in 'right'
|
||||
return left, 'in', res_ids
|
||||
# resolve string-based m2o criterion into IDs
|
||||
if isinstance(right, basestring) or \
|
||||
right and isinstance(right, (tuple, list)) and all(isinstance(item, basestring) for item in right):
|
||||
push(create_substitution_leaf(leaf, _get_expression(comodel, cr, uid, left, right, operator, context=context), model))
|
||||
push(create_substitution_leaf(leaf, _get_expression(comodel, left, right, operator), model))
|
||||
else:
|
||||
# right == [] or right == False and all other cases are handled by __leaf_to_sql()
|
||||
push_result(leaf)
|
||||
@@ -1140,7 +1135,7 @@ class expression(object):
|
||||
|
||||
params = (
|
||||
model._name + ',' + left,
|
||||
context.get('lang') or 'en_US',
|
||||
model.env.lang or 'en_US',
|
||||
'model',
|
||||
right,
|
||||
)
|
||||
|
||||
@@ -86,8 +86,6 @@ class _column(object):
|
||||
'translate',
|
||||
'select',
|
||||
'manual',
|
||||
'write',
|
||||
'read',
|
||||
'selectable',
|
||||
'group_operator',
|
||||
'groups', # CSV list of ext IDs of groups
|
||||
@@ -120,8 +118,6 @@ class _column(object):
|
||||
args['translate'] = translate
|
||||
args['select'] = select
|
||||
args['manual'] = manual
|
||||
args['write'] = args.get('write', False)
|
||||
args['read'] = args.get('read', False)
|
||||
args['selectable'] = args.get('selectable', True)
|
||||
args['group_operator'] = args.get('group_operator', None)
|
||||
args['groups'] = args.get('groups', None)
|
||||
|
||||
+2
-2
@@ -240,10 +240,10 @@ class Cursor(object):
|
||||
self.sql_into_log[res_into.group(1)][1] += delay
|
||||
return res
|
||||
|
||||
def split_for_in_conditions(self, ids):
|
||||
def split_for_in_conditions(self, ids, size=None):
|
||||
"""Split a list of identifiers into one or more smaller tuples
|
||||
safe for IN conditions, after uniquifying them."""
|
||||
return tools.misc.split_every(self.IN_MAX, ids)
|
||||
return tools.misc.split_every(size or self.IN_MAX, ids)
|
||||
|
||||
def print_log(self):
|
||||
global sql_counter
|
||||
|
||||
@@ -883,19 +883,6 @@ def convert_csv_import(cr, module, fname, csvcontent, idref=None, mode='init',
|
||||
input = cStringIO.StringIO(csvcontent) #FIXME
|
||||
reader = csv.reader(input, quotechar='"', delimiter=',')
|
||||
fields = reader.next()
|
||||
fname_partial = ""
|
||||
if config.get('import_partial'):
|
||||
fname_partial = module + '/'+ fname
|
||||
if not os.path.isfile(config.get('import_partial')):
|
||||
pickle.dump({}, file(config.get('import_partial'),'w+'))
|
||||
else:
|
||||
data = pickle.load(file(config.get('import_partial')))
|
||||
if fname_partial in data:
|
||||
if not data[fname_partial]:
|
||||
return
|
||||
else:
|
||||
for i in range(data[fname_partial]):
|
||||
reader.next()
|
||||
|
||||
if not (mode == 'init' or 'id' in fields):
|
||||
_logger.error("Import specification does not contain 'id' and we are in init mode, Cannot continue.")
|
||||
@@ -912,15 +899,16 @@ def convert_csv_import(cr, module, fname, csvcontent, idref=None, mode='init',
|
||||
_logger.error("Cannot import the line: %s", line)
|
||||
|
||||
registry = openerp.registry(cr.dbname)
|
||||
result, rows, warning_msg, dummy = registry[model].import_data(cr, uid, fields, datas,mode, module, noupdate, filename=fname_partial)
|
||||
if result < 0:
|
||||
context = {
|
||||
'mode': mode,
|
||||
'module': module,
|
||||
'noupdate': noupdate,
|
||||
}
|
||||
result = registry[model].load(cr, uid, fields, datas, context=context)
|
||||
if any(msg['type'] == 'error' for msg in result['messages']):
|
||||
# Report failed import and abort module install
|
||||
warning_msg = "\n".join(msg['message'] for msg in result['messages'])
|
||||
raise Exception(_('Module loading %s failed: file %s could not be processed:\n %s') % (module, fname, warning_msg))
|
||||
if config.get('import_partial'):
|
||||
data = pickle.load(file(config.get('import_partial')))
|
||||
data[fname_partial] = 0
|
||||
pickle.dump(data, file(config.get('import_partial'),'wb'))
|
||||
cr.commit()
|
||||
|
||||
def convert_xml_import(cr, module, xmlfile, idref=None, mode='init', noupdate=False, report=None):
|
||||
doc = etree.parse(xmlfile)
|
||||
|
||||
Reference in New Issue
Block a user