summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaphael Collet <rco@odoo.com>2020-05-05 12:55:52 +0000
committerRaphael Collet <rco@odoo.com>2020-05-20 07:56:14 +0000
commit020536142cf9fe9124675ef5c7e46eaf41b9c528 (patch)
tree4a043e8755f59ed89b68ff85a682ea3f573bc054
parente3be55d3f6d3ee8145e182fd54185115f6699bb6 (diff)
[IMP] core: better tests on queries made by search
-rw-r--r--odoo/addons/base/tests/test_expression.py782
-rw-r--r--odoo/osv/expression.py19
-rw-r--r--odoo/tests/common.py32
3 files changed, 624 insertions, 209 deletions
diff --git a/odoo/addons/base/tests/test_expression.py b/odoo/addons/base/tests/test_expression.py
index 2a7df364cc6..973574655ec 100644
--- a/odoo/addons/base/tests/test_expression.py
+++ b/odoo/addons/base/tests/test_expression.py
@@ -777,27 +777,7 @@ class TestExpression2(TransactionCase):
class TestAutoJoin(TransactionCase):
- def setUp(self):
- super(TestAutoJoin, self).setUp()
- # Mock BaseModel._where_calc(), to be able to proceed to some tests about generated expression
- self._reinit_mock()
- BaseModel_where_calc = BaseModel._where_calc
-
- def _where_calc(model, *args, **kwargs):
- """ Mock `_where_calc` to be able to test its results. Store them
- into some internal variable for latter processing. """
- query = BaseModel_where_calc(model, *args, **kwargs)
- self.query_list.append(query)
- return query
-
- self.patch(BaseModel, '_where_calc', _where_calc)
-
- def _reinit_mock(self):
- self.query_list = []
-
def test_auto_join(self):
- unaccent = expression.get_unaccent_wrapper(self.cr)
-
# Get models
partner_obj = self.env['res.partner']
state_obj = self.env['res.country.state']
@@ -819,6 +799,7 @@ class TestAutoJoin(TransactionCase):
# Create demo data: partners and bank object
p_a = partner_obj.create({'name': 'test__A', 'state_id': states[0].id})
p_b = partner_obj.create({'name': 'test__B', 'state_id': states[1].id})
+ p_c = partner_obj.create({'name': 'test__C', 'state_id': False})
p_aa = partner_obj.create({'name': 'test__AA', 'parent_id': p_a.id, 'state_id': states[0].id})
p_ab = partner_obj.create({'name': 'test__AB', 'parent_id': p_a.id, 'state_id': states[1].id})
p_ba = partner_obj.create({'name': 'test__BA', 'parent_id': p_b.id, 'state_id': states[0].id})
@@ -841,111 +822,46 @@ class TestAutoJoin(TransactionCase):
name_test = '12'
# Do: one2many without _auto_join
- self._reinit_mock()
partners = partner_obj.search([('bank_ids.sanitized_acc_number', 'like', name_test)])
- # Test result
self.assertEqual(partners, p_aa,
"_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..'): incorrect result")
- # Test produced queries
- self.assertEqual(len(self.query_list), 2,
- "_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') should produce 2 queries (1 in res_partner_bank, 1 on res_partner)")
- sql_query = self.query_list[0].get_sql()
- self.assertIn('res_partner_bank', sql_query[0],
- "_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') first query incorrect main table")
-
- expected = "%s like %s" % (unaccent('"res_partner_bank"."sanitized_acc_number"::text'), unaccent('%s'))
- self.assertIn(expected, sql_query[1],
- "_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') first query incorrect where condition")
-
- self.assertEqual(['%' + name_test + '%'], sql_query[2],
- "_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') first query incorrect parameter")
- sql_query = self.query_list[1].get_sql()
- self.assertIn('res_partner', sql_query[0],
- "_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') second query incorrect main table")
- self.assertIn('"res_partner"."id" in (%s)', sql_query[1],
- "_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') second query incorrect where condition")
- self.assertIn(p_aa.id, sql_query[2],
- "_auto_join off: ('bank_ids.sanitized_acc_number', 'like', '..') second query incorrect parameter")
+
+ partners = partner_obj.search(['|', ('name', 'like', 'C'), ('bank_ids.sanitized_acc_number', 'like', name_test)])
+ self.assertIn(p_aa, partners,
+ "_auto_join off: '|', ('name', 'like', 'C'), ('bank_ids.sanitized_acc_number', 'like', '..'): incorrect result")
+ self.assertIn(p_c, partners,
+ "_auto_join off: '|', ('name', 'like', 'C'), ('bank_ids.sanitized_acc_number', 'like', '..'): incorrect result")
# Do: cascaded one2many without _auto_join
- self._reinit_mock()
partners = partner_obj.search([('child_ids.bank_ids.id', 'in', [b_aa.id, b_ba.id])])
- # Test result
self.assertEqual(partners, p_a + p_b,
"_auto_join off: ('child_ids.bank_ids.id', 'in', [..]): incorrect result")
- # Test produced queries
- self.assertEqual(len(self.query_list), 3,
- "_auto_join off: ('child_ids.bank_ids.id', 'in', [..]) should produce 3 queries (1 in res_partner_bank, 2 on res_partner)")
# Do: one2many with _auto_join
patch_auto_join(partner_obj, 'bank_ids', True)
- self._reinit_mock()
partners = partner_obj.search([('bank_ids.sanitized_acc_number', 'like', name_test)])
- # Test result
self.assertEqual(partners, p_aa,
"_auto_join on: ('bank_ids.sanitized_acc_number', 'like', '..') incorrect result")
- # Test produced queries
- self.assertEqual(len(self.query_list), 1,
- "_auto_join on: ('bank_ids.sanitized_acc_number', 'like', '..') should produce 1 query")
- sql_query = self.query_list[0].get_sql()
- self.assertIn('"res_partner"', sql_query[0],
- "_auto_join on: ('bank_ids.sanitized_acc_number', 'like', '..') query incorrect main table")
- self.assertIn('"res_partner_bank" as "res_partner__bank_ids"', sql_query[0],
- "_auto_join on: ('bank_ids.sanitized_acc_number', 'like', '..') query incorrect join")
-
- expected = "%s like %s" % (unaccent('"res_partner__bank_ids"."sanitized_acc_number"::text'), unaccent('%s'))
- self.assertIn(expected, sql_query[1],
- "_auto_join on: ('bank_ids.sanitized_acc_number', 'like', '..') query incorrect where condition")
-
- self.assertIn('"res_partner"."id"="res_partner__bank_ids"."partner_id"', sql_query[1],
- "_auto_join on: ('bank_ids.sanitized_acc_number', 'like', '..') query incorrect join condition")
- self.assertIn('%' + name_test + '%', sql_query[2],
- "_auto_join on: ('bank_ids.sanitized_acc_number', 'like', '..') query incorrect parameter")
+
+ partners = partner_obj.search(['|', ('name', 'like', 'C'), ('bank_ids.sanitized_acc_number', 'like', name_test)])
+ self.assertIn(p_aa, partners,
+ "_auto_join on: '|', ('name', 'like', 'C'), ('bank_ids.sanitized_acc_number', 'like', '..'): incorrect result")
+ # TO FIX: this is not correct
+ self.assertNotIn(p_c, partners,
+ "_auto_join on: '|', ('name', 'like', 'C'), ('bank_ids.sanitized_acc_number', 'like', '..'): incorrect result")
# Do: one2many with _auto_join, test final leaf is an id
- self._reinit_mock()
bank_ids = [b_aa.id, b_ab.id]
partners = partner_obj.search([('bank_ids.id', 'in', bank_ids)])
- # Test result
self.assertEqual(partners, p_aa + p_ab,
"_auto_join on: ('bank_ids.id', 'in', [..]) incorrect result")
- # Test produced queries
- self.assertEqual(len(self.query_list), 1,
- "_auto_join on: ('bank_ids.id', 'in', [..]) should produce 1 query")
- sql_query = self.query_list[0].get_sql()
- self.assertIn('"res_partner"', sql_query[0],
- "_auto_join on: ('bank_ids.id', 'in', [..]) query incorrect main table")
- self.assertIn('"res_partner__bank_ids"."id" in (%s,%s)', sql_query[1],
- "_auto_join on: ('bank_ids.id', 'in', [..]) query incorrect where condition")
- self.assertLessEqual(set(bank_ids), set(sql_query[2]),
- "_auto_join on: ('bank_ids.id', 'in', [..]) query incorrect parameter")
# Do: 2 cascaded one2many with _auto_join, test final leaf is an id
patch_auto_join(partner_obj, 'child_ids', True)
- self._reinit_mock()
bank_ids = [b_aa.id, b_ba.id]
partners = partner_obj.search([('child_ids.bank_ids.id', 'in', bank_ids)])
- # Test result
self.assertEqual(partners, p_a + p_b,
"_auto_join on: ('child_ids.bank_ids.id', 'not in', [..]): incorrect result")
- # # Test produced queries
- self.assertEqual(len(self.query_list), 1,
- "_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) should produce 1 query")
- sql_query = self.query_list[0].get_sql()
- self.assertIn('"res_partner"', sql_query[0],
- "_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) incorrect main table")
- self.assertIn('"res_partner" as "res_partner__child_ids"', sql_query[0],
- "_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect join")
- self.assertIn('"res_partner_bank" as "res_partner__child_ids__bank_ids"', sql_query[0],
- "_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect join")
- self.assertIn('"res_partner__child_ids__bank_ids"."id" in (%s,%s)', sql_query[1],
- "_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect where condition")
- self.assertIn('"res_partner"."id"="res_partner__child_ids"."parent_id"', sql_query[1],
- "_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect join condition")
- self.assertIn('"res_partner__child_ids"."id"="res_partner__child_ids__bank_ids"."partner_id"', sql_query[1],
- "_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect join condition")
- self.assertLessEqual(set(bank_ids), set(sql_query[2][-2:]),
- "_auto_join on: ('child_ids.bank_ids.id', 'in', [..]) query incorrect parameter")
# --------------------------------------------------
# Test3: many2one
@@ -953,107 +869,38 @@ class TestAutoJoin(TransactionCase):
name_test = 'US'
# Do: many2one without _auto_join
- self._reinit_mock()
partners = partner_obj.search([('state_id.country_id.code', 'like', name_test)])
- # Test result: at least our added data + demo data
self.assertLessEqual(p_a + p_b + p_aa + p_ab + p_ba, partners,
"_auto_join off: ('state_id.country_id.code', 'like', '..') incorrect result")
- # Test produced queries
- self.assertEqual(len(self.query_list), 3,
- "_auto_join off: ('state_id.country_id.code', 'like', '..') should produce 3 queries (1 on res_country, 1 on res_country_state, 1 on res_partner)")
+
+ partners = partner_obj.search(['|', ('state_id.code', '=', states[0].code), ('name', 'like', 'C')])
+ self.assertIn(p_a, partners, '_auto_join off: disjunction incorrect result')
+ self.assertIn(p_c, partners, '_auto_join off: disjunction incorrect result')
# Do: many2one with 1 _auto_join on the first many2one
patch_auto_join(partner_obj, 'state_id', True)
- self._reinit_mock()
partners = partner_obj.search([('state_id.country_id.code', 'like', name_test)])
- # Test result: at least our added data + demo data
self.assertLessEqual(p_a + p_b + p_aa + p_ab + p_ba, partners,
"_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') incorrect result")
- # Test produced queries
- self.assertEqual(len(self.query_list), 2,
- "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') should produce 2 query")
- sql_query = self.query_list[0].get_sql()
- self.assertIn('"res_country"', sql_query[0],
- "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect main table")
-
- expected = "%s like %s" % (unaccent('"res_country"."code"::text'), unaccent('%s'))
- self.assertIn(expected, sql_query[1],
- "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect where condition")
-
- self.assertEqual(['%' + name_test + '%'], sql_query[2],
- "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect parameter")
- sql_query = self.query_list[1].get_sql()
- self.assertIn('"res_partner"', sql_query[0],
- "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect main table")
- self.assertIn('"res_country_state" as "res_partner__state_id"', sql_query[0],
- "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect join")
- self.assertIn('"res_partner__state_id"."country_id" in (%s)', sql_query[1],
- "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect where condition")
- self.assertIn('"res_partner"."state_id"="res_partner__state_id"."id"', sql_query[1],
- "_auto_join on for state_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect join condition")
+
+ partners = partner_obj.search(['|', ('state_id.code', '=', states[0].code), ('name', 'like', 'C')])
+ self.assertIn(p_a, partners, '_auto_join: disjunction incorrect result')
+ # TO FIX: this is not correct
+ self.assertNotIn(p_c, partners, '_auto_join: disjunction incorrect result')
# Do: many2one with 1 _auto_join on the second many2one
patch_auto_join(partner_obj, 'state_id', False)
patch_auto_join(state_obj, 'country_id', True)
- self._reinit_mock()
partners = partner_obj.search([('state_id.country_id.code', 'like', name_test)])
- # Test result: at least our added data + demo data
self.assertLessEqual(p_a + p_b + p_aa + p_ab + p_ba, partners,
"_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') incorrect result")
- # Test produced queries
- self.assertEqual(len(self.query_list), 2,
- "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') should produce 2 query")
- # -- first query
- sql_query = self.query_list[0].get_sql()
- self.assertIn('"res_country_state"', sql_query[0],
- "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect main table")
- self.assertIn('"res_country" as "res_country_state__country_id"', sql_query[0],
- "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect join")
-
- expected = "%s like %s" % (unaccent('"res_country_state__country_id"."code"::text'), unaccent('%s'))
- self.assertIn(expected, sql_query[1],
- "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect where condition")
-
- self.assertIn('"res_country_state"."country_id"="res_country_state__country_id"."id"', sql_query[1],
- "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect join condition")
- self.assertEqual(['%' + name_test + '%'], sql_query[2],
- "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 1 incorrect parameter")
- # -- second query
- sql_query = self.query_list[1].get_sql()
- self.assertIn('"res_partner"', sql_query[0],
- "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect main table")
- self.assertIn('"res_partner"."state_id" in', sql_query[1],
- "_auto_join on for country_id: ('state_id.country_id.code', 'like', '..') query 2 incorrect where condition")
# Do: many2one with 2 _auto_join
patch_auto_join(partner_obj, 'state_id', True)
patch_auto_join(state_obj, 'country_id', True)
- self._reinit_mock()
partners = partner_obj.search([('state_id.country_id.code', 'like', name_test)])
- # Test result: at least our added data + demo data
self.assertLessEqual(p_a + p_b + p_aa + p_ab + p_ba, partners,
"_auto_join on: ('state_id.country_id.code', 'like', '..') incorrect result")
- # Test produced queries
- self.assertEqual(len(self.query_list), 1,
- "_auto_join on: ('state_id.country_id.code', 'like', '..') should produce 1 query")
- sql_query = self.query_list[0].get_sql()
- self.assertIn('"res_partner"', sql_query[0],
- "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect main table")
- self.assertIn('"res_country_state" as "res_partner__state_id"', sql_query[0],
- "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join")
- self.assertIn('"res_country" as "res_partner__state_id__country_id"', sql_query[0],
- "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join")
-
- expected = "%s like %s" % (unaccent('"res_partner__state_id__country_id"."code"::text'), unaccent('%s'))
- self.assertIn(expected, sql_query[1],
- "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect where condition")
-
- self.assertIn('"res_partner"."state_id"="res_partner__state_id"."id"', sql_query[1],
- "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join condition")
- self.assertIn('"res_partner__state_id"."country_id"="res_partner__state_id__country_id"."id"', sql_query[1],
- "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect join condition")
- self.assertIn('%' + name_test + '%', sql_query[2],
- "_auto_join on: ('state_id.country_id.code', 'like', '..') query incorrect parameter")
# --------------------------------------------------
# Test4: domain attribute on one2many fields
@@ -1063,28 +910,16 @@ class TestAutoJoin(TransactionCase):
patch_auto_join(partner_obj, 'bank_ids', True)
patch_domain(partner_obj, 'child_ids', lambda self: ['!', ('name', '=', self._name)])
patch_domain(partner_obj, 'bank_ids', [('sanitized_acc_number', 'like', '2')])
+
# Do: 2 cascaded one2many with _auto_join, test final leaf is an id
- self._reinit_mock()
partners = partner_obj.search(['&', (1, '=', 1), ('child_ids.bank_ids.id', 'in', [b_aa.id, b_ba.id])])
- # Test result: at least one of our added data
self.assertLessEqual(p_a, partners,
"_auto_join on one2many with domains incorrect result")
self.assertFalse((p_ab + p_ba) & partners,
"_auto_join on one2many with domains incorrect result")
- # Test produced queries that domains effectively present
- sql_query = self.query_list[0].get_sql()
-
- expected = "%s like %s" % (unaccent('"res_partner__child_ids__bank_ids"."sanitized_acc_number"::text'), unaccent('%s'))
- self.assertIn(expected, sql_query[1],
- "_auto_join on one2many with domains incorrect result")
- # TDE TODO: check first domain has a correct table name
- self.assertIn('"res_partner__child_ids"."name" = %s', sql_query[1],
- "_auto_join on one2many with domains incorrect result")
patch_domain(partner_obj, 'child_ids', lambda self: [('name', '=', '__%s' % self._name)])
- self._reinit_mock()
partners = partner_obj.search(['&', (1, '=', 1), ('child_ids.bank_ids.id', 'in', [b_aa.id, b_ba.id])])
- # Test result: no one
self.assertFalse(partners,
"_auto_join on one2many with domains incorrect result")
@@ -1101,27 +936,17 @@ class TestAutoJoin(TransactionCase):
patch_domain(partner_obj, 'bank_ids', [])
# Do: ('child_ids.state_id.country_id.code', 'like', '..') without _auto_join
- self._reinit_mock()
partners = partner_obj.search([('child_ids.state_id.country_id.code', 'like', name_test)])
- # Test result: at least our added data + demo data
self.assertLessEqual(p_a + p_b, partners,
"_auto_join off: ('child_ids.state_id.country_id.code', 'like', '..') incorrect result")
- # Test produced queries
- self.assertEqual(len(self.query_list), 4,
- "_auto_join off: ('child_ids.state_id.country_id.code', 'like', '..') number of queries incorrect")
# Do: ('child_ids.state_id.country_id.code', 'like', '..') with _auto_join
patch_auto_join(partner_obj, 'child_ids', True)
patch_auto_join(partner_obj, 'state_id', True)
patch_auto_join(state_obj, 'country_id', True)
- self._reinit_mock()
partners = partner_obj.search([('child_ids.state_id.country_id.code', 'like', name_test)])
- # Test result: at least our added data + demo data
self.assertLessEqual(p_a + p_b, partners,
"_auto_join on: ('child_ids.state_id.country_id.code', 'like', '..') incorrect result")
- # Test produced queries
- self.assertEqual(len(self.query_list), 1,
- "_auto_join on: ('child_ids.state_id.country_id.code', 'like', '..') number of queries incorrect")
def test_nullfields(self):
obj1 = self.env['res.bank'].create({'name': 'c0'})
@@ -1146,3 +971,562 @@ class TestAutoJoin(TransactionCase):
(obj1 | obj2 | obj3),
"Should have returned all banks whose city doesn't contain field"
)
+
+
+class TestQueries(TransactionCase):
+
+ def test_logic(self):
+ Model = self.env['res.partner']
+ domain = [
+ '&', ('name', 'like', 'foo'),
+ '|', ('title', '=', 1), '!', ('ref', '=', '42'),
+ ]
+ Model.search(domain)
+
+ with self.assertQueries(['''
+ SELECT "res_partner".id
+ FROM "res_partner"
+ WHERE (("res_partner"."active" = %s) AND (
+ ("res_partner"."name"::text LIKE %s) AND (
+ ("res_partner"."title" = %s) OR (
+ ("res_partner"."ref" != %s) OR
+ "res_partner"."ref" IS NULL
+ )
+ )
+ ))
+ ORDER BY "res_partner"."display_name"
+ ''']):
+ Model.search(domain)
+
+ def test_translated_field(self):
+ self.env['res.lang']._activate_lang('fr_FR')
+ Model = self.env['res.partner.title'].with_context(lang='fr_FR')
+ Model.search([('name', 'ilike', 'foo')])
+
+ with self.assertQueries(['''
+ SELECT "res_partner_title".id
+ FROM "res_partner_title"
+ LEFT JOIN "ir_translation" AS "res_partner_title__name" ON
+ ("res_partner_title"."id" = "res_partner_title__name"."res_id"
+ AND "res_partner_title__name"."type" = 'model'
+ AND "res_partner_title__name"."name" = %s
+ AND "res_partner_title__name"."lang" = %s
+ AND "res_partner_title__name"."value" != %s)
+ WHERE ("res_partner_title"."id" IN (
+ WITH temp_irt_current (id, name) AS (
+ SELECT ct.id, coalesce(it.value, ct."name")
+ FROM res_partner_title ct
+ LEFT JOIN ir_translation it ON
+ (it.name = %s AND it.lang = %s AND it.type = %s
+ AND it.res_id = ct.id AND it.value != '')
+ )
+ SELECT id
+ FROM temp_irt_current
+ WHERE name LIKE %s
+ ORDER BY name
+ ))
+ ORDER BY COALESCE("res_partner_title__name"."value", "res_partner_title"."name")
+ ''']):
+ Model.search([('name', 'like', 'foo')])
+
+ with self.assertQueries(['''
+ SELECT COUNT(1)
+ FROM "res_partner_title"
+ LEFT JOIN "ir_translation" AS "res_partner_title__name" ON
+ ("res_partner_title"."id" = "res_partner_title__name"."res_id"
+ AND "res_partner_title__name"."type" = 'model'
+ AND "res_partner_title__name"."name" = %s
+ AND "res_partner_title__name"."lang" = %s
+ AND "res_partner_title__name"."value" != %s)
+ WHERE ("res_partner_title"."id" = %s)
+ ''']):
+ Model.search_count([('id', '=', 1)])
+
+ @mute_logger('odoo.models.unlink')
+ def test_access_rules(self):
+ Model = self.env['res.users'].with_user(self.env.ref('base.user_admin'))
+ self.env['ir.rule'].search([]).unlink()
+ self.env['ir.rule'].create([{
+ 'name': 'users rule',
+ 'model_id': self.env['ir.model']._get('res.users').id,
+ 'domain_force': str([('id', '=', 1)]),
+ }, {
+ 'name': 'partners rule',
+ 'model_id': self.env['ir.model']._get('res.partner').id,
+ 'domain_force': str([('id', '=', 1)]),
+ }])
+ Model.search([])
+
+ with self.assertQueries(['''
+ SELECT "res_users".id
+ FROM "res_users", "res_partner" AS "res_users__partner_id"
+ WHERE ("res_users"."active" = %s)
+ AND ("res_users"."id" = %s)
+ AND ("res_users"."partner_id" = "res_users__partner_id"."id")
+ AND ("res_users__partner_id"."id" = %s)
+ ORDER BY "res_users__partner_id"."name", "res_users"."login"
+ ''']):
+ Model.search([])
+
+
+class TestMany2one(TransactionCase):
+ def setUp(self):
+ super().setUp()
+ self.Partner = self.env['res.partner'].with_context(active_test=False)
+ self.User = self.env['res.users'].with_context(active_test=False)
+ self.company = self.env['res.company'].browse(1)
+
+ def test_inherited(self):
+ with self.assertQueries(['''
+ SELECT "res_users".id
+ FROM "res_users", "res_partner" AS "res_users__partner_id"
+ WHERE ("res_users"."partner_id" = "res_users__partner_id"."id")
+ AND ("res_users__partner_id"."name"::text LIKE %s)
+ ORDER BY "res_users__partner_id"."name", "res_users"."login"
+ ''']):
+ self.User.search([('name', 'like', 'foo')])
+
+ def test_regular(self):
+ self.Partner.search([('company_id.partner_id.name', 'like', self.company.name)])
+ self.Partner.search([('country_id.code', 'like', 'BE')])
+
+ with self.assertQueries(['''
+ SELECT "res_partner".id
+ FROM "res_partner"
+ WHERE ("res_partner"."company_id" = %s)
+ ORDER BY "res_partner"."display_name"
+ ''']):
+ self.Partner.search([('company_id', '=', self.company.id)])
+
+ with self.assertQueries(['''
+ SELECT "res_company".id
+ FROM "res_company"
+ WHERE ("res_company"."name"::text LIKE %s)
+ ORDER BY "res_company"."sequence", "res_company"."name"
+ ''', '''
+ SELECT "res_partner".id
+ FROM "res_partner"
+ WHERE ("res_partner"."company_id" IN (%s))
+ ORDER BY "res_partner"."display_name"
+ ''']):
+ self.Partner.search([('company_id.name', 'like', self.company.name)])
+
+ with self.assertQueries(['''
+ SELECT "res_partner".id
+ FROM "res_partner"
+ WHERE ("res_partner"."name"::text LIKE %s)
+ ORDER BY "res_partner"."display_name"
+ ''', '''
+ SELECT "res_company".id
+ FROM "res_company"
+ WHERE ("res_company"."partner_id" IN (%s))
+ ORDER BY "res_company"."sequence", "res_company"."name"
+ ''', '''
+ SELECT "res_partner".id
+ FROM "res_partner"
+ WHERE ("res_partner"."company_id" IN (%s))
+ ORDER BY "res_partner"."display_name"
+ ''']):
+ self.Partner.search([('company_id.partner_id.name', 'like', self.company.name)])
+
+ with self.assertQueries(['''
+ SELECT "res_company".id
+ FROM "res_company"
+ WHERE ("res_company"."name"::text LIKE %s)
+ ORDER BY "res_company"."sequence", "res_company"."name"
+ ''', '''
+ SELECT "res_country".id
+ FROM "res_country"
+ WHERE ("res_country"."code"::text LIKE %s)
+ ORDER BY "res_country"."name"
+ ''', '''
+ SELECT "res_partner".id
+ FROM "res_partner"
+ WHERE (("res_partner"."company_id" IN (%s)) OR ("res_partner"."country_id" IN (%s)))
+ ORDER BY "res_partner"."display_name"
+ ''']):
+ self.Partner.search([
+ '|',
+ ('company_id.name', 'like', self.company.name),
+ ('country_id.code', 'like', 'BE'),
+ ])
+
+ def test_autojoin(self):
+ # auto_join on the first many2one
+ self.patch(self.Partner._fields['company_id'], 'auto_join', True)
+ self.patch(self.company._fields['partner_id'], 'auto_join', False)
+ self.Partner.search([('company_id.partner_id.name', 'like', self.company.name)])
+
+ with self.assertQueries(['''
+ SELECT "res_partner".id
+ FROM "res_partner", "res_company" AS "res_partner__company_id"
+ WHERE ("res_partner"."company_id" = "res_partner__company_id"."id")
+ AND ("res_partner__company_id"."name"::text LIKE %s)
+ ORDER BY "res_partner"."display_name"
+ ''']):
+ self.Partner.search([('company_id.name', 'like', self.company.name)])
+
+ with self.assertQueries(['''
+ SELECT "res_partner".id
+ FROM "res_partner"
+ WHERE ("res_partner"."name"::text LIKE %s)
+ ORDER BY "res_partner"."display_name"
+ ''', '''
+ SELECT "res_partner".id
+ FROM "res_partner", "res_company" AS "res_partner__company_id"
+ WHERE ("res_partner"."company_id" = "res_partner__company_id"."id")
+ AND ("res_partner__company_id"."partner_id" IN (%s))
+ ORDER BY "res_partner"."display_name"
+ ''']):
+ self.Partner.search([('company_id.partner_id.name', 'like', self.company.name)])
+
+ # auto_join on the second many2one
+ self.patch(self.Partner._fields['company_id'], 'auto_join', False)
+ self.patch(self.company._fields['partner_id'], 'auto_join', True)
+ self.Partner.search([('company_id.partner_id.name', 'like', self.company.name)])
+
+ with self.assertQueries(['''
+ SELECT "res_company".id
+ FROM "res_company", "res_partner" AS "res_company__partner_id"
+ WHERE ("res_company"."partner_id" = "res_company__partner_id"."id")
+ AND ("res_company__partner_id"."name"::text LIKE %s)
+ ORDER BY "res_company"."sequence", "res_company"."name"
+ ''', '''
+ SELECT "res_partner".id
+ FROM "res_partner"
+ WHERE ("res_partner"."company_id" IN (%s))
+ ORDER BY "res_partner"."display_name"
+ ''']):
+ self.Partner.search([('company_id.partner_id.name', 'like', self.company.name)])
+
+ # auto_join on both many2one
+ self.patch(self.Partner._fields['company_id'], 'auto_join', True)
+ self.patch(self.company._fields['partner_id'], 'auto_join', True)
+ self.Partner.search([('company_id.partner_id.name', 'like', self.company.name)])
+
+ with self.assertQueries(['''
+ SELECT "res_partner".id
+ FROM "res_partner", "res_company" AS "res_partner__company_id",
+ "res_partner" AS "res_partner__company_id__partner_id"
+ WHERE ("res_partner"."company_id" = "res_partner__company_id"."id"
+ AND "res_partner__company_id"."partner_id" = "res_partner__company_id__partner_id"."id")
+ AND ("res_partner__company_id__partner_id"."name"::text LIKE %s)
+ ORDER BY "res_partner"."display_name"
+ ''']):
+ self.Partner.search([('company_id.partner_id.name', 'like', self.company.name)])
+
+ # union with two auto_join
+ self.patch(self.Partner._fields['company_id'], 'auto_join', True)
+ self.patch(self.Partner._fields['country_id'], 'auto_join', True)
+ self.Partner.search([
+ '|',
+ ('company_id.name', 'like', self.company.name),
+ ('country_id.code', 'like', 'BE'),
+ ])
+
+ # WRONG QUERY: it discards results where company_id or country_id are NULL
+ with self.assertQueries(['''
+ SELECT "res_partner".id
+ FROM "res_partner", "res_company" AS "res_partner__company_id",
+ "res_country" AS "res_partner__country_id"
+ WHERE ("res_partner"."company_id" = "res_partner__company_id"."id"
+ AND "res_partner"."country_id" = "res_partner__country_id"."id")
+ AND (("res_partner__company_id"."name"::text LIKE %s)
+ OR ("res_partner__country_id"."code"::text LIKE %s))
+ ORDER BY "res_partner"."display_name"
+ ''']):
+ self.Partner.search([
+ '|',
+ ('company_id.name', 'like', self.company.name),
+ ('country_id.code', 'like', 'BE'),
+ ])
+
+ def test_name_search(self):
+ self.Partner.search([('company_id', 'like', self.company.name)])
+
+ with self.assertQueries(['''
+ SELECT "res_company".id
+ FROM "res_company"
+ WHERE ("res_company"."name"::text LIKE %s)
+ ORDER BY "res_company"."sequence", "res_company"."name"
+ ''', '''
+ SELECT "res_partner".id
+ FROM "res_partner"
+ WHERE ("res_partner"."company_id" IN (%s))
+ ORDER BY "res_partner"."display_name"
+ ''']):
+ self.Partner.search([('company_id', 'like', self.company.name)])
+
+
+class TestOne2many(TransactionCase):
+ def setUp(self):
+ super().setUp()
+ self.Partner = self.env['res.partner'].with_context(active_test=False)
+ self.partner = self.Partner.create({
+ 'name': 'Foo',
+ 'bank_ids': [
+ (0, 0, {'acc_number': '123', 'acc_type': 'bank'}),
+ (0, 0, {'acc_number': '456', 'acc_type': 'bank'}),
+ (0, 0, {'acc_number': '789', 'acc_type': 'bank'}),
+ ],
+ })
+
+ def test_regular(self):
+ self.Partner.search([('bank_ids', 'in', self.partner.bank_ids.ids)])
+ self.Partner.search([('bank_ids.sanitized_acc_number', 'like', '12')])
+ self.Partner.search([('child_ids.bank_ids.sanitized_acc_number', 'like', '12')])
+
+ with self.assertQueries(['''
+ SELECT DISTINCT "partner_id"
+ FROM "res_partner_bank"
+ WHERE "id" IN %s
+ ''', '''
+ SELECT "res_partner".id
+ FROM "res_partner"
+ WHERE ("res_partner"."id" IN (%s))
+ ORDER BY "res_partner"."display_name"
+ ''']):
+ self.Partner.search([('bank_ids', 'in', self.partner.bank_ids.ids)])
+
+ with self.assertQueries(['''
+ SELECT "res_partner_bank".id
+ FROM "res_partner_bank"
+ WHERE ("res_partner_bank"."sanitized_acc_number"::text LIKE %s)
+ ORDER BY "res_partner_bank"."sequence", "res_partner_bank"."id"
+ ''', '''
+ SELECT DISTINCT "partner_id"
+ FROM "res_partner_bank"
+ WHERE "id" IN %s
+ ''', '''
+ SELECT "res_partner".id
+ FROM "res_partner"
+ WHERE ("res_partner"."id" IN (%s))
+ ORDER BY "res_partner"."display_name"
+ ''']):
+ self.Partner.search([('bank_ids.sanitized_acc_number', 'like', '12')])
+
+ with self.assertQueries(['''
+ SELECT "res_partner_bank".id
+ FROM "res_partner_bank"
+ WHERE ("res_partner_bank"."sanitized_acc_number"::text LIKE %s)
+ ORDER BY "res_partner_bank"."sequence", "res_partner_bank"."id"
+ ''', '''
+ SELECT DISTINCT "partner_id"
+ FROM "res_partner_bank"
+ WHERE "id" IN %s
+ ''', '''
+ SELECT "res_partner".id
+ FROM "res_partner"
+ WHERE ("res_partner"."id" IN (%s))
+ ORDER BY "res_partner"."display_name"
+ ''', '''
+ SELECT DISTINCT "parent_id"
+ FROM "res_partner"
+ WHERE "id" IN %s
+ ''', '''
+ SELECT "res_partner".id
+ FROM "res_partner"
+ WHERE ("res_partner"."id" IN (%s))
+ ORDER BY "res_partner"."display_name"
+ ''']):
+ self.Partner.search([('child_ids.bank_ids.sanitized_acc_number', 'like', '12')])
+
+ def test_autojoin(self):
+ self.patch(self.Partner._fields['bank_ids'], 'auto_join', True)
+ self.patch(self.Partner._fields['child_ids'], 'auto_join', True)
+ self.Partner.search([('bank_ids', 'in', self.partner.bank_ids.ids)])
+ self.Partner.search([('bank_ids.sanitized_acc_number', 'like', '12')])
+ self.Partner.search([('child_ids.bank_ids.sanitized_acc_number', 'like', '12')])
+
+ with self.assertQueries(['''
+ SELECT DISTINCT "partner_id"
+ FROM "res_partner_bank"
+ WHERE "id" IN %s
+ ''', '''
+ SELECT "res_partner".id
+ FROM "res_partner"
+ WHERE ("res_partner"."id" IN (%s))
+ ORDER BY "res_partner"."display_name"
+ ''']):
+ self.Partner.search([('bank_ids', 'in', self.partner.bank_ids.ids)])
+
+ with self.assertQueries(['''
+ SELECT "res_partner".id
+ FROM "res_partner", "res_partner_bank" AS "res_partner__bank_ids"
+ WHERE ("res_partner"."id" = "res_partner__bank_ids"."partner_id")
+ AND ("res_partner__bank_ids"."sanitized_acc_number"::text LIKE %s)
+ ORDER BY "res_partner"."display_name"
+ ''']):
+ self.Partner.search([('bank_ids.sanitized_acc_number', 'like', '12')])
+
+ # WRONG QUERY: the domain semantics is having some line matching '12'
+ # and having some line matching '45', but not necessarily the same line!
+ with self.assertQueries(['''
+ SELECT "res_partner".id
+ FROM "res_partner", "res_partner_bank" AS "res_partner__bank_ids"
+ WHERE ("res_partner"."id" = "res_partner__bank_ids"."partner_id")
+ AND (("res_partner__bank_ids"."sanitized_acc_number"::text LIKE %s)
+ AND ("res_partner__bank_ids"."sanitized_acc_number"::text LIKE %s))
+ ORDER BY "res_partner"."display_name"
+ ''']):
+ self.Partner.search([
+ ('bank_ids.sanitized_acc_number', 'like', '12'),
+ ('bank_ids.sanitized_acc_number', 'like', '45'),
+ ])
+
+ with self.assertQueries(['''
+ SELECT "res_partner".id
+ FROM "res_partner", "res_partner" AS "res_partner__child_ids",
+ "res_partner_bank" AS "res_partner__child_ids__bank_ids"
+ WHERE ("res_partner"."id" = "res_partner__child_ids"."parent_id"
+ AND "res_partner__child_ids"."id" = "res_partner__child_ids__bank_ids"."partner_id")
+ AND (("res_partner__child_ids"."active" = %s)
+ AND ("res_partner__child_ids__bank_ids"."sanitized_acc_number"::text LIKE %s))
+ ORDER BY "res_partner"."display_name"
+ ''']):
+ self.Partner.search([('child_ids.bank_ids.sanitized_acc_number', 'like', '12')])
+
+ # check domains on one2many fields
+ self.patch(self.Partner._fields['bank_ids'], 'domain',
+ [('sanitized_acc_number', 'like', '2')])
+ self.patch(self.Partner._fields['child_ids'], 'domain',
+ lambda self: ['!', ('name', '=', self._name)])
+
+ with self.assertQueries(['''
+ SELECT "res_partner".id
+ FROM "res_partner", "res_partner" AS "res_partner__child_ids",
+ "res_partner_bank" AS "res_partner__child_ids__bank_ids"
+ WHERE ("res_partner"."id" = "res_partner__child_ids"."parent_id"
+ AND "res_partner__child_ids"."id" = "res_partner__child_ids__bank_ids"."partner_id")
+ AND ((NOT (("res_partner__child_ids"."name" = %s)))
+ AND (("res_partner__child_ids__bank_ids"."sanitized_acc_number"::text LIKE %s)
+ AND ("res_partner__child_ids__bank_ids"."id" in (%s,%s,%s))))
+ ORDER BY "res_partner"."display_name"
+ ''']):
+ self.Partner.search([('child_ids.bank_ids.id', 'in', self.partner.bank_ids.ids)])
+
+ def test_autojoin_mixed(self):
+ self.patch(self.Partner._fields['child_ids'], 'auto_join', True)
+ self.patch(self.Partner._fields['state_id'], 'auto_join', True)
+ self.patch(self.Partner.state_id._fields['country_id'], 'auto_join', True)
+ self.Partner.search([('child_ids.state_id.country_id.code', 'like', 'US')])
+
+ with self.assertQueries(['''
+ SELECT "res_partner".id
+ FROM "res_partner", "res_partner" AS "res_partner__child_ids",
+ "res_country_state" AS "res_partner__child_ids__state_id",
+ "res_country" AS "res_partner__child_ids__state_id__country_id"
+ WHERE ("res_partner"."id" = "res_partner__child_ids"."parent_id"
+ AND "res_partner__child_ids"."state_id" = "res_partner__child_ids__state_id"."id"
+ AND "res_partner__child_ids__state_id"."country_id" = "res_partner__child_ids__state_id__country_id"."id")
+ AND (("res_partner__child_ids"."active" = %s)
+ AND ("res_partner__child_ids__state_id__country_id"."code"::text LIKE %s))
+ ORDER BY "res_partner"."display_name"
+ ''']):
+ self.Partner.search([('child_ids.state_id.country_id.code', 'like', 'US')])
+
+ def test_name_search(self):
+ self.Partner.search([('bank_ids', 'like', '12')])
+
+ with self.assertQueries(['''
+ SELECT "res_partner_bank".id
+ FROM "res_partner_bank"
+ WHERE ("res_partner_bank"."sanitized_acc_number"::text LIKE %s)
+ ORDER BY "res_partner_bank"."sequence", "res_partner_bank"."id"
+ ''', '''
+ SELECT DISTINCT "partner_id"
+ FROM "res_partner_bank"
+ WHERE "id" IN %s
+ ''', '''
+ SELECT "res_partner".id
+ FROM "res_partner"
+ WHERE ("res_partner"."id" IN (%s))
+ ORDER BY "res_partner"."display_name"
+ ''']):
+ self.Partner.search([('bank_ids', 'like', '12')])
+
+
+class TestMany2many(TransactionCase):
+ def setUp(self):
+ super().setUp()
+ self.User = self.env['res.users'].with_context(active_test=False)
+ self.company = self.env['res.company'].browse(1)
+
+ def test_regular(self):
+ group = self.env.ref('base.group_user')
+ rule = group.rule_groups[0]
+
+ self.User.search([('groups_id', 'in', group.ids)], order='id')
+ self.User.search([('groups_id.name', 'like', group.name)], order='id')
+ self.User.search([('groups_id.rule_groups.name', 'like', rule.name)], order='id')
+
+ with self.assertQueries(['''
+ SELECT "res_users".id
+ FROM "res_users"
+ WHERE ("res_users"."id" IN (
+ SELECT "uid" FROM "res_groups_users_rel" WHERE "gid" IN %s
+ ))
+ ORDER BY "res_users"."id"
+ ''']):
+ self.User.search([('groups_id', 'in', group.ids)], order='id')
+
+ with self.assertQueries(['''
+ SELECT "res_groups".id
+ FROM "res_groups"
+ WHERE ("res_groups"."color" = %s)
+ ORDER BY "res_groups"."name"
+ ''', '''
+ SELECT "res_users".id
+ FROM "res_users"
+ WHERE ("res_users"."id" IN (
+ SELECT "uid" FROM "res_groups_users_rel" WHERE "gid" IN %s
+ ))
+ ORDER BY "res_users"."id"
+ ''']):
+ self.User.search([('groups_id.color', '=', group.color)], order='id')
+
+ with self.assertQueries(['''
+ SELECT "ir_rule".id
+ FROM "ir_rule"
+ LEFT JOIN "ir_model" AS "ir_rule__model_id" ON ("ir_rule"."model_id" = "ir_rule__model_id"."id")
+ WHERE ("ir_rule"."name"::text LIKE %s)
+ ORDER BY "ir_rule__model_id"."model" DESC, "ir_rule"."id"
+ ''', '''
+ SELECT "res_groups".id
+ FROM "res_groups"
+ WHERE ("res_groups"."id" IN (
+ SELECT "group_id" FROM "rule_group_rel" WHERE "rule_group_id" IN %s
+ ))
+ ORDER BY "res_groups"."name"
+ ''', '''
+ SELECT "res_users".id
+ FROM "res_users"
+ WHERE ("res_users"."id" IN (
+ SELECT "uid" FROM "res_groups_users_rel" WHERE "gid" IN %s
+ ))
+ ORDER BY "res_users"."id"
+ ''']):
+ self.User.search([('groups_id.rule_groups.name', 'like', rule.name)], order='id')
+
+ def test_autojoin(self):
+ self.patch(self.User._fields['groups_id'], 'auto_join', True)
+ with self.assertRaises(NotImplementedError):
+ self.User.search([('groups_id.name', '=', 'foo')])
+
+ def test_name_search(self):
+ self.User.search([('company_ids', 'like', self.company.name)], order='id')
+
+ with self.assertQueries(['''
+ SELECT "res_company".id
+ FROM "res_company"
+ WHERE ("res_company"."name"::text LIKE %s)
+ ORDER BY "res_company"."sequence", "res_company"."name"
+ ''', '''
+ SELECT "res_users".id
+ FROM "res_users"
+ WHERE ("res_users"."id" IN (
+ SELECT "user_id" FROM "res_company_users_rel" WHERE "cid" IN %s
+ ))
+ ORDER BY "res_users"."id"
+ ''']):
+ self.User.search([('company_ids', 'like', self.company.name)], order='id')
diff --git a/odoo/osv/expression.py b/odoo/osv/expression.py
index 12c2f2df5d7..d894de46b60 100644
--- a/odoo/osv/expression.py
+++ b/odoo/osv/expression.py
@@ -583,12 +583,13 @@ class ExtendedLeaf(object):
return conditions
def get_tables(self):
- tables = set()
+ tables = []
links = []
for context in self.join_context:
links.append((context[1]._table, context[4]))
alias, alias_statement = generate_table_alias(self._models[0]._table, links)
- tables.add(alias_statement)
+ if alias_statement not in tables:
+ tables.append(alias_statement)
return tables
def _get_context_debug(self):
@@ -676,14 +677,11 @@ class expression(object):
def get_tables(self):
""" Returns the list of tables for SQL queries, like select from ... """
- tables = []
+ tables = [_quote(self.root_model._table)]
for leaf in self.result:
for table in leaf.get_tables():
if table not in tables:
tables.append(table)
- table_name = _quote(self.root_model._table)
- if table_name not in tables:
- tables.append(table_name)
return tables
# ----------------------------------------
@@ -1158,10 +1156,11 @@ class expression(object):
# -> generate joins
# ----------------------------------------
- joins = set()
- for leaf in self.result:
- joins |= set(leaf.get_join_conditions())
- self.joins = list(joins)
+ self.joins = list(tools.unique(
+ join
+ for leaf in self.result
+ for join in leaf.get_join_conditions()
+ ))
def __leaf_to_sql(self, eleaf):
model = eleaf.model
diff --git a/odoo/tests/common.py b/odoo/tests/common.py
index d8ea680415c..51fedd67173 100644
--- a/odoo/tests/common.py
+++ b/odoo/tests/common.py
@@ -40,6 +40,7 @@ from xmlrpc import client as xmlrpclib
from odoo.models import BaseModel
from odoo.osv.expression import normalize_domain, TRUE_LEAF, FALSE_LEAF
+from odoo.sql_db import Cursor
from odoo.tools import float_compare, single_email_re
from odoo.tools.misc import find_in_path
from odoo.tools.safe_eval import safe_eval
@@ -380,6 +381,37 @@ class BaseCase(TreeCase, MetaCase('DummyCase', (object,), {})):
return self._assertRaises(exception, **kwargs)
@contextmanager
+ def assertQueries(self, expected):
+ """ Check the queries made by the current cursor. ``expected`` is a list
+ of strings representing the expected queries being made. Query strings
+ are matched against each other, ignoring case and whitespaces.
+ """
+ Cursor_execute = Cursor.execute
+ actual_queries = []
+
+ def execute(self, query, params=None, log_exceptions=None):
+ actual_queries.append(query)
+ return Cursor_execute(self, query, params, log_exceptions)
+
+ def get_unaccent_wrapper(cr):
+ return lambda x: x
+
+ with patch('odoo.sql_db.Cursor.execute', execute):
+ with patch('odoo.osv.expression.get_unaccent_wrapper', get_unaccent_wrapper):
+ yield actual_queries
+
+ self.assertEqual(
+ len(actual_queries), len(expected),
+ "%d queries done, %d expected" % (len(actual_queries), len(expected)),
+ )
+ for actual_query, expect_query in zip(actual_queries, expected):
+ self.assertEqual(
+ "".join(actual_query.lower().split()),
+ "".join(expect_query.lower().split()),
+ "\n---- actual query:\n%s\n---- not like:\n%s" % (actual_query, expect_query),
+ )
+
+ @contextmanager
def assertQueryCount(self, default=0, flush=True, **counters):
""" Context manager that counts queries. It may be invoked either with
one value, or with a set of named arguments like ``login=value``::