name_search
简介
用于通过在网页界面的输入框输入的文字,进行记录匹配并范围匹配的数据记录集((id1,name1),(id2,name2))结构的结果集。
源码
@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`` pattern when compared with the given ``operator``, while also
matching the optional search domain (``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.
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.
:param str name: the name pattern to match
:param list args: optional search domain (see :meth:`~.search` for
syntax), specifying further restrictions
:param str operator: domain operator for matching ``name``, such as
``'like'`` or ``'='``.
:param int limit: optional max number of records to return
:rtype: list
:return: list of pairs ``(id, text_repr)`` for all matching records.
"""
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))
@api.model
def _search(self, args, offset=0, limit=None, order=None, count=False, access_rights_uid=None):
"""
Private implementation of search() method, allowing specifying the uid to use for the access right check.
This is useful for example when filling in the selection list for a drop-down and avoiding access rights errors,
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.
: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 list of record ids or an integer (if count is True)
"""
self.sudo(access_rights_uid or self._uid).check_access_rights('read')
if expression.is_false(self, args):
# optimization: no need to query, as no record satisfies the domain
return 0 if count else []
query = self._where_calc(args)
self._apply_ir_rules(query, 'read')
order_by = self._generate_order_by(order, query)
from_clause, where_clause, where_clause_params = query.get_sql()
where_str = where_clause and (" WHERE %s" % where_clause) or ''
if count:
# Ignore order, limit and offset when just counting, they don't make sense and could
# hurt performance
query_str = 'SELECT count(1) FROM ' + from_clause + where_str
self._cr.execute(query_str, where_clause_params)
res = self._cr.fetchone()
return res[0]
limit_str = limit and ' limit %d' % limit or ''
offset_str = offset and ' offset %d' % offset or ''
query_str = 'SELECT "%s".id FROM ' % self._table + from_clause + where_str + order_by + limit_str + offset_str
self._cr.execute(query_str, where_clause_params)
res = self._cr.fetchall()
# TDE note: with auto_join, we could have several lines about the same result
# i.e. a lead with several unread messages; we uniquify the result using
# a fast way to do it while preserving order (http://www.peterbe.com/plog/uniqifiers-benchmark)
def _uniquify_list(seq):
seen = set()
return [x for x in seq if x not in seen and not seen.add(x)]
return _uniquify_list([x[0] for x in res])
def browse(self, arg=None, prefetch=None):
""" browse([ids]) -> records
Returns a recordset for the ids provided as parameter in the current
environment.
Can take no ids, a single id or a sequence of ids.
"""
ids = _normalize_ids(arg)
#assert all(isinstance(id, IdType) for id in ids), "Browsing invalid ids: %s" % ids
return self._browse(ids, self.env, prefetch)
@classmethod
def _browse(cls, ids, env, prefetch=None, add_prefetch=True):
""" Create a recordset instance.
:param ids: a tuple of record ids
:param env: an environment
:param prefetch: an optional prefetch object
"""
records = object.__new__(cls)
records.env = env
records._ids = ids
if prefetch is None:
prefetch = defaultdict(set) # {model_name: set(ids)}
records._prefetch = prefetch
if add_prefetch:
prefetch[cls._name].update(ids)
return records
def lazy_name_get(self):
""" Evaluate self.name_get() lazily. """
# names
names = tools.lazy(lambda: dict(self.name_get()))
return [(rid, tools.lazy(operator.getitem, names, rid)) for rid in self.ids]
分析
- 首先被搜索的模型必须有name字段或者维护_rec_name属性。
- name_search 方法调用_name_search 方法,
- 在_name_search方法中传入name到args(domain)中。
- _name_search调用_search方法传入 args(domain),limit(默认80条),access_rights_uid(uid) ,首先进行Read单据记录权限校验,校验通过后通过各种SQL转换拼接进行有效记录查询,返回记录的ids。
- 接着通过browser搜索上面的ids获取到记录集。
- lazy_name_get简单理解为直接执行了一个 self.name_get()方法就好了。
- 所以可以看出 name_search最后的返回结果,其实是name_get()的返回值
def name_search(*args,**kwargs): ... return name_get(*args, **kwargs)