跳转到主要内容

一个用于从查询创建简单统计数据的Django包

项目描述

https://badge.fury.io/py/django-simple-stats.svg https://github.com/spapas/django-simple-stats/actions/workflows/ghtox.yml/badge.svg

django-simple-stats

一个从查询创建统计数据的Django包。此包应与所有Django版本 > 3.x 兼容

您可以使用类似以下内容

class TravelStats(StatSet):
    total = QueryAggregateSingleStat(label='Total')
    amount = QueryAggregateSingleStat(label='Total amount', method='sum')
    port__name = QueryAggregateStat(label='By port', )

来获取类似以下内容

How does it look

安装

从pip安装

pip install django-simple-stats

或从git的最新版本安装

pip install git+https://github.com/spapas/django-simple-stats

不需要其他安装。

统计类型

  • query_aggregate_single / QueryAggregateSingleStat 将在字段上运行聚合函数并返回单个值。例如,您可以获取查询的行总数或所有字段的和。

  • query_aggregate / QueryAggregateStat 将在字段上运行聚合函数并返回值列表。您可以通过传递 aggr_field(因此您可以根据字段分组并返回每个组的另一个字段的和)在 不同字段 上运行聚合函数。这主要用于外键,并且如果您在查询中有不同的值,这也很有用。例如,按用户统计行数。这对于布尔值也很有用,例如获取打开和关闭标志的行数。

  • 《choice_aggregate》/《ChoiceAggregateStat》与《query_aggregate》类似,但会使用一个《choices》属性来返回更美观的值。这将不会返回Null值。

  • 《choice_aggregate_with_null》/《ChoiceAggregateNullStat》与《choice_aggregate》相同,但会返回Null值(因此您可以在您的《choices》中添加一个《(None, "Empty")》选择项)。

  • 《query_aggregate_date》/《QueryAggregateDateStat》与《query_aggregate》类似,但会返回特定日期字段的聚合值;使用《what》传递《year》、《month》、《day》。

  • 《query_aggregate_datetime》/《QueryAggregateDateTimeStat》与《query_aggregate_date》类似,但还会返回时间上的聚合值。

  • 《query_aggregate_extract_date》/《QueryAggregateExtractDateStat》与《query_aggregate_date》类似,但会使用《Extract》来处理日期而不是《Trunc》。如果您想按月/日/小时等特定值进行分组,这将非常有用,即这将把六月的所有行放在同一行中,而《query_aggregate_date》会将6月21日与6月22日和6月23日区分开来。

  • 最后,使用《query_aggregate_buckets》//《QueryAggregateBucketsStat》可以创建值桶。您将传递一个桶列表,查询将返回属于每个桶的结果。统计模块将为每个值运行单独的查询,使用《field__gte》。例如,如果您传递《[100, 50, 10]》,并且您有一个《price》字段,它将运行《price__gte=100》、《price__gte=50》、《price__gte=10》并返回结果。

用法

可以使用声明式(类似于Django模型/表单的工作方式)和功能API。

对于声明式API,您需要声明一个继承自《simple_stats.StatSet》的类,并使用相应的统计类(如《simple_stats.QueryAggregateStat》)在该类中声明统计字段。然后,您将在Django视图中初始化此类,传递给它查询。例如

from simple_stats import StatSet, QueryAggregateSingleStat

class MyStats(StatSet):
    id = QueryAggregateSingleStat(label='Total number')

# Then in your view
stats = MyStats(query=MyModel.objects.all())

