membersystem/src/utils/view_utils.py

101 lines
2.4 KiB
Python

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,
)