[IMP] test_new_api: add performance test for modified.
Part-of: odoo/odoo#99274
This commit is contained in:
@@ -1658,3 +1658,42 @@ class Prefetch(models.Model):
|
||||
ron = fields.Float('Ron Weasley', prefetch='Harry Potter')
|
||||
hansel = fields.Integer('Hansel', prefetch="Hansel and Gretel")
|
||||
gretel = fields.Char('Gretel', prefetch="Hansel and Gretel")
|
||||
|
||||
|
||||
class Modified(models.Model):
|
||||
_name = 'test_new_api.modified'
|
||||
_description = 'A model to check modified trigger'
|
||||
|
||||
name = fields.Char('Name')
|
||||
line_ids = fields.One2many('test_new_api.modified.line', 'modified_id')
|
||||
total_quantity = fields.Integer(compute='_compute_total_quantity')
|
||||
|
||||
@api.depends('line_ids.quantity')
|
||||
def _compute_total_quantity(self):
|
||||
for rec in self:
|
||||
rec.total_quantity = sum(rec.line_ids.mapped('quantity'))
|
||||
|
||||
|
||||
class ModifiedLine(models.Model):
|
||||
_name = 'test_new_api.modified.line'
|
||||
_description = 'A model to check modified trigger'
|
||||
|
||||
modified_id = fields.Many2one('test_new_api.modified')
|
||||
modified_name = fields.Char(related="modified_id.name")
|
||||
quantity = fields.Integer()
|
||||
price = fields.Float()
|
||||
total_price = fields.Float(compute='_compute_total_quantity', recursive=True)
|
||||
total_price_quantity = fields.Float(compute='_compute_total_price_quantity')
|
||||
|
||||
parent_id = fields.Many2one('test_new_api.modified.line')
|
||||
child_ids = fields.One2many('test_new_api.modified.line', 'parent_id')
|
||||
|
||||
@api.depends('price', 'child_ids.total_price', 'child_ids.price')
|
||||
def _compute_total_quantity(self):
|
||||
for rec in self:
|
||||
rec.total_price = sum(rec.child_ids.mapped('total_price')) + rec.price
|
||||
|
||||
@api.depends('total_price', 'quantity')
|
||||
def _compute_total_price_quantity(self):
|
||||
for rec in self:
|
||||
rec.total_price_quantity = rec.total_price * rec.quantity
|
||||
|
||||
@@ -94,3 +94,5 @@ access_test_new_api_precompute_readonly,access_test_new_api_precompute_readonly,
|
||||
access_test_new_api_precompute_required,access_test_new_api_precompute_required,model_test_new_api_precompute_required,,1,0,0,0
|
||||
access_test_new_api_precompute_monetary,access_test_new_api_precompute_monetary,model_test_new_api_precompute_monetary,,1,0,0,0
|
||||
access_test_new_api_prefetch,access_test_new_api_prefetch,model_test_new_api_prefetch,,1,0,0,0
|
||||
access_test_new_api_modified,access_test_new_api_modified,model_test_new_api_modified,base.group_user,1,1,1,1
|
||||
access_test_new_api_modified_line,access_test_new_api_modified_line,model_test_new_api_modified_line,base.group_user,1,1,1,1
|
||||
|
||||
|
@@ -4085,3 +4085,119 @@ class TestPrecompute(common.TransactionCase):
|
||||
]
|
||||
with self.assertQueries(QUERIES):
|
||||
model.create({})
|
||||
|
||||
|
||||
class TestModifiedPerformance(common.TransactionCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.Modified = cls.env['test_new_api.modified']
|
||||
cls.ModifiedLine = cls.env['test_new_api.modified.line']
|
||||
cls.modified_a = cls.Modified.create({
|
||||
'name': 'Test',
|
||||
})
|
||||
cls.modified_line_a = cls.ModifiedLine.create({
|
||||
'modified_id': cls.modified_a.id,
|
||||
'quantity': 5,
|
||||
'price': 1,
|
||||
})
|
||||
cls.modified_line_a_child = cls.ModifiedLine.create({
|
||||
'modified_id': cls.modified_a.id,
|
||||
'quantity': 5,
|
||||
'price': 2,
|
||||
'parent_id': cls.modified_line_a.id,
|
||||
})
|
||||
cls.modified_line_a_child_child = cls.ModifiedLine.create({
|
||||
'modified_id': cls.modified_a.id,
|
||||
'quantity': 5,
|
||||
'price': 3,
|
||||
'parent_id': cls.modified_line_a_child.id,
|
||||
})
|
||||
cls.env.invalidate_all() # Clean the cache
|
||||
|
||||
def test_modified_trigger_related(self):
|
||||
with self.assertQueries(["""
|
||||
SELECT "test_new_api_modified_line".id
|
||||
FROM "test_new_api_modified_line"
|
||||
WHERE ("test_new_api_modified_line"."modified_id" IN %s)
|
||||
ORDER BY "test_new_api_modified_line"."id"
|
||||
""",
|
||||
"""
|
||||
SELECT "test_new_api_modified_line"."id" AS "id", "test_new_api_modified_line"."modified_id" AS "modified_id"
|
||||
FROM "test_new_api_modified_line"
|
||||
WHERE "test_new_api_modified_line".id IN %s
|
||||
"""], flush=False):
|
||||
# Two queries to fetch the `line_ids` in order to invalidate the cache for `modified_name`
|
||||
# (even if the cache is empty for this field)
|
||||
self.modified_a.name = "Other"
|
||||
|
||||
self.assertEqual(self.modified_line_a.modified_name, 'Other') # check
|
||||
|
||||
def test_modified_trigger_no_store_compute(self):
|
||||
with self.assertQueries(["""
|
||||
SELECT "test_new_api_modified_line"."id" AS "id", "test_new_api_modified_line"."modified_id" AS "modified_id",
|
||||
"test_new_api_modified_line"."quantity" AS "quantity", "test_new_api_modified_line"."price" AS "price",
|
||||
"test_new_api_modified_line"."parent_id" AS "parent_id", "test_new_api_modified_line"."create_uid" AS "create_uid",
|
||||
"test_new_api_modified_line"."create_date" AS "create_date", "test_new_api_modified_line"."write_uid" AS "write_uid",
|
||||
"test_new_api_modified_line"."write_date" AS "write_date"
|
||||
FROM "test_new_api_modified_line"
|
||||
WHERE "test_new_api_modified_line".id IN %s
|
||||
"""], flush=False):
|
||||
# One query to fetch `modified_id` in order to invalidate `total_quantity`
|
||||
self.modified_line_a.quantity = 8
|
||||
|
||||
self.assertEqual(self.modified_a.total_quantity, 18)
|
||||
|
||||
def test_modified_trigger_recursive_empty_cache(self):
|
||||
with self.assertQueries(["""
|
||||
SELECT "test_new_api_modified_line"."id" AS "id", "test_new_api_modified_line"."modified_id" AS "modified_id",
|
||||
"test_new_api_modified_line"."quantity" AS "quantity", "test_new_api_modified_line"."price" AS "price",
|
||||
"test_new_api_modified_line"."parent_id" AS "parent_id", "test_new_api_modified_line"."create_uid" AS "create_uid",
|
||||
"test_new_api_modified_line"."create_date" AS "create_date", "test_new_api_modified_line"."write_uid" AS "write_uid",
|
||||
"test_new_api_modified_line"."write_date" AS "write_date"
|
||||
FROM "test_new_api_modified_line"
|
||||
WHERE "test_new_api_modified_line".id IN %s
|
||||
"""], flush=False):
|
||||
# One query to fetch `parent_id` in order to invalidate `total_price` recursively
|
||||
# but because no record has already recompute `total_price` no need to invalidate recursivly
|
||||
self.modified_line_a_child_child.price = 4
|
||||
|
||||
self.assertEqual(self.modified_line_a.total_price, 7)
|
||||
self.assertEqual(self.modified_line_a.total_price_quantity, 35)
|
||||
self.assertEqual(self.modified_line_a_child.total_price, 6)
|
||||
self.assertEqual(self.modified_line_a_child.total_price_quantity, 30)
|
||||
|
||||
def test_modified_trigger_recursive_fill_cache(self):
|
||||
self.assertEqual(self.modified_line_a.total_price, 6)
|
||||
self.assertEqual(self.modified_line_a.total_price_quantity, 30)
|
||||
with self.assertQueryCount(0, flush=False):
|
||||
# No query because the `modified_line_a.total_price` has fetch every data needed
|
||||
self.modified_line_a_child_child.price = 4
|
||||
|
||||
self.assertEqual(self.modified_line_a.total_price_quantity, 35)
|
||||
self.assertEqual(self.modified_line_a.total_price, 7)
|
||||
|
||||
def test_modified_trigger_recursive_partial_invalidate(self):
|
||||
self.assertEqual(self.modified_line_a_child.total_price_quantity, 25)
|
||||
self.modified_line_a_child_child.invalidate_recordset()
|
||||
|
||||
self.modified_line_a_child.price
|
||||
with self.assertQueries(["""
|
||||
SELECT "test_new_api_modified_line"."id" AS "id", "test_new_api_modified_line"."modified_id" AS "modified_id",
|
||||
"test_new_api_modified_line"."quantity" AS "quantity", "test_new_api_modified_line"."price" AS "price",
|
||||
"test_new_api_modified_line"."parent_id" AS "parent_id", "test_new_api_modified_line"."create_uid" AS "create_uid",
|
||||
"test_new_api_modified_line"."create_date" AS "create_date", "test_new_api_modified_line"."write_uid" AS "write_uid",
|
||||
"test_new_api_modified_line"."write_date" AS "write_date"
|
||||
FROM "test_new_api_modified_line"
|
||||
WHERE "test_new_api_modified_line".id IN %s
|
||||
"""] * 2, flush=False):
|
||||
# Two requests:
|
||||
# - one for fetch modified_line_a_child_child data (invalidate just before)
|
||||
# - one because modified_line_a_child.parent_id (invalidate just before because we invalidate inverse in `_invalidate_cache`,
|
||||
# see TODO) -> We should change that
|
||||
self.modified_line_a_child_child.price = 4
|
||||
self.assertEqual(self.modified_line_a_child_child.total_price_quantity, 20)
|
||||
self.assertEqual(self.modified_line_a_child.total_price_quantity, 30)
|
||||
self.assertEqual(self.modified_line_a.total_price_quantity, 35)
|
||||
self.assertEqual(self.modified_line_a.total_price, 7)
|
||||
|
||||
Reference in New Issue
Block a user