From 1c5fa29943c483b9025440625c30a3c5f42a0d01 Mon Sep 17 00:00:00 2001 From: Yaro Kasear Date: Wed, 24 Sep 2025 08:28:02 -0500 Subject: [PATCH] Make the CRUDService more reliable. --- crudkit/core/service.py | 306 +++++++++------------------------------- 1 file changed, 68 insertions(+), 238 deletions(-) diff --git a/crudkit/core/service.py b/crudkit/core/service.py index b84ebc6..b13bb81 100644 --- a/crudkit/core/service.py +++ b/crudkit/core/service.py @@ -1,50 +1,14 @@ -from typing import Any, Callable, Dict, Iterable, List, Tuple, Type, TypeVar, Generic, Optional, Protocol, runtime_checkable, cast +from typing import Any, Callable, Type, TypeVar, Generic, Optional, Protocol, runtime_checkable, cast from sqlalchemy import and_, func, inspect, or_, text from sqlalchemy.engine import Engine, Connection -from sqlalchemy.orm import Load, Session, raiseload, selectinload, with_polymorphic, Mapper, RelationshipProperty, class_mapper, ColumnProperty +from sqlalchemy.orm import Load, Session, selectinload, with_polymorphic, Mapper, RelationshipProperty, ColumnProperty from sqlalchemy.orm.attributes import InstrumentedAttribute -from sqlalchemy.orm.base import NO_VALUE -from sqlalchemy.orm.util import AliasedClass -from sqlalchemy.sql import operators -from sqlalchemy.sql.elements import UnaryExpression from crudkit.core.base import Version from crudkit.core.spec import CRUDSpec from crudkit.core.types import OrderSpec, SeekWindow from crudkit.backend import BackendInfo, make_backend_info - -def _expand_requires(model_cls, fields): - out, seen = [], set() - def add(f): - if f not in seen: - seen.add(f); out.append(f) - - for f in fields: - add(f) - parts = f.split(".") - cur_cls = model_cls - prefix = [] - - for p in parts[:-1]: - rel = getattr(cur_cls.__mapper__.relationships, 'get', lambda _: None)(p) - if not rel: - cur_cls = None - break - cur_cls = rel.mapper.class_ - prefix.append(p) - - if cur_cls is None: - continue - - leaf = parts[-1] - deps = (getattr(cur_cls, "__crudkit_field_requires__", {}) or {}).get(leaf) - if not deps: - continue - - pre = ".".join(prefix) - for dep in deps: - add(f"{pre + '.' if pre else ''}{dep}") - return out +from crudkit.projection import compile_projection def _is_rel(model_cls, name: str) -> bool: try: @@ -53,41 +17,6 @@ def _is_rel(model_cls, name: str) -> bool: except Exception: return False -def _is_instrumented_column(attr) -> bool: - try: - return hasattr(attr, "property") and isinstance(attr.property, ColumnProperty) - except Exception: - return False - -def _loader_options_for_fields(root_alias, model_cls, fields: list[str]) -> list[Load]: - """ - For bare MANYTOONE names in fields (e.g. "location"), selectinload the relationship - and only fetch the related PK. This is enough for preselecting