summaryrefslogtreecommitdiff
path: root/odoo/addons
diff options
context:
space:
mode:
authorRaphael Collet <rco@odoo.com>2019-04-11 08:21:54 +0000
committerRaphael Collet <rco@odoo.com>2019-05-23 05:47:11 +0000
commitd0bb596f1d51b72a6aa20d56eef3e65aefbd09b2 (patch)
tree8d56a32b6f0eea496d0e9af26a4349e8979dfc37 /odoo/addons
parent60be9f2c098cfead8a5e3fd23453b7e93243e0c5 (diff)
[IMP] fields: x2many fields of new records are new records
The value of an x2many field on a new record are new records, possibly related to a real record, called its "origin".
Diffstat (limited to 'odoo/addons')
-rw-r--r--odoo/addons/base/models/ir_model.py2
-rw-r--r--odoo/addons/base/models/res_partner.py8
-rw-r--r--odoo/addons/base/models/res_users.py14
-rw-r--r--odoo/addons/test_new_api/models.py1
-rw-r--r--odoo/addons/test_new_api/tests/test_new_fields.py158
-rw-r--r--odoo/addons/test_performance/tests/test_performance.py12
6 files changed, 180 insertions, 15 deletions
diff --git a/odoo/addons/base/models/ir_model.py b/odoo/addons/base/models/ir_model.py
index 45af7c4fe4e..117d3be6d63 100644
--- a/odoo/addons/base/models/ir_model.py
+++ b/odoo/addons/base/models/ir_model.py
@@ -535,7 +535,7 @@ class IrModelFields(models.Model):
# check whether other fields use the same table
others = self.search([('ttype', '=', 'many2many'),
('relation_table', '=', self.relation_table),
- ('id', 'not in', self._origin.ids)])
+ ('id', 'not in', self.ids)])
if others:
for other in others:
if (other.model, other.relation) == (self.relation, self.model):
diff --git a/odoo/addons/base/models/res_partner.py b/odoo/addons/base/models/res_partner.py
index 0cf10f1ad55..fb5f0906a32 100644
--- a/odoo/addons/base/models/res_partner.py
+++ b/odoo/addons/base/models/res_partner.py
@@ -266,10 +266,8 @@ class Partner(models.Model):
@api.depends('vat')
def _compute_same_vat_partner_id(self):
for partner in self:
- partner_id = partner.id
- if isinstance(partner_id, models.NewId):
- # deal with onchange(), which is always called on a single record
- partner_id = self._origin.id
+ # use _origin to deal with onchange()
+ partner_id = partner._origin.id
domain = [('vat', '=', partner.vat)]
if partner_id:
domain += [('id', '!=', partner_id), '!', ('id', 'child_of', partner_id)]
@@ -379,7 +377,7 @@ class Partner(models.Model):
if not self.parent_id:
return
result = {}
- partner = getattr(self, '_origin', self)
+ partner = self._origin
if partner.parent_id and partner.parent_id != self.parent_id:
result['warning'] = {
'title': _('Warning'),
diff --git a/odoo/addons/base/models/res_users.py b/odoo/addons/base/models/res_users.py
index 256e89cbb7d..f55b99bfdeb 100644
--- a/odoo/addons/base/models/res_users.py
+++ b/odoo/addons/base/models/res_users.py
@@ -962,15 +962,15 @@ class UsersImplied(models.Model):
if 'groups_id' in values:
# complete 'groups_id' with implied groups
user = self.new(values)
+ gs = user.groups_id._origin
group_public = self.env.ref('base.group_public', raise_if_not_found=False)
group_portal = self.env.ref('base.group_portal', raise_if_not_found=False)
- if group_public and group_public in user.groups_id:
- gs = self.env.ref('base.group_public') | self.env.ref('base.group_public').trans_implied_ids
- elif group_portal and group_portal in user.groups_id:
- gs = self.env.ref('base.group_portal') | self.env.ref('base.group_portal').trans_implied_ids
- else:
- gs = user.groups_id | user.groups_id.trans_implied_ids
- values['groups_id'] = type(self).groups_id.convert_to_write(gs, user.groups_id)
+ if group_public and group_public in gs:
+ gs = group_public
+ elif group_portal and group_portal in gs:
+ gs = group_portal
+ gs = gs | gs.trans_implied_ids
+ values['groups_id'] = type(self).groups_id.convert_to_write(gs, user)
return super(UsersImplied, self).create(vals_list)
@api.multi
diff --git a/odoo/addons/test_new_api/models.py b/odoo/addons/test_new_api/models.py
index e6ad747097d..31e8e9eeeb1 100644
--- a/odoo/addons/test_new_api/models.py
+++ b/odoo/addons/test_new_api/models.py
@@ -214,6 +214,7 @@ class Multi(models.Model):
name = fields.Char(related='partner.name', readonly=True)
partner = fields.Many2one('res.partner')
lines = fields.One2many('test_new_api.multi.line', 'multi')
+ partners = fields.One2many(related='partner.child_ids')
@api.onchange('name')
def _onchange_name(self):
diff --git a/odoo/addons/test_new_api/tests/test_new_fields.py b/odoo/addons/test_new_api/tests/test_new_fields.py
index de9bc11dcaf..c83704dbbe3 100644
--- a/odoo/addons/test_new_api/tests/test_new_fields.py
+++ b/odoo/addons/test_new_api/tests/test_new_fields.py
@@ -956,10 +956,154 @@ class TestFields(common.TransactionCase):
discussion = discussion.with_context(default_categories=[(4, cat1.id)])
# no value gives the default value
new_disc = discussion.new({'name': "Foo"})
- self.assertEqual(new_disc.categories, cat1)
+ self.assertEqual(new_disc.categories._origin, cat1)
# value is combined with default value
new_disc = discussion.new({'name': "Foo", 'categories': [(4, cat2.id)]})
- self.assertEqual(new_disc.categories, cat1 + cat2)
+ self.assertEqual(new_disc.categories._origin, cat1 + cat2)
+
+ def test_40_new_fields(self):
+ """ Test new records with relational fields. """
+ # create a new discussion with all kinds of relational fields
+ msg0 = self.env['test_new_api.message'].create({'body': "XXX"})
+ msg1 = self.env['test_new_api.message'].create({'body': "WWW"})
+ cat0 = self.env['test_new_api.category'].create({'name': 'AAA'})
+ cat1 = self.env['test_new_api.category'].create({'name': 'DDD'})
+ new_disc = self.env['test_new_api.discussion'].new({
+ 'name': "Stuff",
+ 'moderator': self.env.uid,
+ 'messages': [
+ (4, msg0.id),
+ (4, msg1.id), (1, msg1.id, {'body': "YYY"}),
+ (0, 0, {'body': "ZZZ"})
+ ],
+ 'categories': [
+ (4, cat0.id),
+ (4, cat1.id), (1, cat1.id, {'name': "BBB"}),
+ (0, 0, {'name': "CCC"})
+ ],
+ })
+ self.assertFalse(new_disc.id)
+
+ # many2one field values are actual records
+ self.assertEqual(new_disc.moderator.id, self.env.uid)
+
+ # x2many fields values are new records
+ new_msg0, new_msg1, new_msg2 = new_disc.messages
+ self.assertFalse(new_msg0.id)
+ self.assertFalse(new_msg1.id)
+ self.assertFalse(new_msg2.id)
+
+ new_cat0, new_cat1, new_cat2 = new_disc.categories
+ self.assertFalse(new_cat0.id)
+ self.assertFalse(new_cat1.id)
+ self.assertFalse(new_cat2.id)
+
+ # the x2many has its inverse field set
+ self.assertEqual(new_msg0.discussion, new_disc)
+ self.assertEqual(new_msg1.discussion, new_disc)
+ self.assertEqual(new_msg2.discussion, new_disc)
+
+ self.assertFalse(msg0.discussion)
+ self.assertFalse(msg1.discussion)
+
+ self.assertEqual(new_cat0.discussions, new_disc) # add other discussions
+ self.assertEqual(new_cat1.discussions, new_disc)
+ self.assertEqual(new_cat2.discussions, new_disc)
+
+ self.assertNotIn(new_disc, cat0.discussions)
+ self.assertNotIn(new_disc, cat1.discussions)
+
+ # new lines are connected to their origin
+ self.assertEqual(new_msg0._origin, msg0)
+ self.assertEqual(new_msg1._origin, msg1)
+ self.assertFalse(new_msg2._origin)
+
+ self.assertEqual(new_cat0._origin, cat0)
+ self.assertEqual(new_cat1._origin, cat1)
+ self.assertFalse(new_cat2._origin)
+
+ # the field values are either specific, or the same as the origin
+ self.assertEqual(new_msg0.body, "XXX")
+ self.assertEqual(new_msg1.body, "YYY")
+ self.assertEqual(new_msg2.body, "ZZZ")
+
+ self.assertEqual(msg0.body, "XXX")
+ self.assertEqual(msg1.body, "WWW")
+
+ self.assertEqual(new_cat0.name, "AAA")
+ self.assertEqual(new_cat1.name, "BBB")
+ self.assertEqual(new_cat2.name, "CCC")
+
+ self.assertEqual(cat0.name, "AAA")
+ self.assertEqual(cat1.name, "DDD")
+
+ # special case for many2one fields that define _inherits
+ new_email = self.env['test_new_api.emailmessage'].new({'body': "XXX"})
+ self.assertFalse(new_email.id)
+ self.assertTrue(new_email.message)
+ self.assertFalse(new_email.message.id)
+ self.assertEqual(new_email.body, "XXX")
+
+ new_email = self.env['test_new_api.emailmessage'].new({'message': msg0.id})
+ self.assertFalse(new_email.id)
+ self.assertFalse(new_email._origin)
+ self.assertFalse(new_email.message.id)
+ self.assertEqual(new_email.message._origin, msg0)
+ self.assertEqual(new_email.body, "XXX")
+
+ def test_40_new_ref_origin(self):
+ """ Test the behavior of new records with ref/origin. """
+ Discussion = self.env['test_new_api.discussion']
+ new = Discussion.new
+
+ # new records with identical/different refs
+ xs = new() + new(ref='a') + new(ref='b') + new(ref='b')
+ self.assertEqual([x == y for x in xs for y in xs], [
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 1,
+ 0, 0, 1, 1,
+ ])
+ for x in xs:
+ self.assertFalse(x._origin)
+
+ # new records with identical/different origins
+ a, b = Discussion.create([{'name': "A"}, {'name': "B"}])
+ xs = new() + new(origin=a) + new(origin=b) + new(origin=b)
+ self.assertEqual([x == y for x in xs for y in xs], [
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 1,
+ 0, 0, 1, 1,
+ ])
+ self.assertFalse(xs[0]._origin)
+ self.assertEqual(xs[1]._origin, a)
+ self.assertEqual(xs[2]._origin, b)
+ self.assertEqual(xs[3]._origin, b)
+ self.assertEqual(xs._origin, a + b + b)
+ self.assertEqual(xs._origin._origin, a + b + b)
+
+ # new records with refs and origins
+ x1 = new(ref='a')
+ x2 = new(origin=b)
+ self.assertNotEqual(x1, x2)
+
+ # new discussion based on existing discussion
+ disc = self.env.ref('test_new_api.discussion_0')
+ new_disc = disc.new(origin=disc)
+ self.assertFalse(new_disc.id)
+ self.assertEqual(new_disc._origin, disc)
+ self.assertEqual(new_disc.name, disc.name)
+ # many2one field
+ self.assertEqual(new_disc.moderator, disc.moderator)
+ # one2many field
+ self.assertTrue(new_disc.messages)
+ self.assertNotEqual(new_disc.messages, disc.messages)
+ self.assertEqual(new_disc.messages._origin, disc.messages)
+ # many2many field
+ self.assertTrue(new_disc.participants)
+ self.assertNotEqual(new_disc.participants, disc.participants)
+ self.assertEqual(new_disc.participants._origin, disc.participants)
@mute_logger('odoo.addons.base.models.ir_model')
def test_41_new_related(self):
@@ -1005,6 +1149,16 @@ class TestFields(common.TransactionCase):
self.assertNotEqual(message.sudo().env, message.env)
self.assertEqual(message.discussion_name, discussion.name)
+ def test_43_new_related(self):
+ """ test the behavior of one2many related fields """
+ partner = self.env['res.partner'].create({
+ 'name': 'Foo',
+ 'child_ids': [(0, 0, {'name': 'Bar'})],
+ })
+ multi = self.env['test_new_api.multi'].new()
+ multi.partner = partner
+ self.assertEqual(multi.partners.mapped('name'), ['Bar'])
+
def test_50_defaults(self):
""" test default values. """
fields = ['discussion', 'body', 'author', 'size']
diff --git a/odoo/addons/test_performance/tests/test_performance.py b/odoo/addons/test_performance/tests/test_performance.py
index 7ea5fbcdadc..c05a62cc4d9 100644
--- a/odoo/addons/test_performance/tests/test_performance.py
+++ b/odoo/addons/test_performance/tests/test_performance.py
@@ -355,14 +355,26 @@ class TestPerformance(TransactionCase):
)
records = self.env['test_performance.base'].search([])
self.assertEqual(len(records), 1280)
+
# should only cause 2 queries thanks to prefetching
with self.assertQueryCount(__system__=2, demo=2):
records.mapped('value')
+
records.invalidate_cache(['value'])
+ with self.assertQueryCount(__system__=2, demo=2):
+ records.mapped('value')
+ records.invalidate_cache(['value'])
+ with self.assertQueryCount(__system__=2, demo=2):
+ new_recs = records.browse(records.new(origin=record).id for record in records)
+ new_recs.mapped('value')
+
+ records.invalidate_cache(['value'])
with self.assertQueryCount(__system__=2, demo=2):
with self.env.do_in_onchange():
records.mapped('value')
+
+ # clean up after each pass
self.env.cr.execute(
'delete from test_performance_base where id not in %s',
(tuple(initial_records.ids),)