统计类接受以下初始化参数

  • field(可选):aggreate将运行的字段;使用《__》进行连接,即《field1__field2》

  • label(可选):此统计的文本描述(如果没有传递,将使用字段填充)

  • method(可选):聚合方法。可以是《count》、《sum》、《max》、《min》、《avg》之一。默认为count

  • what(可选):仅对《query_aggregate_date》是必需的,它是《year》、《month》、《day》

  • choices(可选):仅对《choice_aggregate》和《choice_aggregate_with_null》是必需的,它必须是Django选择列表

  • buckets(可选):仅对《query_aggregate_buckets》是必需的。必须是从最大值到最小值的列表。

  • aggr_field(可选):此字段为可选字段,可用于query_aggregatequery_aggregate_datechoice_aggregatechoice_aggregate_with_null。它表示将要运行聚合函数的字段。

  • formatter(可选):一个回调函数,可以用来格式化值,它应该接收一个值并返回一个渲染后的值(例如 lambda v: "{}".format(v)

请注意,如果您没有传递field参数,则将使用属性名称(例如,在上面的示例中,它将是field=id)。

StatSet实例将是一个可枚举的对象,返回一个包含以下属性的字典列表

  • label:与配置中的标签相同

  • value:如果您使用query_aggregate_single,则将有一个值,否则将为None

  • values:对于query_aggregate_single将为空,否则将为元组列表。每个元组将有两个元素,(label, value)

另一方面,功能API中唯一支持的方法是simple_stats.get_stats。它期望一个Django查询和统计配置(字典列表)。配置列表的每个元素都是一个具有与类初始化参数相同属性的字典。只有两个区别

  • 现在字段是必需的

  • 我们必须传递所需的聚合类型(类似于我们在声明性API中使用的类)。这里的选项有:query_aggregate_singlequery_aggregatechoice_aggregatechoice_aggregate_with_nullquery_aggregate_datequery_aggregate_buckets

下面是一个完整的示例。

响应将是一个具有与StatSet实例相同属性的字典列表。

请注意,声明性API将创建一个字典并实际调用get_stats函数,因此在这两种情况下结果将完全相同。

示例声明性

请记住,在下面的示例中,如果您没有传递field参数,则将使用属性名称。默认情况下,methodcount

from simple_stats import from simple_stats import (
    StatSet,
    QueryAggregateStat,
    QueryAggregateSingleStat,
    ChoiceAggregateStat,
    QueryAggregateDateStat,
    QueryAggregateBucketsStat,
)

class MyStats(StatSet):
    id = QueryAggregateSingleStat(label='Total number')
    price = QueryAggregateSingleStat(label='Total price', method='sum')
    pilot_authority__name = QueryAggregateStat(label='Per authority')
    pilot_authority__name = QueryAggregateStat(label='Per authority by price', aggr_field='price')
    status = ChoiceAggregateStat(label='Per status', choices=MyModel.STATUS_CHOICES)
    status_price = ChoiceAggregateStat(
        label='Per status by price',
        choices=MyModel.STATUS_CHOICES,
        field='status',
        aggr_field='price'
        formatter=lambda v: "€ {}".format(v) if v else '-'
    )
    year = QueryAggregateDateStat(label='Per year', what='year', field='created_on')
    year_price = QueryAggregateDateStat(label='Per year by price', what='year', aggr_field='price', field='created_on')
    buckets = QueryAggregateBucketsStat(label='Buckets', buckets=[100, 50, 10])

def my_view(request):
    qs = TestModel.objects.all()

    stats = MyStats(qs)
    return render(request, 'my_template.html', {'stats': stats})

统计结果将类似于以下可枚举对象

[
  {'label': 'Total', 'values': [], 'value': 1216},
  {'label': 'Total price', 'values': [], 'value': 323.16},
  {'label': 'Per authority', 'values': [('Authority 1', 200), ('Authority 2', 9),   ], 'value': None},
  {'label': 'Per authority by price', 'values': [('Authority 1', 123.23), ('Authority 2', 42.12),   ], 'value': None},
  {'label': 'Per status', 'values': [('New', 200), ('Cancel', 0)], 'value': None},
  {'label': 'Per status by price', 'values': [('New', '€ 32.01'), ('Cancel', '€ 44.23')], 'value': None},
  {'label': 'Per year', 'values': [(2021, 582), (2022, 634)], 'value': None}
  {'label': 'Per year by price', 'values': [(2021, 5.82), (2022, 6.34)], 'value': None}
  {'label': 'Per price', 'values': [('> 5000', 1), ('> 1000', 29), ('> 500', 86), ('> 0', 305)], 'value': None}
]

您可以使用类似以下内容在模板中显示它(使用bootstrap)

<div class='row'>
  {% for s in stats %}
  <div class='col-md-4 mb-5' style='max-height: 500px; overflow: auto;'>
      <h4>{{ s.label }}</h4>
      {% if s.values %}
          <table class='table table-condensed table-striped small table-sm'>
              {% for v in s.values %}
                  <tr>
                      <td>{{ v.0 }}</td>
                      <td>{{ v.1 }}</td>
                  </tr>
              {% endfor %}
          </table>
      {% else %}
          <b>{{ s.value }}</b>
      {% endif %}
  </div>
  {% endfor %}
</div>

示例功能

from simple_stats import get_stats

STATS_CFG = cfg = [
        {
            'kind': 'query_aggregate_single',
            'label': 'Total',
            'field': 'id',
        }, {
            'kind': 'query_aggregate_single',
            'label': 'Total price',
            'method': 'sum',
            'field': 'price',
        }, {
            'kind': 'query_aggregate',
            'label': 'Per authority',
            'field': 'pilot_authority__name',
        }, {
            'kind': 'query_aggregate',
            'label': 'Per authority by price',
            'field': 'pilot_authority__name',
            'aggr_field': 'price',
            'formatter': lambda v: "€ {}".format(v) if v else '-'
        }, {
            'kind': 'choice_aggregate',
            'label': 'Per status',
            'field': 'status',
            'choices': models.STATUS_CHOICES,
        }, {
            'kind': 'choice_aggregate',
            'label': 'Per status by price',
            'field': 'status',
            'aggr_field': 'price',
            'choices': models.STATUS_CHOICES,
        }, {
            'kind': 'query_aggregate_date',
            'label': 'Per year',
            'field': 'created_on',
            'what': 'year',
        }, {
            'kind': 'query_aggregate_date',
            'label': 'Per year by price',
            'field': 'created_on',
            'what': 'year',
            'aggr_field': 'price',
        }, {
            'kind': 'query_aggregate_buckets',
            'label': 'Per price',
            'field': 'price',
            'buckets': [100_00, 50_00, 1_000, 500, 0]
        }
    ]

def my_view(request):
    qs = TestModel.objects.all()

    stats = get_stats(qs, STATS_CFG)
    return render(request, 'my_template.html', {'stats': stats})

统计结果将是与声明性示例相似的字典数组。

导出统计结果

您可以使用xlwt库和此函数轻松地将这些统计结果导出到xls(https://pypi.ac.cn/project/xlwt/

import xlwt

def create_xls_resp(stats, response):
    context = self.get_context_data()
    import xlwt
    wb = xlwt.Workbook(encoding="utf-8")
    for stat in stats:
        ws = wb.add_sheet(stat["label"][:31])
        ws.write(0,0,stat["label"], xlwt.easyxf('font: name Calibri, bold on', ))
        if stat["value"]:
            ws.write(0,1,stat["value"], xlwt.easyxf('font: name Calibri, bold on', ))

        for i, val in enumerate(stat["values"], start=2):
            for j,v in enumerate(val, start=0):
                ws.write(i,j,v)
    wb.save(response)

现在您可以从视图调用它

from django.http import HttpResponse

def my_export_view(request):
    qs = TestModel.objects.all()

    stats = get_stats(qs, STATS_CFG)
    response = HttpResponse(content_type="application/ms-excel")
    response["Content-Disposition"] = "attachment; filename=export.xls"
    create_xls_resp(response)
    return response

变更日志

  • v.0.7.0:添加query_aggregate_extract_dateQueryAggregateExtractDateStat

  • v.0.6.0:添加测试!

  • v.0.5.1:允许为值添加格式化程序

  • v.0.5.0:添加声明性API

  • v.0.4.0:允许使用aggr_field在不同的字段上运行聚合函数

  • v.0.3.1:修复与choice_aggregate_with_null相关的小错误

  • v.0.3.0:添加choice_aggregate_with_null并在找不到统计类型时抛出异常

  • v.0.2.1:修复与列别名相关的小错误

  • v.0.2.0:更改API;使用query_aggregate_datetime对日期时间字段进行操作,使用query_aggregate_date对日期字段进行操作

  • v.0.1.0:初始版本

项目详情


下载文件

下载适合您平台文件。如果您不确定选择哪个,请了解有关 安装包 的更多信息。

源代码发行版

django-simple-stats-0.7.0.tar.gz (11.0 kB 查看哈希值)

上传时间 源代码

由以下组织支持