Django 报告选项

发布于 2024-07-26 10:12:13 字数 139 浏览 0 评论 0 原文

我想使用 Django 框架创建一个新的业务应用程序。 关于我可以使用什么作为报告框架有什么建议吗? 该应用程序需要生成有关各种业务实体的报告,包括摘要、总计、分组等。基本上,Django/Python 是否有类似于 Crystal reports 的等效项?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

蓝眼泪 2024-08-02 10:12:13

djangopackages.com 上有一个网格,可用于评估选项:

https://www.djangopackages.com。 djangopackages.com/grids/g/reporting/

There is a grid on djangopackages.com which may be of use evaluating options:

https://www.djangopackages.com/grids/g/reporting/

倚栏听风 2024-08-02 10:12:13

我制作了 django-report-builder。 它允许您使用 GUI 构建 ORM 查询并生成电子表格报告。 它不能做模板,但这将是一个很好的功能。

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.

如梦 2024-08-02 10:12:13

根据 @s-lott 的建议,您可以使用代理模型、具有自定义 changelist_view() 的模型管理类以及派生自 admin/base_site 的自定义模板,将报告添加到管理站点.html

假设 Django v2.1(用于模型视图权限)和一个经典的客户、产品和销售域,这是一个完整的示例:

  1. 创建一个代理模型以在管理索引页面中显示报告:
    类 SalesReport(SalesOrder): 
          类元: 
              代理=真 
      
  2. 为模型创建模型管理类:
    @admin.register(销售报告) 
      类 SalesReportAdmin(admin.ModelAdmin): 
          ... 
      
  3. 实现报表视图:
    def sales_report(自我,请求): 
          Monthly_products_by_customer_sql = ''' 
          选择 c.name 作为客户, 
                 p.name AS 产品, 
                 COUNT(DISTINCT o.id) AS 总订单数, 
                 SUM(oi.数量) AS 总产品, 
                 SUM(oi.数量 * oi.价格) AS 总金额 
          FROM sales_salesorder o 
          INNER JOIN 客户_客户 c ON o.客户_id = c.id 
          内连接 sales_salesorderitem oi ON o.id = oi.sales_order_id 
          INNER JOIN products_product p ON oi.product_id = p.id 
          其中 o.departure_date >= %s 并且 o.departure_date <= %s 
          按 c.id、p.id 分组 
          按总金额 DESC 排序; 
          ''' 
          开始,结束 = get_previous_month_start_end_date() 
          使用 connection.cursor() 作为光标: 
              光标.执行(monthly_products_by_customer_sql,(开始,结束)) 
              结果=namedtuplefetchall(光标) 
              总计 = 总计( 
                  Total_orders=sum(结果中 r 的 r.total_orders), 
                  Total_products=sum(结果中 r 的 r.total_products), 
                  Total_amount=sum(r.结果中r的total_amount), 
              ) 
              上下文=字典( 
                  self.admin_site.each_context(请求), 
                  title=f'{start} - {end} 的销售报告', 
                  period_start=开始, 
                  period_end=结束, 
                  结果=结果, 
                  总计=总计, 
              ) 
              返回 TemplateResponse(请求, 'sales/report.html', 上下文) 
      
  4. changelist_view() 返回报告视图,将其包装到 admin_site.admin_view() 中以防止未经授权的访问
    def changelist_view(self, request): 
          返回 self.admin_site.admin_view(self.sales_report)(请求) 
      
  5. 删除添加、更改、删​​除权限,以便仅保留查看权限并保护更改和历史视图:

    def has_add_permission(self, request): 
          返回错误 
    
      def has_change_permission(self, request, obj=None): 
          返回错误 
    
      def has_delete_permission(self, request, obj=None): 
          返回错误 
    
      def change_view(self, *args, **kwargs): 
          提出权限被拒绝 
    
      def History_view(self, *args, **kwargs): 
          提出权限被拒绝 
      
  6. sales_report() 视图的帮助程序和导入如下:

    从集合导入namedtuple 
      从 django.core.exceptions 导入 PermissionDenied     
      从 django.db 导入连接 
      从 django.template.response 导入 TemplateResponse 
    
      总计 = nametuple('总计', ['total_orders', 'total_products', 'total_amount']) 
    
      def nametuplefetchall(光标): 
          '''将游标中的所有行作为命名元组返回''' 
          desc = 光标.描述 
          nt_result = nametuple('结果', [col[0] for col in desc]) 
          返回 [cursor.fetchall() 中的行的 nt_result(*row)] 
    
      def get_previous_month_start_end_date(): 
          今天 = 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) 
          返回前一个月的第一个、前一个月的最后一个 
      
  7. 将以下模板添加到 sales/report.html,派生自 admin/base_site.html 使用管理布局:
    {% 扩展“admin/base_site.html” %} 
      {% 块内容 %} 
      
    <表> <头>
    客户
    产品
    # 订单
    # 个产品
    金额 €
    <正文> {% 结果中的结果 %} {{ result.customer }} {{ result.product }} {{ result.total_orders }} {{ result.total_products }} {{ result.total_amount|floatformat:2 }} {% 结束 %}   {{total_orders }} {{total_products }} {{totals.total_amount|floatformat:2 }}
    {% 末端嵌段 %}

