Five essential Odoo development methods you must know and master

search, name_search, search_count, search_read, read_group methods

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

关于我们

​我们致力于帮助中小企业实现数字化转型,我们的团队由一群充满激情和创新思维的专业人士组成,他们具备丰富的行业经验和技术专长。

扫一扫获取顾问以及手册

归档
Sign in to leave a comment
Odoo Recruitment Process Guide: A Full-Cycle Closed Loop from Talent Sourcing to Employee Onboarding
Modern enterprises commonly face challenges in the recruitment process such as cumbersome procedures, low communication efficiency, and scattered candidate data.