odoo中分组查询函数read_group

本文详细介绍了read_group函数,包括其功能是获取按给定字段分组的列表视图中的记录列表,还阐述了各参数含义,如domain指定搜索条件、groupby可按日期/日期时间字段的天、周、月、季、年分组等,最后说明函数返回字典列表。

read_group函数

read_group中英文注释解释

    def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True):
        """
        Get the list of records in list view grouped by the given ``groupby`` fields

        :param domain: list specifying search criteria [['field_name', 'operator', 'value'], ...]
        :param list fields: list of fields present in the list view specified on the object
        :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.
        :param int offset: optional number of records to skip
        :param int limit: optional max number of records to return
        :param list orderby: optional ``order by`` specification, for
                             overriding the natural sort ordering of the
                             groups, see also :py:meth:`~osv.osv.osv.search`
                             (supported only for many2one fields currently)
        :param bool lazy: if true, the results are only grouped by the first groupby and the 
                remaining groupbys are put in the __context key.  If false, all the groupbys are
                done in one 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
        """
              result = self._read_group_raw(domain, fields, groupby, offset=offset, limit=limit, orderby=orderby, lazy=lazy)

        groupby = [groupby] if isinstance(groupby, basestring) else list(OrderedSet(groupby))
        dt = [
            f for f in groupby
            if self._fields[f.split(':')[0]].type in ('date', 'datetime')
        ]

        # 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` instead of a (value, label) pair. In that case,
                # leave the `False` value alone
                if group.get(df):
                    group[df] = group[df][1]
        return result
  

获取按给定groupby字段分组的列表视图中的记录列表