现在,该报告将在管理索引页面中列出,并带有仅供查看的图标

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.

Assuming Django v2.1 (for model view permissions) and a classic customers, products and sales domain, here's a full example:

  1. Create a proxy model to show the report in admin index page:
    class SalesReport(SalesOrder):
        class Meta:
            proxy = True
    
  2. Create a model admin class for the model:
    @admin.register(SalesReport)
    class SalesReportAdmin(admin.ModelAdmin):
        ...
    
  3. 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)
    
  4. Return the report view from changelist_view(), wrapping it into admin_site.admin_view() to protect it from unauthorized access
    def changelist_view(self, request):
        return self.admin_site.admin_view(self.sales_report)(request)
    
  5. 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
    
  6. 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
    
  7. Add the following template to sales/report.html, deriving from admin/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 %}
    

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.

⊕婉儿 2024-08-02 10:12:13

这些只是具有普通视图功能的 HTML 模板。

这不需要太多:参数来自表单; 在视图函数中编写查询,将查询集传递给模板。 模板呈现报告。

为什么你需要比这更多的东西?

您可以使用 通用列表/详细视图< /a> 以避免编写大量代码。 如果您采用此路线,则可以将查询集和模板提供给通用视图,该视图会为您处理一些处理。

由于您必须在 Crystal 报告或 Django 中编写查询,因此您并没有真正从“报告”工具中获得太多优势。

These are just HTML templates with ordinary view functions.

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 views to 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.

一个人的夜不怕黑 2024-08-02 10:12:13

编辑
看起来确实两个包都消失了,但现在我们有了一个很好的数据结构,这是从 R 借来的 - DataFrame
快速教程(注意“分组”部分)


我不知道 Django(或 Python)的完整报告解决方案,但是无论有或没有 ORM,使用 Django 进行报告都非常容易:

  • django-tables 可以为您提供处理表数据的非常基本的结构(asc/desc 服务器端排序等) )
  • 您可以使用标准 django 1.1 查询集聚合(django-reporting 使用它们)来进行总计/小计内容。

就我个人而言,我使用 django-tables 和 nore 的 datashaping python 包进行快速摘要/avg/median/IQR/过滤东西,因为我有许多不同的数据源(REST 数据、两个 mysql 数据库、R 中的 csv 文件),现在 django 数据库中只有很少的数据源。

Pycha 是我绘制简单图表的候选人之一。

我不喜欢客户端基于 ajax 的网格等进行报告,但您也可以将它与 django 模板一起使用。

Edit
It really looks like both packages are gone, but now we have a nice data structure, borrowed from R -- DataFrame in pandas package
Quick tutorial (pay attention to section "Grouping")


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-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.

Personally I use django-tables and neithere's datashaping python 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.

Pycha is one of candidates for me to draw simple charts.

I don't like client-side ajax-based grids etc for reporting, but you can use it with django templates too.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文