[IMP] core: _search() no longer uses a default order
This simplifies the use of subqueries by avoiding some costly default order on the model or the idiotic order='id'. Method _flush_search() has been adapted accordingly. Part-of: odoo/odoo#112126
This commit is contained in:
@@ -570,7 +570,7 @@ class AccountAccount(models.Model):
|
||||
domain = ['|', ('code', '=ilike', name.split(' ')[0] + '%'), ('name', operator, name)]
|
||||
if operator in expression.NEGATIVE_TERM_OPERATORS:
|
||||
domain = ['&', '!'] + domain[1:]
|
||||
return self._search(expression.AND([domain, args]), limit=limit, access_rights_uid=name_get_uid)
|
||||
return self._search(expression.AND([domain, args]), limit=limit, order=self._order, access_rights_uid=name_get_uid)
|
||||
|
||||
@api.onchange('account_type')
|
||||
def _onchange_account_type(self):
|
||||
@@ -842,7 +842,7 @@ class AccountGroup(models.Model):
|
||||
else:
|
||||
criteria_operator = ['|'] if operator not in expression.NEGATIVE_TERM_OPERATORS else ['&', '!']
|
||||
domain = criteria_operator + [('code_prefix_start', '=ilike', name + '%'), ('name', operator, name)]
|
||||
return self._search(expression.AND([domain, args]), limit=limit, access_rights_uid=name_get_uid)
|
||||
return self._search(expression.AND([domain, args]), limit=limit, order=self._order, access_rights_uid=name_get_uid)
|
||||
|
||||
@api.constrains('code_prefix_start', 'code_prefix_end')
|
||||
def _constraint_prefix_overlap(self):
|
||||
|
||||
@@ -28,7 +28,6 @@ class AccountAnalyticAccount(models.Model):
|
||||
[str(account_id) for account_id in self.ids],
|
||||
)
|
||||
|
||||
query.order = None
|
||||
query_string, query_param = query.select(
|
||||
'jsonb_object_keys(account_move_line.analytic_distribution) as account_id',
|
||||
'COUNT(DISTINCT(move_id)) as move_count',
|
||||
@@ -53,7 +52,6 @@ class AccountAnalyticAccount(models.Model):
|
||||
[str(account_id) for account_id in self.ids],
|
||||
)
|
||||
|
||||
query.order = None
|
||||
query_string, query_param = query.select(
|
||||
'jsonb_object_keys(account_move_line.analytic_distribution) as account_id',
|
||||
'COUNT(DISTINCT(move_id)) as move_count',
|
||||
@@ -68,7 +66,6 @@ class AccountAnalyticAccount(models.Model):
|
||||
def action_view_invoice(self):
|
||||
self.ensure_one()
|
||||
query = self.env['account.move.line']._search([('move_id.move_type', 'in', self.env['account.move'].get_sale_types())])
|
||||
query.order = None
|
||||
query.add_where('analytic_distribution ? %s', [str(self.id)])
|
||||
query_string, query_param = query.select('DISTINCT move_id')
|
||||
self._cr.execute(query_string, query_param)
|
||||
@@ -86,7 +83,6 @@ class AccountAnalyticAccount(models.Model):
|
||||
def action_view_vendor_bill(self):
|
||||
self.ensure_one()
|
||||
query = self.env['account.move.line']._search([('move_id.move_type', 'in', self.env['account.move'].get_purchase_types())])
|
||||
query.order = None
|
||||
query.add_where('analytic_distribution ? %s', [str(self.id)])
|
||||
query_string, query_param = query.select('DISTINCT move_id')
|
||||
self._cr.execute(query_string, query_param)
|
||||
|
||||
@@ -6,6 +6,7 @@ from functools import lru_cache
|
||||
|
||||
from odoo import api, fields, models, Command, _
|
||||
from odoo.exceptions import ValidationError, UserError
|
||||
from odoo.osv import expression
|
||||
from odoo.tools import frozendict, formatLang, format_date, float_is_zero, Query
|
||||
from odoo.tools.sql import create_index
|
||||
from odoo.addons.web.controllers.utils import clean_action
|
||||
@@ -1551,14 +1552,12 @@ class AccountMoveLine(models.Model):
|
||||
if field_name != 'account_root_id' or set_count:
|
||||
return super()._search_panel_domain_image(field_name, domain, set_count, limit)
|
||||
|
||||
# Override in order to not read the complete move line table and use the index instead
|
||||
query = self._search(domain, limit=1)
|
||||
|
||||
# if domain is logically equivalent to false
|
||||
if not isinstance(query, Query):
|
||||
if expression.is_false(self, domain):
|
||||
return {}
|
||||
|
||||
query.order = None
|
||||
# Override in order to not read the complete move line table and use the index instead
|
||||
query = self._search(domain, limit=1)
|
||||
query.add_where('account.id = account_move_line.account_id')
|
||||
query_str, query_param = query.select()
|
||||
self.env.cr.execute(f"""
|
||||
|
||||
@@ -350,8 +350,7 @@ class EventEvent(models.Model):
|
||||
domain = [('date_begin', '<=', now), ('date_end', '>', now)]
|
||||
else:
|
||||
domain = ['|', ('date_begin', '>', now), ('date_end', '<=', now)]
|
||||
event_ids = self.env['event.event']._search(domain)
|
||||
return [('id', 'in', event_ids)]
|
||||
return domain
|
||||
|
||||
@api.depends('date_begin', 'date_end', 'date_tz')
|
||||
def _compute_field_is_one_day(self):
|
||||
@@ -384,8 +383,7 @@ class EventEvent(models.Model):
|
||||
domain = [('date_end', '<=', now)]
|
||||
else:
|
||||
domain = [('date_end', '>', now)]
|
||||
event_ids = self.env['event.event']._search(domain)
|
||||
return [('id', 'in', event_ids)]
|
||||
return domain
|
||||
|
||||
@api.depends('event_type_id')
|
||||
def _compute_date_tz(self):
|
||||
|
||||
@@ -443,13 +443,13 @@ class TestEventData(TestEventInternalsCommon):
|
||||
'date_end': datetime(2020, 2, 1, 18, 0, 0),
|
||||
})
|
||||
self.assertTrue(event_1.is_ongoing)
|
||||
ongoing_event_ids = self.env['event.event']._search([('is_ongoing', '=', True)])
|
||||
self.assertIn(event_1.id, ongoing_event_ids)
|
||||
ongoing_events = self.env['event.event'].search([('is_ongoing', '=', True)])
|
||||
self.assertIn(event_1, ongoing_events)
|
||||
|
||||
event_1.update({'date_begin': datetime(2020, 2, 1, 9, 0, 0)})
|
||||
self.assertFalse(event_1.is_ongoing)
|
||||
ongoing_event_ids = self.env['event.event']._search([('is_ongoing', '=', True)])
|
||||
self.assertNotIn(event_1.id, ongoing_event_ids)
|
||||
ongoing_events = self.env['event.event'].search([('is_ongoing', '=', True)])
|
||||
self.assertNotIn(event_1, ongoing_events)
|
||||
|
||||
event_2 = self.env['event.event'].create({
|
||||
'name': 'Test Event 2',
|
||||
@@ -457,13 +457,13 @@ class TestEventData(TestEventInternalsCommon):
|
||||
'date_end': datetime(2020, 1, 28, 8, 0, 0),
|
||||
})
|
||||
self.assertFalse(event_2.is_ongoing)
|
||||
finished_or_upcoming_event_ids = self.env['event.event']._search([('is_ongoing', '=', False)])
|
||||
self.assertIn(event_2.id, finished_or_upcoming_event_ids)
|
||||
finished_or_upcoming_events = self.env['event.event'].search([('is_ongoing', '=', False)])
|
||||
self.assertIn(event_2, finished_or_upcoming_events)
|
||||
|
||||
event_2.update({'date_end': datetime(2020, 2, 2, 8, 0, 1)})
|
||||
self.assertTrue(event_2.is_ongoing)
|
||||
finished_or_upcoming_event_ids = self.env['event.event']._search([('is_ongoing', '=', False)])
|
||||
self.assertNotIn(event_2.id, finished_or_upcoming_event_ids)
|
||||
finished_or_upcoming_events = self.env['event.event'].search([('is_ongoing', '=', False)])
|
||||
self.assertNotIn(event_2, finished_or_upcoming_events)
|
||||
|
||||
@users('user_eventmanager')
|
||||
def test_event_seats(self):
|
||||
|
||||
@@ -566,7 +566,7 @@ class HolidaysType(models.Model):
|
||||
to the method.
|
||||
"""
|
||||
employee_id = self._get_contextual_employee_id()
|
||||
if not order and employee_id:
|
||||
if order == self._order and employee_id:
|
||||
# retrieve all leaves, sort them, then apply offset and limit
|
||||
leaves = self.browse(super()._search(domain, access_rights_uid=access_rights_uid))
|
||||
leaves = leaves.sorted(key=self._model_sorting_key, reverse=True)
|
||||
|
||||
@@ -62,4 +62,4 @@ class ChatbotScriptAnswer(models.Model):
|
||||
if force_domain_chatbot_script_id:
|
||||
domain = expression.AND([domain, [('chatbot_script_id', '=', force_domain_chatbot_script_id)]])
|
||||
|
||||
return self._search(domain, limit=limit, access_rights_uid=name_get_uid)
|
||||
return self._search(domain, limit=limit, order=self._order, access_rights_uid=name_get_uid)
|
||||
|
||||
@@ -258,7 +258,7 @@ class MailActivityMixin(models.AbstractModel):
|
||||
|
||||
# explicitly check access rights, since we bypass the ORM
|
||||
self.check_access_rights('read')
|
||||
self._flush_search(domain, fields=[group_by_fname], order='id')
|
||||
self._flush_search(domain, fields=[group_by_fname])
|
||||
self.env['mail.activity'].flush_model(['res_model', 'res_id', 'user_id', 'date_deadline'])
|
||||
self.env['res.users'].flush_model(['partner_id'])
|
||||
self.env['res.partner'].flush_model(['tz'])
|
||||
|
||||
@@ -105,7 +105,7 @@ class MaintenanceEquipment(models.Model):
|
||||
args = args or []
|
||||
equipment_ids = []
|
||||
if name and operator not in expression.NEGATIVE_TERM_OPERATORS and operator != '=':
|
||||
equipment_ids = self._search([('name', '=', name)] + args, limit=limit, access_rights_uid=name_get_uid)
|
||||
equipment_ids = self._search([('name', '=', name)] + args, limit=limit, order=self._order, access_rights_uid=name_get_uid)
|
||||
return equipment_ids or super()._name_search(name, args, operator, limit, name_get_uid)
|
||||
|
||||
name = fields.Char('Equipment Name', required=True, translate=True)
|
||||
|
||||
@@ -506,19 +506,19 @@ class ProductProduct(models.Model):
|
||||
positive_operators = ['=', 'ilike', '=ilike', 'like', '=like']
|
||||
product_ids = []
|
||||
if operator in positive_operators:
|
||||
product_ids = list(self._search([('default_code', '=', name)] + args, limit=limit, access_rights_uid=name_get_uid))
|
||||
product_ids = list(self._search([('default_code', '=', name)] + args, limit=limit, order=self._order, access_rights_uid=name_get_uid))
|
||||
if not product_ids:
|
||||
product_ids = list(self._search([('barcode', '=', name)] + args, limit=limit, access_rights_uid=name_get_uid))
|
||||
product_ids = list(self._search([('barcode', '=', name)] + args, limit=limit, order=self._order, access_rights_uid=name_get_uid))
|
||||
if not product_ids and operator not in expression.NEGATIVE_TERM_OPERATORS:
|
||||
# Do not merge the 2 next lines into one single search, SQL search performance would be abysmal
|
||||
# on a database with thousands of matching products, due to the huge merge+unique needed for the
|
||||
# OR operator (and given the fact that the 'name' lookup results come from the ir.translation table
|
||||
# Performing a quick memory merge of ids in Python will give much better performance
|
||||
product_ids = list(self._search(args + [('default_code', operator, name)], limit=limit))
|
||||
product_ids = list(self._search(args + [('default_code', operator, name)], limit=limit, order=self._order))
|
||||
if not limit or len(product_ids) < limit:
|
||||
# we may underrun the limit because of dupes in the results, that's fine
|
||||
limit2 = (limit - len(product_ids)) if limit else False
|
||||
product2_ids = self._search(args + [('name', operator, name), ('id', 'not in', product_ids)], limit=limit2, access_rights_uid=name_get_uid)
|
||||
product2_ids = self._search(args + [('name', operator, name), ('id', 'not in', product_ids)], limit=limit2, order=self._order, access_rights_uid=name_get_uid)
|
||||
product_ids.extend(product2_ids)
|
||||
elif not product_ids and operator in expression.NEGATIVE_TERM_OPERATORS:
|
||||
domain = expression.OR([
|
||||
@@ -526,12 +526,12 @@ class ProductProduct(models.Model):
|
||||
['&', ('default_code', '=', False), ('name', operator, name)],
|
||||
])
|
||||
domain = expression.AND([args, domain])
|
||||
product_ids = list(self._search(domain, limit=limit, access_rights_uid=name_get_uid))
|
||||
product_ids = list(self._search(domain, limit=limit, order=self._order, access_rights_uid=name_get_uid))
|
||||
if not product_ids and operator in positive_operators:
|
||||
ptrn = re.compile('(\[(.*?)\])')
|
||||
res = ptrn.search(name)
|
||||
if res:
|
||||
product_ids = list(self._search([('default_code', '=', res.group(2))] + args, limit=limit, access_rights_uid=name_get_uid))
|
||||
product_ids = list(self._search([('default_code', '=', res.group(2))] + args, limit=limit, order=self._order, access_rights_uid=name_get_uid))
|
||||
# still no results, partner in context: search on supplier info as last hope to find something
|
||||
if not product_ids and self._context.get('partner_id'):
|
||||
suppliers_ids = self.env['product.supplierinfo']._search([
|
||||
@@ -540,9 +540,9 @@ class ProductProduct(models.Model):
|
||||
('product_code', operator, name),
|
||||
('product_name', operator, name)], access_rights_uid=name_get_uid)
|
||||
if suppliers_ids:
|
||||
product_ids = self._search([('product_tmpl_id.seller_ids', 'in', suppliers_ids)], limit=limit, access_rights_uid=name_get_uid)
|
||||
product_ids = self._search([('product_tmpl_id.seller_ids', 'in', suppliers_ids)], limit=limit, order=self._order, access_rights_uid=name_get_uid)
|
||||
else:
|
||||
product_ids = self._search(args, limit=limit, access_rights_uid=name_get_uid)
|
||||
product_ids = self._search(args, limit=limit, order=self._order, access_rights_uid=name_get_uid)
|
||||
return product_ids
|
||||
|
||||
@api.model
|
||||
|
||||
@@ -673,8 +673,10 @@ class Project(models.Model):
|
||||
|
||||
@api.model
|
||||
def _search(self, domain, offset=0, limit=None, order=None, access_rights_uid=None):
|
||||
if not order:
|
||||
return super()._search(domain, offset, limit, order, access_rights_uid)
|
||||
new_order, item_index, desc = [], -1, False
|
||||
for index, order_item in enumerate((order or self._order).split(',')):
|
||||
for index, order_item in enumerate(order.split(',')):
|
||||
order_item_list = order_item.strip().lower().split(' ')
|
||||
if order_item_list[0] == 'is_favorite':
|
||||
item_index = index
|
||||
@@ -683,7 +685,7 @@ class Project(models.Model):
|
||||
new_order.append(order_item)
|
||||
query = super()._search(domain, offset, limit, ', '.join(new_order), access_rights_uid)
|
||||
if item_index != -1:
|
||||
query_order_list = query.order.split(',')
|
||||
query_order_list = query.order.split(',') if query.order else []
|
||||
query_order_list.insert(item_index, f"""
|
||||
"project_project"."id" IN (
|
||||
SELECT project_id
|
||||
|
||||
@@ -19,7 +19,6 @@ class Project(models.Model):
|
||||
query = self.env['hr.expense']._search([])
|
||||
query.add_where('hr_expense.analytic_distribution ?| array[%s]', [str(account_id) for account_id in self.analytic_account_id.ids])
|
||||
|
||||
query.order = None
|
||||
query_string, query_param = query.select(
|
||||
'jsonb_object_keys(analytic_distribution) as account_id',
|
||||
'COUNT(DISTINCT(id)) as expense_count',
|
||||
@@ -76,7 +75,6 @@ class Project(models.Model):
|
||||
('move_id.expense_sheet_id', '!=', False),
|
||||
('id', 'not in', move_line_ids),
|
||||
])
|
||||
query.order = None
|
||||
return move_line_ids + list(query)
|
||||
|
||||
def _get_expenses_profitability_items(self, with_action=True):
|
||||
@@ -84,7 +82,6 @@ class Project(models.Model):
|
||||
return {}
|
||||
can_see_expense = with_action and self.user_has_groups('hr_expense.group_hr_expense_team_approver')
|
||||
query = self.env['hr.expense']._search([('state', 'in', ['approved', 'done'])])
|
||||
query.order = None
|
||||
query.add_where('hr_expense.analytic_distribution ? %s', [str(self.analytic_account_id.id)])
|
||||
query_string, query_param = query.select('array_agg(id) as ids', 'SUM(untaxed_amount) as untaxed_amount')
|
||||
self._cr.execute(query_string, query_param)
|
||||
|
||||
@@ -21,7 +21,6 @@ class Project(models.Model):
|
||||
query = self.env['purchase.order.line']._search([])
|
||||
query.add_where('purchase_order_line.analytic_distribution ?| array[%s]', [str(account_id) for account_id in self.analytic_account_id.ids])
|
||||
|
||||
query.order = None
|
||||
query_string, query_param = query.select(
|
||||
'jsonb_object_keys(analytic_distribution) as account_id',
|
||||
'COUNT(DISTINCT(order_id)) as purchase_order_count',
|
||||
@@ -127,7 +126,7 @@ class Project(models.Model):
|
||||
'|',
|
||||
('qty_invoiced', '>', 0),
|
||||
'|', ('qty_to_invoice', '>', 0), ('product_uom_qty', '>', 0),
|
||||
])
|
||||
], order=self.env['purchase.order.line']._order)
|
||||
query.add_where('purchase_order_line.analytic_distribution ? %s', [str(self.analytic_account_id.id)])
|
||||
query_string, query_param = query.select('"purchase_order_line".id', 'qty_invoiced', 'qty_to_invoice', 'product_uom_qty', 'price_unit', 'purchase_order_line.currency_id', '"purchase_order_line".analytic_distribution')
|
||||
self._cr.execute(query_string, query_param)
|
||||
@@ -173,7 +172,7 @@ class Project(models.Model):
|
||||
('parent_state', 'in', ['draft', 'posted']),
|
||||
('price_subtotal', '>', 0),
|
||||
('id', 'not in', purchase_order_line_invoice_line_ids),
|
||||
])
|
||||
], order=self.env['account.move.line']._order)
|
||||
query.add_where('account_move_line.analytic_distribution ? %s', [str(self.analytic_account_id.id)])
|
||||
# account_move_line__move_id is the alias of the joined table account_move in the query
|
||||
# we can use it, because of the "move_id.move_type" clause in the domain of the query, which generates the join
|
||||
|
||||
@@ -15,7 +15,6 @@ class Project(models.Model):
|
||||
can_see_expense = with_action and self.user_has_groups('hr_expense.group_hr_expense_team_approver')
|
||||
query = self.env['hr.expense']._search([('state', 'in', ['approved', 'done'])])
|
||||
query.add_where('hr_expense.analytic_distribution ? %s', [str(self.analytic_account_id.id)])
|
||||
query.order = None
|
||||
query_string, query_param = query.select('sale_order_id', 'product_id', 'array_agg(id) as ids', 'SUM(untaxed_amount) as untaxed_amount')
|
||||
query_string = f"{query_string} GROUP BY sale_order_id, product_id"
|
||||
self._cr.execute(query_string, query_param)
|
||||
|
||||
@@ -155,7 +155,6 @@ class Project(models.Model):
|
||||
def action_open_project_invoices(self):
|
||||
query = self.env['account.move.line']._search([('move_id.move_type', 'in', ['out_invoice', 'out_refund'])])
|
||||
query.add_where('analytic_distribution ? %s', [str(self.analytic_account_id.id)])
|
||||
query.order = None
|
||||
query_string, query_param = query.select('DISTINCT move_id')
|
||||
self._cr.execute(query_string, query_param)
|
||||
invoice_ids = [line.get('move_id') for line in self._cr.dictfetchall()]
|
||||
@@ -539,7 +538,6 @@ class Project(models.Model):
|
||||
def action_open_project_vendor_bills(self):
|
||||
query = self.env['account.move.line']._search([('move_id.move_type', 'in', ['in_invoice', 'in_refund'])])
|
||||
query.add_where('analytic_distribution ? %s', [str(self.analytic_account_id.id)])
|
||||
query.order = None
|
||||
query_string, query_param = query.select('DISTINCT move_id')
|
||||
self._cr.execute(query_string, query_param)
|
||||
vendor_bill_ids = [line.get('move_id') for line in self._cr.dictfetchall()]
|
||||
|
||||
@@ -115,7 +115,6 @@ class AccountMove(models.Model):
|
||||
continue
|
||||
MoveLines = self.env["account.move.line"].with_company(company_id)
|
||||
query = MoveLines._search(domain)
|
||||
query.order = None
|
||||
query_str, params = query.select(
|
||||
"SUM(debit) AS debit", "SUM(credit) AS credit"
|
||||
)
|
||||
|
||||
@@ -746,11 +746,11 @@ class IrServerObjectLines(models.Model):
|
||||
try:
|
||||
value = int(value)
|
||||
if not self.env[line.col1.relation].browse(value).exists():
|
||||
record = list(self.env[line.col1.relation]._search([], limit=1))
|
||||
value = record[0] if record else 0
|
||||
record = self.env[line.col1.relation].search([], limit=1)
|
||||
value = record.id or 0
|
||||
except ValueError:
|
||||
record = list(self.env[line.col1.relation]._search([], limit=1))
|
||||
value = record[0] if record else 0
|
||||
record = self.env[line.col1.relation].search([], limit=1)
|
||||
value = record.id or 0
|
||||
line.resource_ref = '%s,%s' % (line.col1.relation, value)
|
||||
else:
|
||||
line.resource_ref = False
|
||||
|
||||
@@ -652,7 +652,7 @@ actual arch.
|
||||
from_clause, where_clause, where_params = e.query.get_sql()
|
||||
assert from_clause == '"ir_ui_view"', f"Unexpected from clause: {from_clause}"
|
||||
|
||||
self._flush_search(domain, fields=['inherit_id', 'priority', 'model', 'mode'], order='id')
|
||||
self._flush_search(domain, fields=['inherit_id', 'priority', 'model', 'mode'])
|
||||
query = f"""
|
||||
WITH RECURSIVE ir_ui_view_inherits AS (
|
||||
SELECT id, inherit_id, priority, mode, model
|
||||
|
||||
@@ -45,7 +45,7 @@ class Bank(models.Model):
|
||||
domain = ['|', ('bic', '=ilike', name + '%'), ('name', operator, name)]
|
||||
if operator in expression.NEGATIVE_TERM_OPERATORS:
|
||||
domain = ['&'] + domain
|
||||
return self._search(domain + args, limit=limit, access_rights_uid=name_get_uid)
|
||||
return self._search(domain + args, limit=limit, order=self._order, access_rights_uid=name_get_uid)
|
||||
|
||||
@api.onchange('country')
|
||||
def _onchange_country_id(self):
|
||||
|
||||
@@ -87,12 +87,12 @@ class Country(models.Model):
|
||||
|
||||
ids = []
|
||||
if len(name) == 2:
|
||||
ids = list(self._search([('code', 'ilike', name)] + args, limit=limit))
|
||||
ids = list(self._search([('code', 'ilike', name)] + args, limit=limit, order=self._order))
|
||||
|
||||
search_domain = [('name', operator, name)]
|
||||
if ids:
|
||||
search_domain.append(('id', 'not in', ids))
|
||||
ids += list(self._search(search_domain + args, limit=limit))
|
||||
ids += list(self._search(search_domain + args, limit=limit, order=self._order))
|
||||
|
||||
return ids
|
||||
|
||||
@@ -180,11 +180,11 @@ class CountryState(models.Model):
|
||||
first_domain = [('code', '=ilike', name)]
|
||||
domain = [('name', operator, name)]
|
||||
|
||||
first_state_ids = self._search(expression.AND([first_domain, args]), limit=limit, access_rights_uid=name_get_uid) if first_domain else []
|
||||
first_state_ids = self._search(expression.AND([first_domain, args]), limit=limit, order=self._order, access_rights_uid=name_get_uid) if first_domain else []
|
||||
return list(first_state_ids) + [
|
||||
state_id
|
||||
for state_id in self._search(expression.AND([domain, args]),
|
||||
limit=limit, access_rights_uid=name_get_uid)
|
||||
limit=limit, order=self._order, access_rights_uid=name_get_uid)
|
||||
if state_id not in first_state_ids
|
||||
]
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@ class PartnerCategory(models.Model):
|
||||
# Be sure name_search is symetric to name_get
|
||||
name = name.split(' / ')[-1]
|
||||
args = [('name', operator, name)] + args
|
||||
return self._search(args, limit=limit, access_rights_uid=name_get_uid)
|
||||
return self._search(args, limit=limit, order=self._order, access_rights_uid=name_get_uid)
|
||||
|
||||
|
||||
class PartnerTitle(models.Model):
|
||||
|
||||
@@ -659,9 +659,9 @@ class Users(models.Model):
|
||||
domain = []
|
||||
else:
|
||||
domain = [('login', '=', name)]
|
||||
user_ids = self._search(expression.AND([domain, args]), limit=limit, access_rights_uid=name_get_uid)
|
||||
user_ids = self._search(expression.AND([domain, args]), limit=limit, order=self._order, access_rights_uid=name_get_uid)
|
||||
if not user_ids:
|
||||
user_ids = self._search(expression.AND([[('name', operator, name)], args]), limit=limit, access_rights_uid=name_get_uid)
|
||||
user_ids = self._search(expression.AND([[('name', operator, name)], args]), limit=limit, order=self._order, access_rights_uid=name_get_uid)
|
||||
return user_ids
|
||||
|
||||
def copy(self, default=None):
|
||||
|
||||
@@ -52,7 +52,7 @@ for name, field in MODELS:
|
||||
@api.model
|
||||
def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
|
||||
if isinstance(name, str) and name.split(':')[0] == self._name:
|
||||
return self._search([('value', operator, int(name.split(':')[1]))], access_rights_uid=name_get_uid)
|
||||
return self._search([('value', operator, int(name.split(':')[1]))], limit=limit, order=self._order, access_rights_uid=name_get_uid)
|
||||
else:
|
||||
return []
|
||||
|
||||
@@ -73,7 +73,7 @@ class One2ManyChild(models.Model):
|
||||
@api.model
|
||||
def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
|
||||
if isinstance(name, str) and name.split(':')[0] == self._name:
|
||||
return self._search([('value', operator, int(name.split(':')[1]))], access_rights_uid=name_get_uid)
|
||||
return self._search([('value', operator, int(name.split(':')[1]))], limit=limit, order=self._order, access_rights_uid=name_get_uid)
|
||||
else:
|
||||
return []
|
||||
|
||||
@@ -130,7 +130,7 @@ class Many2ManyChild(models.Model):
|
||||
@api.model
|
||||
def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
|
||||
if isinstance(name, str) and name.split(':')[0] == self._name:
|
||||
return self._search([('value', operator, int(name.split(':')[1]))], access_rights_uid=name_get_uid)
|
||||
return self._search([('value', operator, int(name.split(':')[1]))], limit=limit, order=self._order, access_rights_uid=name_get_uid)
|
||||
else:
|
||||
return []
|
||||
|
||||
|
||||
+1
-1
@@ -4659,7 +4659,7 @@ class Many2many(_RelationalMulti):
|
||||
context.update(self.context)
|
||||
comodel = records.env[self.comodel_name].with_context(**context)
|
||||
domain = self.get_domain_list(records)
|
||||
comodel._flush_search(domain)
|
||||
comodel._flush_search(domain, order=comodel._order)
|
||||
wquery = comodel._where_calc(domain)
|
||||
comodel._apply_ir_rules(wquery, 'read')
|
||||
order_by = comodel._generate_order_by(None, wquery)
|
||||
|
||||
+22
-14
@@ -1469,7 +1469,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
list to match all records.
|
||||
:param limit: maximum number of record to count (upperbound) (default: all)
|
||||
"""
|
||||
query = self._search(domain, limit=limit, order='id')
|
||||
query = self._search(domain, limit=limit)
|
||||
return len(query)
|
||||
|
||||
@api.model
|
||||
@@ -1488,7 +1488,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
:returns: at most ``limit`` records matching the search criteria
|
||||
:raise AccessError: if user is not allowed to access requested information
|
||||
"""
|
||||
query = self._search(domain, offset=offset, limit=limit, order=order)
|
||||
query = self._search(domain, offset=offset, limit=limit, order=order or self._order)
|
||||
return self.browse(query)
|
||||
|
||||
#
|
||||
@@ -1604,7 +1604,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
aggregator = expression.AND if operator in expression.NEGATIVE_TERM_OPERATORS else expression.OR
|
||||
domain = aggregator([[(field_name, operator, name)] for field_name in search_fnames])
|
||||
args += domain
|
||||
return self._search(args, limit=limit, access_rights_uid=name_get_uid)
|
||||
return self._search(args, limit=limit, order=self._order, access_rights_uid=name_get_uid)
|
||||
|
||||
@api.model
|
||||
def _add_missing_default_values(self, values):
|
||||
@@ -2273,7 +2273,7 @@ class BaseModel(metaclass=MetaModel):
|
||||
for gb in annotated_groupbys:
|
||||
select_terms.append('%s as "%s" ' % (gb['qualified_field'], gb['groupby']))
|
||||
|
||||
self._flush_search(domain, fields=fnames + groupby_fields)
|
||||
self._flush_search(domain, fields=fnames + groupby_fields, order=order)
|
||||
|
||||
groupby_terms, orderby_terms = self._read_group_prepare(order, aggregated_fields, annotated_groupbys, query)
|
||||
from_clause, where_clause, where_clause_params = query.get_sql()
|
||||
@@ -4535,7 +4535,11 @@ class BaseModel(metaclass=MetaModel):
|
||||
|
||||
@api.model
|
||||
def _flush_search(self, domain, fields=None, order=None, seen=None):
|
||||
""" Flush all the fields appearing in `domain`, `fields` and `order`. """
|
||||
""" Flush all the fields appearing in `domain`, `fields` and `order`.
|
||||
|
||||
Note that ``order=None`` actually means no order, so if you expect some
|
||||
fallback order, you have to provide it yourself.
|
||||
"""
|
||||
if seen is None:
|
||||
seen = set()
|
||||
elif self._name in seen:
|
||||
@@ -4588,14 +4592,15 @@ class BaseModel(metaclass=MetaModel):
|
||||
collect_from_domain(self, domain)
|
||||
|
||||
# flush the order fields
|
||||
order_spec = order or self._order
|
||||
for order_part in order_spec.split(','):
|
||||
order_field = order_part.split()[0]
|
||||
field = self._fields.get(order_field)
|
||||
if field is not None:
|
||||
to_flush[self._name].add(order_field)
|
||||
if field.relational:
|
||||
self.env[field.comodel_name]._flush_search([], seen=seen)
|
||||
if order:
|
||||
for order_part in order.split(','):
|
||||
order_field = order_part.split()[0]
|
||||
field = self._fields.get(order_field)
|
||||
if field is not None:
|
||||
to_flush[self._name].add(order_field)
|
||||
if field.relational:
|
||||
comodel = self.env[field.comodel_name]
|
||||
comodel._flush_search([], order=comodel._order, seen=seen)
|
||||
|
||||
if self._active_name:
|
||||
to_flush[self._name].add(self._active_name)
|
||||
@@ -4620,6 +4625,8 @@ class BaseModel(metaclass=MetaModel):
|
||||
by specifying ``access_rights_uid=1`` to bypass access rights check, but not ir.rules!
|
||||
This is ok at the security level because this method is private and not callable through XML-RPC.
|
||||
|
||||
No default order is applied when the method is invoked without parameter ``order``.
|
||||
|
||||
:param access_rights_uid: optional user ID to use when checking access rights
|
||||
(not for ir.rules, this is only for ir.model.access)
|
||||
:return: a collection of record ids (may be a ``Query`` object)
|
||||
@@ -4637,7 +4644,8 @@ class BaseModel(metaclass=MetaModel):
|
||||
query = self._where_calc(domain)
|
||||
self._apply_ir_rules(query, 'read')
|
||||
|
||||
query.order = self._generate_order_by(order, query).replace('ORDER BY ', '')
|
||||
if order:
|
||||
query.order = self._generate_order_by(order, query).replace('ORDER BY ', '')
|
||||
query.limit = limit
|
||||
query.offset = offset
|
||||
|
||||
|
||||
@@ -555,7 +555,7 @@ class expression(object):
|
||||
records = records.search([(parent_name, 'in', records.ids)], order='id')
|
||||
domain = [('id', 'in', list(child_ids))]
|
||||
if prefix:
|
||||
return [(left, 'in', left_model._search(domain, order='id'))]
|
||||
return [(left, 'in', left_model._search(domain))]
|
||||
return domain
|
||||
|
||||
def parent_of_domain(left, ids, left_model, parent=None, prefix=''):
|
||||
@@ -583,7 +583,7 @@ class expression(object):
|
||||
records = records[parent_name]
|
||||
domain = [('id', 'in', list(parent_ids))]
|
||||
if prefix:
|
||||
return [(left, 'in', left_model._search(domain, order='id'))]
|
||||
return [(left, 'in', left_model._search(domain))]
|
||||
return domain
|
||||
|
||||
HIERARCHY_FUNCS = {'child_of': child_of_domain,
|
||||
@@ -707,12 +707,12 @@ class expression(object):
|
||||
raise NotImplementedError('auto_join attribute not supported on field %s' % field)
|
||||
|
||||
elif len(path) > 1 and field.store and field.type == 'many2one':
|
||||
right_ids = comodel.with_context(active_test=False)._search([(path[1], operator, right)], order='id')
|
||||
right_ids = comodel.with_context(active_test=False)._search([(path[1], operator, right)])
|
||||
push((path[0], 'in', right_ids), model, alias)
|
||||
|
||||
# Making search easier when there is a left operand as one2many or many2many
|
||||
elif len(path) > 1 and field.store and field.type in ('many2many', 'one2many'):
|
||||
right_ids = comodel.with_context(**field.context)._search([(path[1], operator, right)], order='id')
|
||||
right_ids = comodel.with_context(**field.context)._search([(path[1], operator, right)])
|
||||
push((path[0], 'in', right_ids), model, alias)
|
||||
|
||||
elif not field.store:
|
||||
@@ -727,10 +727,10 @@ class expression(object):
|
||||
else:
|
||||
# Let the field generate a domain.
|
||||
if len(path) > 1:
|
||||
right = comodel._search([(path[1], operator, right)], order='id')
|
||||
right = comodel._search([(path[1], operator, right)])
|
||||
operator = 'in'
|
||||
domain = field.determine_domain(model, operator, right)
|
||||
model._flush_search(domain, order='id')
|
||||
model._flush_search(domain)
|
||||
|
||||
for elem in normalize_domain(domain):
|
||||
push(elem, model, alias, internal=True)
|
||||
@@ -766,7 +766,7 @@ class expression(object):
|
||||
else:
|
||||
ids2 = [right]
|
||||
if inverse_is_int and domain:
|
||||
ids2 = comodel._search([('id', 'in', ids2)] + domain, order='id')
|
||||
ids2 = comodel._search([('id', 'in', ids2)] + domain)
|
||||
|
||||
if inverse_field.store:
|
||||
# In the condition, one must avoid subqueries to return
|
||||
@@ -817,7 +817,7 @@ class expression(object):
|
||||
# determine ids2 in comodel
|
||||
ids2 = to_ids(right, comodel, leaf)
|
||||
domain = HIERARCHY_FUNCS[operator]('id', ids2, comodel)
|
||||
ids2 = comodel._search(domain, order='id')
|
||||
ids2 = comodel._search(domain)
|
||||
|
||||
# rewrite condition in terms of ids2
|
||||
if comodel == model:
|
||||
|
||||
+6
-13
@@ -223,20 +223,13 @@ class Query(object):
|
||||
return bool(self.get_result_ids())
|
||||
|
||||
def __len__(self):
|
||||
if self._ids is None and not self.offset:
|
||||
from_clause, where_clause, params = self.get_sql()
|
||||
where_clause = where_clause or 'TRUE'
|
||||
if self.limit:
|
||||
# apply the limit, and wrap the query into a more efficient one
|
||||
query_str = f'''
|
||||
SELECT COUNT(*) FROM (
|
||||
SELECT FROM {from_clause}
|
||||
WHERE {where_clause}
|
||||
LIMIT {int(self.limit)}
|
||||
) t
|
||||
'''
|
||||
if self._ids is None:
|
||||
if self.limit or self.offset:
|
||||
# optimization: generate a SELECT FROM, and then count the rows
|
||||
query_str, params = self.select('')
|
||||
query_str = f'SELECT COUNT(*) FROM ({query_str}) t'
|
||||
else:
|
||||
query_str = f'SELECT COUNT(*) FROM {from_clause} WHERE {where_clause}'
|
||||
query_str, params = self.select('COUNT(*)')
|
||||
self._cr.execute(query_str, params)
|
||||
return self._cr.fetchone()[0]
|
||||
return len(self.get_result_ids())
|
||||
|
||||
Reference in New Issue
Block a user