Group By Reports

General use case

Group by reports are reports that group the data by a specific field, while doing some kind of calculation on the grouped fields. For example, a report that groups the expenses by the expense type.

Example:

class GroupByReport(ReportView):
    report_model = SalesTransaction
    report_title = _("Group By Report")
    date_field = "date"
    group_by = "product"

    columns = [
        "name",
        ComputationField.create(
            method=Sum,
            field="value",
            name="value__sum",
            verbose_name="Total sold $",
            is_summable=True,
        ),
    ]

    # Charts
    chart_settings = [
        Chart(
            "Total sold $",
            Chart.BAR,
            data_source=["value__sum"],
            title_source=["name"],
        ),
    ]

A Sample group by report would look like this:

Group Report

In the columns you can access to fields on the model that is being grouped by, in this case the Expense model, and the computation fields.

Group by a traversing field

group_by value can be a traversing field. If set, the report will be grouped by the last field in the traversing path,

and, the columns available will be those of the last model in the traversing path.

Example:

# Inherit from previous report and make another version, keeping the columns and charts
class GroupByTraversingFieldReport(GroupByReport):

    report_title = _("Group By Traversing Field")
    group_by = "product__product_category"  # Note the traversing

Group by custom querysets

Grouping can also be over a curated queryset(s).

Example:

class GroupByCustomQueryset(ReportView):
    report_model = SalesTransaction
    report_title = _("Group By Custom Queryset")
    date_field = "date"

    group_by_custom_querysets = [
        SalesTransaction.objects.filter(product__size__in=["big", "extra_big"]),
        SalesTransaction.objects.filter(product__size__in=["small", "extra_small"]),
        SalesTransaction.objects.filter(product__size="medium"),
    ]
    group_by_custom_querysets_column_verbose_name = _("Product Size")

    columns = [
        "__index__",
        ComputationField.create(
            Sum, "value", verbose_name=_("Total Sold $"), name="value"
        ),
    ]

    chart_settings = [
        Chart(
            title="Total sold By Size $",
            type=Chart.PIE,
            data_source=["value"],
            title_source=["__index__"],
        ),
        Chart(
            title="Total sold By Size $",
            type=Chart.BAR,
            data_source=["value"],
            title_source=["__index__"],
        ),
    ]

    def format_row(self, row_obj):
        # Put the verbose names we need instead of the integer index
        index = row_obj["__index__"]
        if index == 0:
            row_obj["__index__"] = "Big"
        elif index == 1:
            row_obj["__index__"] = "Small"
        elif index == 2:
            row_obj["__index__"] = "Medium"
        return row_obj

This report will create two groups, one for pending sales and another for paid and overdue together.

The __index__ column is a “magic” column, it will added automatically to the report if it’s not added. It just hold the index of the row in the group. its verbose name (ie the one on the table header) can be customized via group_by_custom_querysets_column_verbose_name

You can then customize the value of the __index__ column via format_row hook

The No Group By

Sometimes you want to get some calculations done on the whole report_model, without a group_by. You can do that by having the calculation fields you need in the columns, and leave out the group by.

Example:

class NoGroupByReport(ReportView):
    report_model = SalesTransaction
    report_title = _("No-Group-By Report [WIP]")
    date_field = "date"
    group_by = ""

    columns = [
        ComputationField.create(
            method=Sum,
            field="value",
            name="value__sum",
            verbose_name="Total sold $",
            is_summable=True,
        ),
    ]

This report will give one number, the sum of all the values in the value field of the SalesTransaction model, within a period.