import contextlib from dataclasses import dataclass from django.core.exceptions import FieldError from django.core.paginator import Paginator from django.db.models import Model from django.http import HttpRequest from django.http import HttpResponse from django.shortcuts import render from django.urls import reverse from zen_queries import queries_disabled @dataclass class Row: """ A row in a table. """ data: dict[str, str] actions: list[dict[str, str]] @dataclass class RowAction: """ An action that can be performed on a row in a table. """ label: str url_name: str url_kwargs: dict[str, str] def render(self, obj) -> dict[str, str]: """ Render the action as a dictionary for the given object. """ url = reverse( self.url_name, kwargs={key: getattr(obj, value) for key, value in self.url_kwargs.items()}, ) return {"label": self.label, "url": url} def render_list( request: HttpRequest, objects: list["Model"], columns: list[tuple[str, str]], row_actions: list[RowAction] = None, list_actions: list[tuple[str, str]] = None, paginate_by: int = None, ) -> HttpResponse: """ Render a list of objects with a table. """ # TODO: List actions total_count = len(objects) order_by = request.GET.get("order_by") if order_by: with contextlib.suppress(FieldError): objects = objects.order_by(order_by) if paginate_by: paginator = Paginator(object_list=objects, per_page=paginate_by) page = paginator.get_page(request.GET.get("page")) objects = page.object_list rows = [] for obj in objects: with queries_disabled(): row = Row( data={column: getattr(obj, column[0]) for column in columns}, actions=[action.render(obj) for action in row_actions], ) rows.append(row) context = { "rows": rows, "columns": columns, "row_actions": row_actions, "list_actions": list_actions, "total_count": total_count, "order_by": order_by, } if paginate_by: context |= { "page": page, "is_paginated": True, } return render( request=request, template_name="utils/list.html", context=context, )