import pypinyin from odoo import fields, models, api from odoo.models import BaseModel import re _origin_search = BaseModel._search _origin_read_group = BaseModel._read_group class CharWithPinyin(fields.Char): pass # 把字段挂到 fields 命名空间 fields.CharWithPinyin = CharWithPinyin _origin_setup_fields = BaseModel._setup_fields def _my_setup_fields(self): _origin_setup_fields(self) # 把字段挂到 fields 命名空间 cls = self.env.registry[self._name] for name in list(cls._fields): field = self._fields[name] # if isinstance(field, CharWithPinyin): if type(field) == fields.CharWithPinyin: new_field_name = f'{name}_pinyin' compute_method_name = f'_compute_{new_field_name}' if new_field_name not in cls._fields: def _compute_func(self, fn=name, nfn=new_field_name): for rec in self: value = rec[fn] or '' rec[nfn] = ''.join(pypinyin.lazy_pinyin(value)) setattr(cls, compute_method_name, api.depends(name)(_compute_func)) cls._add_field( self, new_field_name, fields.Char( compute=_compute_func, store=True, index=True, string=f"{field.string or name}拼音", ) ) BaseModel._setup_fields = _my_setup_fields def _replace_char_with_pinyin_order(self, order): """ 将排序字符串中属于 CharWithPinyin 的字段替换为对应 _pinyin 字段 """ if not order: return order # 拆分多字段排序,例如 "name desc, create_date asc" parts = [p.strip() for p in order.split(',')] new_parts = [] for part in parts: # 分离字段名和排序方向 m = re.match(r'([^\s]+)(?:\s+(asc|desc))?$', part, re.IGNORECASE) if not m: new_parts.append(part) continue field_name, direction = m.groups() field = self._fields.get(field_name) # if field and isinstance(field, fields.CharWithPinyin): if field and type(field) == fields.CharWithPinyin: # 替换成 _pinyin 字段 field_name = f"{field_name}_pinyin" if direction: new_parts.append(f"{field_name} {direction}") else: new_parts.append(field_name) return ', '.join(new_parts) # 重写 _search def _my_search(self, domain, offset=0, limit=None, order=None, access_rights_uid=None): order = _replace_char_with_pinyin_order(self, order) return _origin_search(self, domain, offset=offset, limit=limit, order=order, access_rights_uid=access_rights_uid) # 重写 _read_group def _my_read_group(self, domain, groupby=(), aggregates=(), having=(), offset=0, limit=None, order=None): order = _replace_char_with_pinyin_order(self, order) return _origin_read_group(self, domain, groupby=groupby, aggregates=aggregates, having=having, offset=offset, limit=limit, order=order) # Patch BaseModel BaseModel._search = _my_search BaseModel._read_group = _my_read_group