一个用于从查询创建简单统计数据的Django包
项目描述
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', )
来获取类似以下内容
安装
从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_aggregate、query_aggregate_date、choice_aggregate和choice_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_single、query_aggregate、choice_aggregate、choice_aggregate_with_null、query_aggregate_date、query_aggregate_buckets。
下面是一个完整的示例。
响应将是一个具有与StatSet实例相同属性的字典列表。
请注意,声明性API将创建一个字典并实际调用get_stats函数,因此在这两种情况下结果将完全相同。
示例声明性
请记住,在下面的示例中,如果您没有传递field参数,则将使用属性名称。默认情况下,method是count。
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_date和QueryAggregateExtractDateStat
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 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 77e36eb2216954b293582792ff349338609234b10abd56db7251c5be0674afea |
|
MD5 | 31f60b61b3ea8a98746c8114c9d92c4a |
|
BLAKE2b-256 | 73a20f60b2ef97191fc05e91b6754dcd04f9e699384fb244ae5db8e3cb0c4f64 |