【问题标题】:Django Reporting OptionsDjango 报告选项
【发布时间】:2010-11-08 11:49:32
【问题描述】:

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

【问题讨论】:

标签: python django reporting


【解决方案1】:

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

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

【讨论】:

  • 你应该说这是 alpha,有两个打开的 bug,项目活动比你想象的要少。
  • 现在是 2014 年 5 月,该代码库自 2009 年 12 月以来没有更新
【解决方案2】:

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

【讨论】:

  • 我希望有一天你可以让它与 Django 1.10 和最新版本的 Django Rest Framework 一起工作。它看起来像一个非常有用的工具!
  • 如果最新的 DRF 造成问题,请打开错误报告。 Django 1.10 是一个已知问题,目前部分支持。
  • @Bufke 我已经准备好过滤器的查询(使用 django_filters)。我只需要添加一个下载按钮。是否可以只将下载功能添加到我的过滤器集?
【解决方案3】:

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

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

  1. 创建代理模型以在管理索引页面中显示报告:
    class SalesReport(SalesOrder):
        class Meta:
            proxy = True
    
  2. 为模型创建模型管理类:
    @admin.register(SalesReport)
    class SalesReportAdmin(admin.ModelAdmin):
        ...
    
  3. 实现报告视图:
    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. changelist_view()返回报告视图,将其包装到admin_site.admin_view()中以防止未经授权的访问
    def changelist_view(self, request):
        return self.admin_site.admin_view(self.sales_report)(request)
    
  5. 删除添加、更改、删​​除权限,以便仅保留查看权限并保护更改和历史视图:

    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. 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
    
  7. 将以下模板添加到 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>&nbsp;</td><td>&nbsp;</td>
            <td>{{ totals.total_orders }}</td>
            <td>{{ totals.total_products }}</td>
            <td>{{ totals.total_amount|floatformat:2 }}</td>
          </tr>
        </tbody>
      </table>
    </div></div>
    {% endblock %}
    

现在该报告将在管理索引页面中列出,带有仅供查看的图标 ?,它可以防止未经授权的访问,并且与管理站点的其余部分具有一致的外观。

【讨论】:

  • 这太棒了!我希望人们注意到它,因为它被埋在一个 9 岁的线程中。 SO中还有其他线程,这个答案也可以照亮其他人。就像那些与自定义聚合和/或更改管理模板 change_list.html 相关的内容
  • 谢谢你:)!我也可以协助处理其他线程,但我需要您的帮助才能找到它们。
  • 哦,孩子。我读了很多。但我不记得如何找到它们。这是我目前唯一想到的>stackoverflow.com/search?q=%22change_list_template%22
  • @mrts 是的,这个解决方案真的很棒,而且感觉很合适。我有很多时间在谷歌上搜索以这种方式做的资源。扩展此方法的任何好的资源,也许与报告的“索引”?非常感谢一百万。
  • 此解决方案也适用于 django 3.2 为 9/13/21
【解决方案4】:

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

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

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

您可以使用generic list/detail views 来避免编写尽可能多的代码。如果你走这条路,你将查询集和模板提供给一个为你处理一些处理的通用视图。

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

【讨论】:

  • 有了一个好的报告框架,您可以获得多个显示视图和排序,并且无需创建模板。
  • @Daniel:有一个“好”的报告框架......很好的情绪。有什么例子或建议吗?您可以非常快速地为简单的柱状报告编写通用模板。不知道有什么能让报告工具比简单地在 Django 中做的更好。
  • @S.Lott,当您想要更多而不是简单的平面数据表时,报告框架非常有用。例如,newforms 很不错,但这并不会使管理界面变得多余。
【解决方案5】:

编辑 看起来两个包都不见了,但现在我们有了一个很好的数据结构,从 R 借来的 -- DataFrame 在 pandas 包中 Quick tutorial(注意“分组”部分)


我不了解 Django(或 Python)的完整报告解决方案,但无论是否使用 ORM,使用 Django 进行报告都很容易:

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

我个人使用 django-tables 和 nonee 的 datashaping python 包来快速汇总/avg/median/IQR/过滤的东西,因为我有许多不同的数据源(REST 数据、两个 mysql dbs、来自 R 的 csv 文件)只有现在在 django db 中很少。

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

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

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-11-08
  • 1970-01-01
  • 2013-05-05
  • 2010-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多