python Django 报告选项
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1088738/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Django Reporting Options
提问by yuben
I want to create a new "Business" application using the Django framework. Any suggestions as to what I can use as a reporting framework? The application will need to generate reports on various business entities including summaries, totals, grouping, etc. Basically, is there a Crystal reports-like equivalent for Django/Python?
我想使用 Django 框架创建一个新的“业务”应用程序。关于我可以用作报告框架的任何建议?该应用程序需要生成关于各种业务实体的报告,包括汇总、总计、分组等。基本上,是否有类似 Crystal 报告的 Django/Python 等价物?
采纳答案by Daniel Naab
There is a grid on djangopackages.com which may be of use evaluating options:
djangopackages.com 上有一个网格,可以用来评估选项:
回答by Bufke
I made django-report-builder. It lets you build ORM queries with a gui and generate spreadsheet reports. It can't do templates, that would be a great feature to add though.
我做了django-report-builder。它允许您使用 gui 构建 ORM 查询并生成电子表格报告。它不能做模板,不过这将是一个很好的添加功能。
回答by S.Lott
These are just HTML templates with ordinary view functions.
这些只是带有普通视图函数的 HTML 模板。
This doesn't require much: Parameters come in from a form; write the query in the view function, passing the queryset to the template. The template presents the report.
这不需要太多:参数来自表单;在视图函数中编写查询,将查询集传递给模板。模板显示报告。
Why would you need something more than this?
为什么你需要比这更多的东西?
You can use generic list/detail viewsto save yourself from having to write as much code. If you go this route, you provide the query set and the template to a generic view that handles some of the processing for you.
您可以使用通用列表/详细信息视图来避免编写尽可能多的代码。如果你走这条路,你将查询集和模板提供给一个为你处理一些处理的通用视图。
Since you must write the query in Crystal reports or Django, you're not really getting much leverage from a "reporting" tool.
由于您必须在 Crystal 报告或 Django 中编写查询,因此您并没有真正从“报告”工具中获得多少优势。
回答by mrts
Building upon @s-lott's suggestion, you can add reports to the admin site using a proxy model, a model admin class with custom changelist_view()
and a custom template that derives from admin/base_site.html
.
根据@s-lott 的建议,您可以使用代理模型、具有自定义的模型管理类changelist_view()
和派生自admin/base_site.html
.
Assuming Django v2.1 (for model view permissions) and a classic customers, products and sales domain, here's a full example:
假设 Django v2.1(用于模型视图权限)和一个经典的客户、产品和销售域,这里有一个完整的例子:
- Create a proxy model to show the report in admin index page:
class SalesReport(SalesOrder): class Meta: proxy = True
- Create a model admin class for the model:
@admin.register(SalesReport) class SalesReportAdmin(admin.ModelAdmin): ...
- Implement the report view:
def sales_report(self, request): monthly_products_by_customer_sql = ''' SELECT c.name AS customer, p.name AS product, COUNT(DISTINCT o.id) AS total_orders, SUM(oi.quantity) AS total_products, SUM(oi.quantity * oi.price) AS total_amount FROM sales_salesorder o INNER JOIN customers_customer c ON o.customer_id = c.id INNER JOIN sales_salesorderitem oi ON o.id = oi.sales_order_id INNER JOIN products_product p ON oi.product_id = p.id WHERE o.departure_date >= %s AND o.departure_date <= %s GROUP BY c.id, p.id ORDER BY total_amount DESC; ''' start, end = get_previous_month_start_end_date() with connection.cursor() as cursor: cursor.execute(monthly_products_by_customer_sql, (start, end)) results = namedtuplefetchall(cursor) totals = Totals( total_orders=sum(r.total_orders for r in results), total_products=sum(r.total_products for r in results), total_amount=sum(r.total_amount for r in results), ) context = dict( self.admin_site.each_context(request), title=f'Sales report for {start} - {end}', period_start=start, period_end=end, results=results, totals=totals, ) return TemplateResponse(request, 'sales/report.html', context)
- Return the report view from
changelist_view()
, wrapping it intoadmin_site.admin_view()
to protect it from unauthorized accessdef changelist_view(self, request): return self.admin_site.admin_view(self.sales_report)(request)
Remove add, change, delete permissions so that only view permission remains and protect change and history views:
def has_add_permission(self, request): return False def has_change_permission(self, request, obj=None): return False def has_delete_permission(self, request, obj=None): return False def change_view(self, *args, **kwargs): raise PermissionDenied def history_view(self, *args, **kwargs): raise PermissionDenied
Helpers and imports for the
sales_report()
view are as follows:from collections import namedtuple from django.core.exceptions import PermissionDenied from django.db import connection from django.template.response import TemplateResponse Totals = namedtuple('Totals', ['total_orders', 'total_products', 'total_amount']) def namedtuplefetchall(cursor): '''Return all rows from a cursor as a namedtuple''' desc = cursor.description nt_result = namedtuple('Result', [col[0] for col in desc]) return [nt_result(*row) for row in cursor.fetchall()] def get_previous_month_start_end_date(): today = datetime.date.today() prev_month_last = datetime.date(today.year, today.month, 1) - datetime.timedelta(1) prev_month_first = datetime.date(prev_month_last.year, prev_month_last.month, 1) return prev_month_first, prev_month_last
- Add the following template to
sales/report.html
, deriving fromadmin/base_site.html
to use the admin layout:{% extends "admin/base_site.html" %} {% block content %} <div id="content-main"><div class="results"> <table> <thead> <tr> <th scope="col"><div class="text">Customer</div></th> <th scope="col"><div class="text">Product</div></th> <th scope="col"><div class="text"># orders</div></th> <th scope="col"><div class="text"># products</div></th> <th scope="col"><div class="text">Amount </div></th> </tr> </thead> <tbody> {% for result in results %} <tr class="row1"> <td>{{ result.customer }}</td> <td>{{ result.product }}</td> <td>{{ result.total_orders }}</td> <td>{{ result.total_products }}</td> <td>{{ result.total_amount|floatformat:2 }}</td> </tr> {% endfor %} <tr class="row1" style="font-weight: bold"> <td> </td><td> </td> <td>{{ totals.total_orders }}</td> <td>{{ totals.total_products }}</td> <td>{{ totals.total_amount|floatformat:2 }}</td> </tr> </tbody> </table> </div></div> {% endblock %}
- 创建代理模型以在管理索引页面中显示报告:
class SalesReport(SalesOrder): class Meta: proxy = True
- 为模型创建模型管理类:
@admin.register(SalesReport) class SalesReportAdmin(admin.ModelAdmin): ...
- 实现报表视图:
def sales_report(self, request): monthly_products_by_customer_sql = ''' SELECT c.name AS customer, p.name AS product, COUNT(DISTINCT o.id) AS total_orders, SUM(oi.quantity) AS total_products, SUM(oi.quantity * oi.price) AS total_amount FROM sales_salesorder o INNER JOIN customers_customer c ON o.customer_id = c.id INNER JOIN sales_salesorderitem oi ON o.id = oi.sales_order_id INNER JOIN products_product p ON oi.product_id = p.id WHERE o.departure_date >= %s AND o.departure_date <= %s GROUP BY c.id, p.id ORDER BY total_amount DESC; ''' start, end = get_previous_month_start_end_date() with connection.cursor() as cursor: cursor.execute(monthly_products_by_customer_sql, (start, end)) results = namedtuplefetchall(cursor) totals = Totals( total_orders=sum(r.total_orders for r in results), total_products=sum(r.total_products for r in results), total_amount=sum(r.total_amount for r in results), ) context = dict( self.admin_site.each_context(request), title=f'Sales report for {start} - {end}', period_start=start, period_end=end, results=results, totals=totals, ) return TemplateResponse(request, 'sales/report.html', context)
- 从 返回报表视图
changelist_view()
,将其包装到其中admin_site.admin_view()
以防止未经授权的访问def changelist_view(self, request): return self.admin_site.admin_view(self.sales_report)(request)
删除添加、更改、删除权限,以便仅保留查看权限并保护更改和历史视图:
def has_add_permission(self, request): return False def has_change_permission(self, request, obj=None): return False def has_delete_permission(self, request, obj=None): return False def change_view(self, *args, **kwargs): raise PermissionDenied def history_view(self, *args, **kwargs): raise PermissionDenied
sales_report()
视图的助手和导入如下:from collections import namedtuple from django.core.exceptions import PermissionDenied from django.db import connection from django.template.response import TemplateResponse Totals = namedtuple('Totals', ['total_orders', 'total_products', 'total_amount']) def namedtuplefetchall(cursor): '''Return all rows from a cursor as a namedtuple''' desc = cursor.description nt_result = namedtuple('Result', [col[0] for col in desc]) return [nt_result(*row) for row in cursor.fetchall()] def get_previous_month_start_end_date(): today = datetime.date.today() prev_month_last = datetime.date(today.year, today.month, 1) - datetime.timedelta(1) prev_month_first = datetime.date(prev_month_last.year, prev_month_last.month, 1) return prev_month_first, prev_month_last
- 将以下模板添加到
sales/report.html
,派生自admin/base_site.html
以使用管理布局:{% extends "admin/base_site.html" %} {% block content %} <div id="content-main"><div class="results"> <table> <thead> <tr> <th scope="col"><div class="text">Customer</div></th> <th scope="col"><div class="text">Product</div></th> <th scope="col"><div class="text"># orders</div></th> <th scope="col"><div class="text"># products</div></th> <th scope="col"><div class="text">Amount </div></th> </tr> </thead> <tbody> {% for result in results %} <tr class="row1"> <td>{{ result.customer }}</td> <td>{{ result.product }}</td> <td>{{ result.total_orders }}</td> <td>{{ result.total_products }}</td> <td>{{ result.total_amount|floatformat:2 }}</td> </tr> {% endfor %} <tr class="row1" style="font-weight: bold"> <td> </td><td> </td> <td>{{ totals.total_orders }}</td> <td>{{ totals.total_products }}</td> <td>{{ totals.total_amount|floatformat:2 }}</td> </tr> </tbody> </table> </div></div> {% endblock %}
Now the report will be listed in admin index page with view-only icon , it is protected from unauthorized access and has a consistent look with the rest of the admin site.
现在,该报告将列在管理索引页面中,带有仅查看图标 ,它受到保护,不会受到未经授权的访问,并且与管理站点的其余部分具有一致的外观。
回答by zzr
EditIt really looks like both packages are gone, but now we have a nice data structure, borrowed from R -- DataFramein pandas package Quick tutorial(pay attention to section "Grouping")
编辑看起来两个包都不见了,但现在我们有了一个很好的数据结构,从 R 借来的——DataFramein pandas 包 快速教程(注意“分组”部分)
I don't know about complete reporting solution for Django (or Python), but make reporting with Django is quite easy with or without ORM:
我不知道 Django(或 Python)的完整报告解决方案,但是无论是否使用 ORM,使用 Django 进行报告都非常容易:
- django-tables can give you very basic structure for handling table data (asc/desc server-side sorting etc)
- you can use standart django 1.1 queryset aggregates (django-reporting uses them) for totals/subtotals stuff.
- django-tables 可以为您提供用于处理表数据的非常基本的结构(asc/desc 服务器端排序等)
- 您可以将标准 django 1.1 查询集聚合(django-reporting 使用它们)用于总计/小计内容。
Personally I use django-tables and neithere's datashapingpython package for quick summary/avg/median/IQR/filtering stuff because I have many different data sources (REST data, two mysql dbs, csv files from R) with only few of them in django db now.
我个人使用 django-tables 和 nonee 的datashapingpython 包来快速汇总/avg/median/IQR/filtering 东西,因为我有许多不同的数据源(REST 数据、两个 mysql dbs、来自 R 的 csv 文件),其中只有少数在 django 中db 现在。
Pycha is one of candidates for me to draw simple charts.
Pycha 是我绘制简单图表的候选人之一。
I don't like client-side ajax-based grids etc for reporting, but you can use it with django templates too.
我不喜欢基于 ajax 的客户端网格等进行报告,但您也可以将它与 django 模板一起使用。