Additional fixes and expansions on field dependencies. Still a WIP.
This commit is contained in:
parent
023acfaafe
commit
515eb27fe0
7 changed files with 419 additions and 5 deletions
|
|
@ -1,8 +1,21 @@
|
|||
from sqlalchemy import Column, Integer, DateTime, Boolean, String, JSON, func
|
||||
from sqlalchemy.orm import declarative_mixin, declarative_base
|
||||
from sqlalchemy import Column, Integer, DateTime, Boolean, String, JSON, func, inspect
|
||||
from sqlalchemy.orm import declarative_mixin, declarative_base, NO_VALUE
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
def _safe_get_loaded_attr(obj, name):
|
||||
try:
|
||||
st = inspect(obj)
|
||||
attr = st.attrs.get(name)
|
||||
if attr is not None:
|
||||
val = attr.loaded_value
|
||||
return None if val is NO_VALUE else val
|
||||
if name in st.dict:
|
||||
return st.dict.get(name)
|
||||
return None
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
@declarative_mixin
|
||||
class CRUDMixin:
|
||||
id = Column(Integer, primary_key=True)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ 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.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
|
||||
|
|
@ -12,6 +13,39 @@ 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
|
||||
|
||||
def _is_rel(model_cls, name: str) -> bool:
|
||||
try:
|
||||
prop = model_cls.__mapper__.relationships.get(name)
|
||||
|
|
@ -232,7 +266,10 @@ class CRUDService(Generic[T]):
|
|||
- forward/backward seek via `key` and `backward`
|
||||
Returns a SeekWindow with items, first/last keys, order spec, limit, and optional total.
|
||||
"""
|
||||
session = self.session
|
||||
fields = list(params.get("fields", []))
|
||||
if fields:
|
||||
fields = _expand_requires(self.model, fields)
|
||||
params = {**params, "fields": fields}
|
||||
query, root_alias = self.get_query()
|
||||
|
||||
spec = CRUDSpec(self.model, params or {}, root_alias)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue