It mainly includes the following methods and their main uses:
search(): Called in the search view
search_count(): Called when counting records in a view
name_search(): called when searching in many2one field
search_read(): called when clicking "search more" on a many2one field
read_group(): Called when grouping the search view
search()
The search method contains several sub-methods.
Retrieve data records that meet the query conditions based on the domain
Special usage of the active field, use active_test=False to bypass
The count attribute can directly perform count statistics without needing search_count.
The _uniquify_list method will deduplicate the ids, meaning that when ids are the same, only one result will be found.
search_count()
Perform statistical counting based on the search results. If counting is needed, you can directly add the count=true attribute during the search, which makes the calculation faster.
@api.model
def search_read(self, domain=None, fields=None, offset=0, limit=None, order=None):
TRANSLATED SEGMENT:
Performs a ``search()`` followed by a ``read()``. First search then read
:param domain: Search domain, see ``args`` parameter in ``search()``. Defaults to an empty domain that will match all records.
Query condition; if empty, query all by default.
:param fields: List of fields to read, see ``fields`` parameter in ``read()``. Defaults to all fields.
Fields to query, default to all fields
:param offset: Number of records to skip, see ``offset`` parameter in ``search()``. Defaults to 0.
Amount of data skipped
:param limit: 要返回的最大记录数,参见 ``search()`` 中的 ``limit`` 参数。默认无限制。
Number of query results
:param order: 对结果进行排序的列,参见 ``search()`` 中的 ``order`` 参数。默认不排序。
Sort criteria
:return: List of dictionaries containing the asked fields.
Returns a list of dictionary values for the field
:rtype: List of dictionaries. A list containing dictionaries
TRANSLATED SEGMENT:
records = self.search(domain or [], offset=offset, limit=limit, order=order)
if not records:
return []
if fields and fields == ['id']:
# shortcut read if we only want the ids
return [{'id': record.id} for record in records]
# read() ignores active_test, but it would forward it to any downstream search call
# (e.g. for x2m or function fields), and this is not the desired behavior, the flag
# was presumably only meant for the main search().
# read() ignores active_test, but it will forward it to any downstream search calls (e.g., for x2m or function words,
# This is not the expected behavior, this flag may only apply to the main search().
# TODO: Move this to read() directly?
if 'active_test' in self._context:
context = dict(self._context)
del context['active_test']
records = records.with_context(context)
result = records.read(fields)
if len(result)
return result
# reorder read
index = {vals['id']: vals for vals in result}
return [index[record.id] for record in records if record.id in index]
name_search()
It is also queried by calling the _search method.
@api.model
def name_search(self, name='', args=None, operator='ilike', limit=100):
name_search(name='', args=None, operator='ilike', limit=100) -> records
Search for records that have a display name matching the given
``name`` 模式在与给定的 ``operator`` 进行比较时,同时
matching the optional search domain (``args``).
Search for records with a display name matching the given name "name" pattern, when compared with the given "operator" pattern, also matches the optional search field (' ' args ' ').
This is used for example to provide suggestions based on a partial
value for a relational field. Sometimes be seen as the inverse
function of :meth:`~.name_get`, but it is not guaranteed to be.
For example, it is used to provide suggested values for relational fields based on partial content. It is sometimes seen as the inverse function of '~.name_get', but this is not guaranteed.
This method is equivalent to calling :meth:`~.search` with a search
domain based on ``display_name`` and then :meth:`~.name_get` on the
result of the search.
This method is equivalent to calling :meth:' ~.search to search for the domain, followed by :meth: ' ~.name_get ' on the search results.
:param str name: the name pattern to match Name used for searching
:param list args: optional search domain (see :meth:`~.search` for
syntax), specifying further restrictions Search criteria
:param str operator: domain operator for matching ``name``, such as
``'like'`` or ``'='``. Condition: like, =
:param int limit: optional max number of records to return Number of records to query
:rtype: list
:return: list of pairs ``(id, text_repr)`` for all matching records.
TRANSLATED SEGMENT:
return self._name_search(name, args, operator, limit=limit)
@api.model
def _name_search(self, name='', args=None, operator='ilike', limit=100, name_get_uid=None):
# private implementation of name_search, allows passing a dedicated user
# for the name_get part to solve some access rights issues
args = list(args or [])
# optimize out the default criterion of ``ilike ''`` that matches everything
if not self._rec_name:
_logger.warning("Cannot execute name_search, no _rec_name defined on %s", self._name)
elif not (name == '' and operator == 'ilike'):
args += [(self._rec_name, operator, name)]
access_rights_uid = name_get_uid or self._uid
ids = self._search(args, limit=limit, access_rights_uid=access_rights_uid)
recs = self.browse(ids)
return lazy_name_get(recs.sudo(access_rights_uid))
read_group()
Used when grouping data
@api.model
def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True):
TRANSLATED SEGMENT:
Get the list of records in list view grouped by the given ``groupby`` fields
Get the list of records grouped by the given "groupby" field in the list view
:param domain: list specifying search criteria [['field_name', 'operator', 'value'], ...]
domain condition
:param list fields: list of fields present in the list view specified on the object.
Each element is either 'field' (field name, using the default aggregation),
or 'field:agg' (aggregate field with aggregation function 'agg'),
or 'name:agg(field)' (聚合字段使用 'agg' 并作为 'name' 返回)。
The possible aggregation functions are the ones provided by PostgreSQL
(https://www.postgresql.org/docs/current/static/functions-aggregate.html)
and 'count_distinct', with the expected meaning.
Fields displayed after grouping
:param list groupby: list of groupby descriptions by which the records will be grouped.
A groupby description is either a field (then it will be grouped by that field)
or a string 'field:groupby_function'. Right now, the only functions supported
are 'day', 'week', 'month', 'quarter' or 'year', and they only make sense for
date/datetime fields.
Grouping conditions
:param int offset: optional number of records to skip
Skip how many query records
:param int limit: 可选的最大返回记录数
Number of records returned
:param list orderby: optional ``order by`` specification, for
overriding the natural sort ordering of the
groups, see also :py:meth:`~osv.osv.osv.search`
(目前仅支持多对一字段)
Sort criteria
:param bool lazy: if true, the results are only grouped by the first groupby and the
剩余的groupby被放入__context键中。如果为false,所有groupby都将
done in one call.
Whether to abandon lazy loading: If true, the result is only grouped by the first groupby and the remaining groups are placed in the __context key. If false, all groups are handled in a single call.
:return: list of dictionaries (one dictionary for each record) containing:
* the values of fields grouped by the fields in ``groupby`` argument
* __domain: list of tuples specifying the search criteria
* __context: dictionary with argument like ``groupby``
:rtype: [{'field_name_1': value, ...]
:raise AccessError: * if user has no read rights on the requested object
* if user tries to bypass access rules for read on the requested object
TRANSLATED SEGMENT:
result = self._read_group_raw(domain, fields, groupby, offset=offset, limit=limit, orderby=orderby, lazy=lazy)
groupby = [groupby] if isinstance(groupby, pycompat.string_types) else list(OrderedSet(groupby))
dt = [
f for f in groupby
if self._fields[f.split(':')[0]].type in ('date', 'datetime') # e.g. 'date:month'
]
# iterate on all results and replace the "full" date/datetime value
# (range, label) by just the formatted label, in-place
for group in result:
for df in dt:
# could group on a date(time) field which is empty in some
# records, in which case as with m2o the _raw value will be
# `False` 而不是一个 (值, 标签) 对。在这种情况下,
# leave the `False` value alone
if group.get(df):
group[df] = group[df][1]
return result
@api.model
def _read_group_raw(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True):
self.check_access_rights('read')
# Parse domain into SQL query statement
query = self._where_calc(domain)
# Extract the field storing the database
fields = fields or [f.name for f in self._fields.values() if f.store]
groupby = [groupby] if isinstance(groupby, pycompat.string_types) else list(OrderedSet(groupby))
groupby_list = groupby[:1] if lazy else groupby
annotated_groupbys = [self._read_group_process_groupby(gb, query) for gb in groupby_list]
groupby_fields = [g['field'] for g in annotated_groupbys]
order = orderby or ','.join([g for g in groupby_list])
groupby_dict = {gb['groupby']: gb for gb in annotated_groupbys}
self._apply_ir_rules(query, 'read')
for gb in groupby_fields:
assert gb in self._fields, "Unknown field %r in 'groupby'" % gb
gb_field = self._fields[gb].base_field
assert gb_field.store and gb_field.column_type, "Fields in 'groupby' must be regular database-persisted fields (no function or related fields), or function fields with store=True"
aggregated_fields = []
select_terms = []
for fspec in fields:
if fspec == 'sequence':
continue
match = regex_field_agg.match(fspec)
if not match:
raise UserError(_("Invalid field specification %r.") % fspec)
name, func, fname = match.groups()
if func:
# we have either 'name:func' or 'name:func(fname)'
fname = fname or name
field = self._fields[fname]
if not (field.base_field.store and field.base_field.column_type):
raise UserError(_("Cannot aggregate field %r.") % fname)
if not func.isidentifier():
raise UserError(_("Invalid aggregation function %r.") % func)
else:
# we have 'name', retrieve the aggregator on the field
field = self._fields.get(name)
if not (field and field.base_field.store and
field.base_field.column_type and field.group_operator):
continue
func, fname = field.group_operator, name
if fname in groupby_fields:
continue
if name in aggregated_fields:
raise UserError(_("Output name %r is used twice.") % name)
aggregated_fields.append(name)
expr = self._inherits_join_calc(self._table, fname, query)
if func.lower() == 'count_distinct':
term = 'COUNT(DISTINCT %s) AS "%s"' % (expr, name)
else:
term = '%s(%s) AS "%s"' % (func, expr, name)
select_terms.append(term)
for gb in annotated_groupbys:
select_terms.append('%s as "%s" ' % (gb['qualified_field'], gb['groupby']))
groupby_terms, orderby_terms = self._read_group_prepare(order, aggregated_fields, annotated_groupbys, query)
from_clause, where_clause, where_clause_params = query.get_sql()
if lazy and (len(groupby_fields) >= 2 or not self._context.get('group_by_no_leaf')):
count_field = groupby_fields[0] if len(groupby_fields) >= 1 else '_'
else:
count_field = '_'
count_field += '_count'
prefix_terms = lambda prefix, terms: (prefix + " " + ",".join(terms)) if terms else ''
prefix_term = lambda prefix, term: ('%s %s' % (prefix, term)) if term else ''
query = """
SELECT min("%(table)s".id) AS id, count("%(table)s".id) AS "%(count_field)s" %(extra_fields)s
FROM %(from)s
%(where)s
%(groupby)s
%(orderby)s
%(limit)s
%(offset)s
% {
'table': self._table,
'count_field': count_field,
'extra_fields': prefix_terms(',', select_terms),
'from': from_clause,
'where': prefix_term('WHERE', where_clause),
'groupby': prefix_terms('GROUP BY', groupby_terms),
'orderby': prefix_terms('ORDER BY', orderby_terms),
'limit': prefix_term('LIMIT', int(limit) if limit else None),
'offset': prefix_term('OFFSET', int(offset) if limit else None),
}
self._cr.execute(query, where_clause_params)
fetched_data = self._cr.dictfetchall()
if not groupby_fields:
return fetched_data
self._read_group_resolve_many2one_fields(fetched_data, annotated_groupbys)
data = [{k: self._read_group_prepare_data(k, v, groupby_dict) for k, v in r.items()} for r in fetched_data]
if self.env.context.get('fill_temporal') and data:
data = self._read_group_fill_temporal(data, groupby, aggregated_fields,
annotated_groupbys)
result = [self._read_group_format_result(d, annotated_groupbys, groupby, domain) for d in data]
if lazy:
# Right now, read_group only fill results in lazy mode (by default).
# If you need to have the empty groups in 'eager' mode, then the
# method _read_group_fill_results need to be completely reimplemented
# in a sane way
result = self._read_group_fill_results(
domain, groupby_fields[0], groupby[len(annotated_groupbys):],
aggregated_fields, count_field, result, read_group_order=order,
)
return result
Original link: https://blog.csdn.net/tsoTeo/article/details/105728776
