diff --git a/docs/docs/report/helpers.md b/docs/docs/report/helpers.md index 125d021016..b235005078 100644 --- a/docs/docs/report/helpers.md +++ b/docs/docs/report/helpers.md @@ -78,6 +78,78 @@ To return an element corresponding to a certain key in a container which support {% endraw %} ``` +## Database Helpers + +A number of helper functions are available for accessing database objects: + +### filter_queryset + +The `filter_queryset` function allows for arbitrary filtering of the provided querysert. It takes a queryset and a list of filter arguments, and returns a filtered queryset. + + + +::: report.templatetags.report.filter_queryset + options: + show_docstring_description: false + show_source: False + +!!! info "Provided QuerySet" + The provided queryset must be a valid Django queryset object, which is already available in the template context. + +!!! warning "Advanced Users" + The `filter_queryset` function is a powerful tool, but it is also easy to misuse. It assumes that the user has a good understanding of Django querysets and the underlying database structure. + +#### Example + +In a report template which has a `PurchaseOrder` object available in its context, fetch any line items which have a received quantity greater than zero: + +```html +{% raw %} +{% load report %} + +{% filter_queryset order.lines.all received__gt=0 as received_lines %} + + + +{% endraw %} +``` + +### filter_db_model + +The `filter_db_model` function allows for filtering of a database model based on a set of filter arguments. It takes a model class and a list of filter arguments, and returns a filtered queryset. + +::: report.templatetags.report.filter_db_model + options: + show_docstring_description: false + show_source: False + +#### Example + +Generate a list of all active customers: + +```html +{% raw %} +{% load report %} + +{% filter_db_model company.company is_customer=True active=True as active_customers %} + + + +{% endraw %} +``` + +### Advanced Database Queries + +More advanced database filtering should be achieved using a [report plugin](../extend/plugins/report.md), and adding custom context data to the report template. + ## Number Formatting ### format_number diff --git a/src/backend/InvenTree/report/templatetags/report.py b/src/backend/InvenTree/report/templatetags/report.py index ef4da60206..7093555014 100644 --- a/src/backend/InvenTree/report/templatetags/report.py +++ b/src/backend/InvenTree/report/templatetags/report.py @@ -8,8 +8,10 @@ from decimal import Decimal from typing import Any, Optional from django import template +from django.apps.registry import apps from django.conf import settings from django.core.exceptions import ValidationError +from django.db.models.query import QuerySet from django.utils.safestring import SafeString, mark_safe from django.utils.translation import gettext_lazy as _ @@ -29,6 +31,50 @@ register = template.Library() logger = logging.getLogger('inventree') +@register.simple_tag() +def filter_queryset(queryset: QuerySet, **kwargs) -> QuerySet: + """Filter a database queryset based on the provided keyword arguments. + + Arguments: + queryset: The queryset to filter + + Keyword Arguments: + field (any): Filter the queryset based on the provided field + + Example: + {% filter_queryset companies is_supplier=True as suppliers %} + """ + return queryset.filter(**kwargs) + + +@register.simple_tag() +def filter_db_model(model_name: str, **kwargs) -> QuerySet: + """Filter a database model based on the provided keyword arguments. + + Arguments: + model_name: The name of the Django model - including app name (e.g. 'part.partcategory') + + Keyword Arguments: + field (any): Filter the queryset based on the provided field + + Example: + {% filter_db_model 'part.partcategory' is_template=True as template_parts %} + """ + app_name, model_name = model_name.split('.') + + try: + model = apps.get_model(app_name, model_name) + except Exception: + return None + + if model is None: + return None + + queryset = model.objects.all() + + return filter_queryset(queryset, **kwargs) + + @register.simple_tag() def getindex(container: list, index: int) -> Any: """Return the value contained at the specified index of the list.