From 7738f1c9c2787c7b7b1e46c7530b47440a243a70 Mon Sep 17 00:00:00 2001 From: Yaro Kasear Date: Fri, 29 Aug 2025 16:04:41 -0500 Subject: [PATCH] Some additional work done with CRUDKit. May start this over with a better design for CRUDKit. --- crudkit/html/templates/crudkit/_macros.html | 14 +------ crudkit/service.py | 21 +++++++---- inventory/models/__init__.py | 15 +------- inventory/models/rooms.py | 37 ++++++++++--------- .../templates/fragments/_table_fragment.html | 33 ++++++++++++++++- 5 files changed, 67 insertions(+), 53 deletions(-) diff --git a/crudkit/html/templates/crudkit/_macros.html b/crudkit/html/templates/crudkit/_macros.html index bac6692..127ba0b 100644 --- a/crudkit/html/templates/crudkit/_macros.html +++ b/crudkit/html/templates/crudkit/_macros.html @@ -56,7 +56,7 @@ {% else %}
  • - {{ label }} @@ -103,18 +103,6 @@ - - {# keep hidden pager-state in sync with one handler instead of inline click spam #} - {%- endmacro %} {% macro form(schema, action, method="POST", obj_id=None, hx=False, csrf_token=None) -%} diff --git a/crudkit/service.py b/crudkit/service.py index a62ae18..b45a0e2 100644 --- a/crudkit/service.py +++ b/crudkit/service.py @@ -56,7 +56,7 @@ def _apply_dotted_ordering(stmt, Model, sort_tokens): rel = current_mapper.relationships.get(rel_name) if rel is None: # invalid sort key; skip quietly or raise - # raise ValueError(f"Unknown relationship {current_mapper.class_.__name__}.{rel_name}") + print(f"Unknown relationship {current_mapper.class_.__name__}.{rel_name}") entity = None break @@ -78,13 +78,20 @@ def _apply_dotted_ordering(stmt, Model, sort_tokens): continue col_name = parts[-1] - # Validate final column - if col_name not in current_mapper.columns: - # raise ValueError(f"Unknown column {current_mapper.class_.__name__}.{col_name}") - continue + # # Validate final column + # if col_name not in current_mapper.columns: + # print(f"Unknown column {current_mapper.class_.__name__}.{col_name}") + # continue - col = getattr(entity, col_name) if entity is not Model else getattr(Model, col_name) - stmt = stmt.order_by(col.desc() if direction == "desc" else col.asc()) + # col = getattr(entity, col_name) if entity is not Model else getattr(Model, col_name) + + attr = getattr(entity, col_name, None) + if attr is None: + attr = getattr(current_mapper.class_, col_name, None) + if attr is None: + print(f"Unknown column {current_mapper.class_.__name__}.{col_name}") + continue + stmt = stmt.order_by(attr.desc() if direction == "desc" else attr.asc()) return stmt diff --git a/inventory/models/__init__.py b/inventory/models/__init__.py index 83d2e78..5d6208e 100644 --- a/inventory/models/__init__.py +++ b/inventory/models/__init__.py @@ -29,22 +29,9 @@ worklog_images = image_links.worklog_images User = users.User # Now it’s safe to configure mappers and set global eagerloads -from sqlalchemy.orm import configure_mappers, joinedload, selectinload +from sqlalchemy.orm import configure_mappers configure_mappers() -User.ui_eagerload = ( - joinedload(User.supervisor), - joinedload(User.location).joinedload(Room.room_function), -) - -Room.ui_eagerload = ( - joinedload(Room.area), - joinedload(Room.room_function), - selectinload(Room.inventory), - selectinload(Room.users) -) - - registry = { "area": Area, "brand": Brand, diff --git a/inventory/models/rooms.py b/inventory/models/rooms.py index 0f8df32..e65c62e 100644 --- a/inventory/models/rooms.py +++ b/inventory/models/rooms.py @@ -1,12 +1,13 @@ from typing import Optional, TYPE_CHECKING, List if TYPE_CHECKING: from .areas import Area - from .room_functions import RoomFunction from .inventory import Inventory from .users import User +from .room_functions import RoomFunction from crudkit import CrudMixin -from sqlalchemy import ForeignKey, Identity, Integer, Unicode +from sqlalchemy import ForeignKey, Identity, Integer, Unicode, func, select, literal +from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy.orm import Mapped, mapped_column, relationship from . import db @@ -27,18 +28,6 @@ class Room(db.Model, CrudMixin): ui_eagerload = tuple() ui_extra_attrs = ('area_id', 'function_id') - @classmethod - def ui_update(cls, session, id_, payload): - print(payload) - obj = session.get(cls, id_) - if not obj: - return None - obj.name = payload.get("name", obj.name) - obj.area_id = payload.get("area_id", obj.area_id) - obj.function_id = payload.get("function_id", obj.function_id) - session.commit() - return obj - def __init__(self, name: Optional[str] = None, area_id: Optional[int] = None, function_id: Optional[int] = None): self.name = name self.area_id = area_id @@ -47,8 +36,22 @@ class Room(db.Model, CrudMixin): def __repr__(self): return f"" - @property + @hybrid_property def identifier(self): name = self.name or "" - func = self.room_function.description if self.room_function else "" - return f"{name} - {func}".strip(" -") + function = self.room_function.description if self.room_function else "" + return f"{name} - {function}".strip(" -") + + @identifier.expression + def identifier(cls): + rf_desc = ( + select(RoomFunction.description) + .where(RoomFunction.id == cls.function_id) + .correlate(cls) + .scalar_subquery() + ) + return func.concat( + func.coalesce(cls.name, ''), # left part + literal(' - '), # separator + func.coalesce(rf_desc, '') # right part via correlated subquery + ) diff --git a/inventory/templates/fragments/_table_fragment.html b/inventory/templates/fragments/_table_fragment.html index ff84d69..b20559b 100644 --- a/inventory/templates/fragments/_table_fragment.html +++ b/inventory/templates/fragments/_table_fragment.html @@ -70,7 +70,9 @@ refresh_url=none, model=none, sort=none) %} {% for h in headers %} - {{ h }} + {{ h }} {% endfor %} @@ -82,7 +84,7 @@ refresh_url=none, model=none, sort=none) %} - {% if sort %}{% endif %} + {% for k,v in (filters or {}).items() %} {% endfor %} @@ -90,4 +92,31 @@ refresh_url=none, model=none, sort=none) %}
    + +{# keep hidden pager-state in sync with one handler instead of inline click spam #} + + {% endmacro %} \ No newline at end of file