:param domain:list指定搜索条件[[‘field_name’,‘operator’,‘value’],…]
:param list fields:对象上指定的列表视图中存在的字段列表
:param list groupby:将记录分组的groupby描述列表。
groupby描述是一个字段(然后它将按该字段分组)
或字符串’field:groupby_function’。现在,支持的唯一功能
是’天’,‘周’,‘月’,‘季’或’年’,它们只是有意义的
日期/日期时间字段。
:param int offset:要跳过的可选记录数
:param int limit:可选的最大返回记录数
:param list orderby:可选order by规范,for
重写自然排序顺序
组,另见:py:meth:~osv.osv.osv.search
(目前仅支持多个字段)
:param bool lazy:如果为true,则结果仅按第一个groupby和
剩余的groupbys放在__context键中。如果为false,则所有组都是
在一个电话中完成。
:return:字典列表(每个记录一个字典)包含:

                *由``groupby``参数中的字段分组的字段值
                * __domain:指定搜索条件的元组列表
                * __context:带有``groupby``之类的参数的字典
    :rtype:[{'field_name_1':value,...]
    :引发AccessError:*如果用户对请求的对象没有读取权限
                        *如果用户试图绕过访问规则以读取请求的对象

我们可以看到对日期中’天’,‘周’,‘月’,'季’或’年’可以进行分组


chart_data = self.env['task_stage_data_analysis'].read_group(domain=
[('project', '=', project_id)], 
                                                                  fields=['current_date'],      
                                                                   groupby='current_date:month')
<think>嗯,我现在需要帮用户解决关于Odoo14中sale.report源码实现机制的问题。用户之前提到了站内引用,里面涉及BI系统的组件,比如ETL、数据仓库、OLAP和报表工具,这些在Odoo中的实现方式。用户现在特别关注销售分析模块(sale.report)的源码和实现机制。首先,我应该回顾Odoo的BI模块结构。之前的引用提到odoo使用PostgreSQL作为数据仓库,支持MDX查询,以及用pyChart做报表工具。销售分析可能也是基于这些组件构建的。sale.report通常是一个SQL视图,那么它的源码可能在哪里呢?根据经验,Odoo的报表一般位于models文件夹下的report子目录中,比如addons/sale/models/report/sale_report.py。接下来需要分解实现机制的几个部分。首先是数据模型,也就是继承report.sale.order的sale.report类。用户提到它是transient模型,或者可能是AbstractModel,但实际查看源码时发现它继承的是models.Model。这里要注意,Odoo的报表视图通常使用SQL视图作为基础,所以应该检查_sql_query的构建过程。然后是字段定义,比如name、date、product_id等。用户可能想知道这些字段如何映射到SQL查询的列。另外,状态字段的枚举值也需要解释,比如sale_order_state。接着是自动计算和分组功能。sale.report依赖_post_execute方法在模块安装时创建SQL视图,这里涉及到ORM的重载。需要确认该方法调用的时机,比如在init中执行,以及如何调用_sql_query来创建视图。时间范围过滤器部分涉及覆盖_read_group方法,用户提到时间粒度的动态切换(日/月/年)。这里要说明odoo如何利用date_trunc函数处理不同的时间区间,以及条件处理何时根据group_by变化。查询优化和索引部分,用户关注性能问题。可能包括外键索引(如order_id和product_id),以及日期字段的索引是否有必要。需要解释物化视图是否由系统自动处理,还是需要手动创建,但Odoo的sale.report是否使用了物化视图?可能只是普通视图,然后依赖定时计算或手动更新。接下来考虑关联的视图文件,比如数据如何通过视图展示。XML文件定义报表的树状图、图表、透视表视图,以及时间范围筛选器的作用机制。需要说明图表和透视表如何通过group_by字段处理数据聚合。最后总结实现机制的核心点:SQL视图动态生成、ORM桥接、时间处理逻辑、自动计算依赖安装钩子。同时还要回答用户可能的疑问,比如销售状态枚举值的扩展,或者如何优化大数据量下的报表性能(例如通过物化视图或定期计划任务)。用户还可能提到时间范围和过滤器的问题,例如如何自动应用当前时间范围,这时候需要解释read_group方法的重写,以及时间粒度的处理。此外,用户可能关心销售分析中的状态过滤(如sale_order_state),如何将不同状态映射到特定值,比如“draft”设为“报价”,而“sale”为“订单”,这里需要参考state字段的定义。总结时,应强调源码位置和核心机制,确保用户理解从SQL查询构建到前端展示的流程,以及可能进行的扩展或优化方法。</think> 在Odoo 14中,`sale.report`(销售分析报表)的实现机制主要基于**SQL视图+ORM模型+动态聚合**的技术组合。以下是核心源码实现机制的分析(基于`addons/sale/models/report/sale_report.py`): ### 一、核心实现步骤 1. **SQL视图定义** 在`_sale_report_sql()`方法中定义原始SQL查询,聚合销售订单、订单行、发票、产品等信息: ```python def _sale_report_sql(self): return """ SELECT ... FROM sale_order_line sol JOIN sale_order so ON so.id = sol.order_id LEFT JOIN product_product product ON product.id = sol.product_id LEFT JOIN product_template template ON template.id = product.product_tmpl_id LEFT JOIN account_move_line aml ON aml.sale_line_id = sol.id LEFT JOIN account_move am ON am.id = aml.move_id WHERE ... GROUP BY so.id, sol.id, ... -- 多维度聚合 """ ``` 2. **ORM模型声明** 定义`sale.report`模型(本质是**只读SQL视图的ORM抽象层**): ```python class SaleReport(models.Model): _name = 'sale.report' _description = 'Sales Analysis Report' _auto = False # 禁止自动创建数据库表 _rec_name = 'order_id' _order = 'date desc' # 关键字段定义(匹配SQL查询的SELECT列) order_id = fields.Many2one('sale.order', string='Order') product_id = fields.Many2one('product.product', string='Product') state = fields.Selection([ # 销售状态枚举 ('draft', 'Quotation'), ('sale', 'Confirmed Order'), ('done', 'Locked'), ('cancel', 'Cancelled') ]) date = fields.Datetime(string='Order Date') price_subtotal = fields.Float('Untaxed Revenue') qty_delivered = fields.Float('Delivered Quantity') # 其他聚合字段... ``` 3. **视图初始化与数据加载** 在模块安装时通过`init()`方法创建SQL视图: ```python def init(self): tools.drop_view_if_exists(self.env.cr, self._table) self.env.cr.execute(f"CREATE VIEW {self._table} AS {self._sale_report_sql()}") ``` --- ### 二、关键机制详解 1. **动态聚合** 通过覆盖`_read_group()`方法实现**时间范围聚合**(日/周/月/年): ```python def _read_group(self, domain, groupby, ...): if 'date' in groupby: # 自动将日期截断到对应粒度(如按月聚合) groupby = list(groupby) groupby.remove('date') groupby = groupby + ["date_trunc('month', date)"] # 动态修改groupby return super()._read_group(domain, groupby, ...) ``` 2. **销售状态映射** 在SQL视图中将订单状态转换为易读的枚举值: ```sql CASE WHEN so.state = 'draft' AND so.is_expired THEN 'expired' WHEN so.state = 'draft' AND not so.is_expired THEN 'draft' WHEN so.state = 'sent' THEN 'sent' ELSE so.state END AS state ``` 3. **多表关联与计算字段** 聚合来自`sale.order`, `sale.order.line`, `account_move`等表的数据: ```sql SUM(sol.price_subtotal) AS price_subtotal, SUM(sol.qty_delivered) AS qty_delivered ``` --- ### 三、前端展示控制 在视图定义文件(如`sale_report_views.xml`)中配置: ```xml <record id="view_order_sales_report_tree" model="ir.ui.view"> <field name="name">销售分析报表</field> <field name="model">sale.report</field> <field name="arch" type="xml"> <tree> <field name="order_id"/> <field name="product_id"/> <field name="date" group_operator="month"/> <!-- 按月份聚合 --> <field name="price_subtotal" sum="合计"/> </tree> </field> </record> ``` --- ### 四、核心源码文件位置 - **模型定义**: `/addons/sale/models/report/sale_report.py` - **视图定义**: `/addons/sale/views/sale_report_views.xml` - **数据加载**: 通过`init()`钩子自动创建SQL视图 > ⚠️ **性能注意事项**:大数据量时需优化SQL视图(通过`CREATE MATERIALIZED VIEW`实现物化视图或添加索引),原始实现依赖视图的实时计算 [^1]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值