diff --git a/odoo/addons/base/tests/test_expression.py b/odoo/addons/base/tests/test_expression.py index c3ffe3de12d..26fe1116140 100644 --- a/odoo/addons/base/tests/test_expression.py +++ b/odoo/addons/base/tests/test_expression.py @@ -1144,12 +1144,25 @@ class TestQueries(TransactionCase): Model.search([('name', 'like', 'foo')]) with self.assertQueries([''' - SELECT count(*) + SELECT COUNT(*) FROM "res_partner" WHERE (("res_partner"."active" = %s) AND ("res_partner"."name"::text LIKE %s)) ''']): Model.search_count([('name', 'like', 'foo')]) + def test_count_limit(self): + Model = self.env['res.partner'] + Model.search([('name', 'like', 'foo')]) + + with self.assertQueries([''' + SELECT COUNT(*) FROM ( + SELECT FROM "res_partner" + WHERE (("res_partner"."active" = %s) AND ("res_partner"."name"::text LIKE %s)) + LIMIT 1 + ) t + ''']): + Model.search_count([('name', 'like', 'foo')], limit=1) + def test_translated_field(self): self.env['res.lang']._activate_lang('fr_FR') Model = self.env['res.partner.title'].with_context(lang='fr_FR') diff --git a/odoo/addons/base/tests/test_search.py b/odoo/addons/base/tests/test_search.py index c7937111fcd..1d9c140ce6c 100644 --- a/odoo/addons/base/tests/test_search.py +++ b/odoo/addons/base/tests/test_search.py @@ -192,3 +192,18 @@ class test_search(TransactionCase): }) cc_search = model_bank.search([('name', '=', 'Crédit Communal')]) self.assertNotIn(bank_credit_communal, cc_search, "Search for inactive record with x_active set to True has failed") + + def test_21_search_count(self): + Partner = self.env['res.partner'] + count_partner_before = Partner.search_count([]) + partners = Partner.create([ + {'name': 'abc'}, + {'name': 'zer'}, + {'name': 'christope'}, + {'name': 'runbot'}, + ]) + self.assertEqual(len(partners) + count_partner_before, Partner.search_count([])) + self.assertEqual(len(partners) + count_partner_before, Partner.search([], count=True)) + + self.assertEqual(3, Partner.search_count([], limit=3)) + self.assertEqual(3, Partner.search([], count=True, limit=3)) diff --git a/odoo/models.py b/odoo/models.py index 9ff00e43803..595eee66001 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -1872,6 +1872,10 @@ class BaseModel(metaclass=MetaModel): Returns the number of records in the current model matching :ref:`the provided domain `. + + :param domain: :ref:`A search domain `. Use an empty + list to match all records. + :param limit: maximum number of record to count (upperbound) (default: all) """ res = self.search(domain, limit=limit, count=True) return res if isinstance(res, int) else len(res) @@ -4888,14 +4892,18 @@ class BaseModel(metaclass=MetaModel): query.limit = limit if count: - # Ignore order, limit and offset when just counting, they don't make sense and could + # Ignore order and offset when just counting, they don't make sense and could # hurt performance - query_str, params = query.select(limit and "1" or "count(*)") if limit: - query_str = "select count(*) from (" + query_str + ") t" + # Special case to avoid counting every record in DB (which can be really slow). + # The result will be between 0 and limit. + query_str, params = query.select("") # generates a `SELECT FROM` (faster) + query_str = f"SELECT COUNT(*) FROM ({query_str}) t" + else: + query_str, params = query.select("COUNT(*)") + self._cr.execute(query_str, params) - res = self._cr.fetchone() - return res[0] + return self._cr.fetchone()[0] query.order = self._generate_order_by(order, query).replace('ORDER BY ', '') query.offset = offset