More fixes and additions for forms. We are also haunted by detached sessions constantly.

This commit is contained in:
Yaro Kasear 2025-09-23 13:49:59 -05:00
parent 979a329d6a
commit a3f2c794f5
6 changed files with 160 additions and 49 deletions

View file

@ -54,15 +54,30 @@ def _get_loaded_attr(obj: Any, name: str) -> Any:
"""
try:
st = inspect(obj)
# 1) Mapped attribute?
attr = st.attrs.get(name)
if attr is not None:
val = attr.loaded_value
return None if val is NO_VALUE else val
# 2) Already present value (e.g., eager-loaded or set on the dict)?
if name in st.dict:
return st.dict.get(name)
return getattr(obj, name, None)
# 3) If object is detached or attr is not mapped, DO NOT eval hybrids
# or descriptors that could lazy-load. That would explode.
if st.session is None:
return None
# 4) As a last resort on attached instances only, try simple getattr,
# but guard against DetachedInstanceError anyway.
try:
return getattr(obj, name, None)
except Exception:
return None
except Exception:
return getattr(obj, name, None)
# If we can't even inspect it, be conservative
try:
return getattr(obj, name, None)
except Exception:
return None
def _normalize_rows_layout(layout: Optional[List[dict]]) -> Dict[str, dict]:
"""
@ -293,7 +308,21 @@ def _value_label_for_field(field: dict, mapper, values_map: dict, instance, sess
rel_obj = q.get(rid)
if rel_obj is not None:
return _label_from_obj(rel_obj, label_spec)
try:
s = _label_from_obj(rel_obj, label_spec)
except Exception:
s = None
# If we couldn't safely render and we have a session+id, do one lean retry.
if (s is None or s == "") and session is not None and rid is not None:
mdl = rel_prop.mapper.class_
try:
rel_obj2 = session.get(mdl, rid) # attached instance
s2 = _label_from_obj(rel_obj2, label_spec)
if s2:
return s2
except Exception:
pass
return s
return str(rid) if rid is not None else None
class _SafeObj:
@ -406,7 +435,7 @@ def _fk_options(session, related_model, label_spec):
if simple_cols:
first = simple_cols[0]
if hasattr(related_model, first):
q = q.order_by(getattr(related_model, first))
q = q.order_by(None).order_by(getattr(related_model, first))
rows = q.all()
return [
@ -559,7 +588,10 @@ def _label_from_obj(obj: Any, spec: Any) -> str:
for f in fields:
root = f.split(".", 1)[0]
if root not in data:
data[root] = _SafeObj(_get_loaded_attr(obj, root))
try:
data[root] = _SafeObj(_get_loaded_attr(obj, root))
except Exception:
data[root] = _SafeObj(None)
try:
return spec.format(**data)
except Exception:
@ -1007,11 +1039,6 @@ def render_form(
base.update(f.get("template_ctx") or {})
f["template_ctx"] = base
for f in fields:
vl = _value_label_for_field(f, mapper, values_map, instance, session)
if vl is not None:
f["value_label"] = vl
for f in fields:
# existing FK label resolution
vl = _value_label_for_field(f, mapper, values_map, instance, session)