Skip to content

Commit af537e2

Browse files
committed
updated README.md
1 parent 6cca444 commit af537e2

1 file changed

Lines changed: 85 additions & 3 deletions

File tree

README.md

Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,93 @@ def commit_extra_resultitem(session):
5656
session.commit()
5757
```
5858

59-
## Developer notes
59+
## Developer Notes
6060

61-
### Publish updates
61+
### Computed Fields
62+
63+
Three field types for deriving values on SQLAlchemy models. All are defined inline on the model and delegate their logic to functions in `post_calculations.py`.
64+
65+
#### `computed_field`
66+
Pure Python. No session required. Cached permanently on the instance — survives `expire()` and `commit()`.
67+
68+
Use when the value is derivable from already-loaded columns only (arithmetic, date calculations, string formatting).
69+
70+
#### `session_computed_field`
71+
Session-aware. Cache is cleared on `expire()` and `commit()` so DB-derived values are never stale.
72+
73+
Use when the calculation requires a DB query — joins, lookups against reference tables, aggregates over related rows.
74+
75+
#### `computed_hybrid`
76+
Wraps `hybrid_property`. Not cached — recalculates on every access. The only type usable in queries (`WHERE`, `ORDER BY`, `filter()`).
77+
78+
Use when you need the value available at the query level, not just on loaded instances.
79+
80+
#### Quick Reference
81+
82+
| | `computed_field` | `session_computed_field` | `computed_hybrid` |
83+
|---|---|---|---|
84+
| Requires session | No | Yes | No |
85+
| Cached | Yes — permanent | Yes — cleared on expiry | No |
86+
| Usable in queries | No | No | Yes |
87+
| Use for | Arithmetic, dates | Joins, DB lookups | Query-level filtering |
88+
89+
#### Example
90+
91+
```python
92+
# post_calculations.py
93+
94+
def _calc_age(self) -> Optional[int]:
95+
if self.birthtime is None:
96+
return None
97+
today = date.today()
98+
born = self.birthtime.date()
99+
return today.year - born.year - ((today.month, today.day) < (born.month, born.day))
100+
101+
def _calc_prepost(self, session: Session) -> str:
102+
# looks up sibling results on the same date to determine PRE/POST dialysis
103+
...
104+
105+
def _numeric_value_py(self) -> Optional[float]:
106+
return float(self.resultvalue) if self.resultvalue else None
107+
108+
def _numeric_value_expr(cls):
109+
return cast(cls.resultvalue, Numeric)
110+
```
111+
112+
```python
113+
# ukrdc.py
114+
115+
class ResultItem(Base):
116+
...
117+
118+
# No DB query needed — birthtime is already loaded
119+
age: int = computed_field(_calc_age)
120+
121+
# Queries sibling rows — cache cleared on expire/commit
122+
prepost: str = session_computed_field(_calc_prepost)
123+
124+
# Usable in WHERE/ORDER BY across a patient cohort
125+
numeric_value: float = computed_hybrid(_numeric_value_py, _numeric_value_expr)
126+
```
127+
128+
```python
129+
# usage
130+
131+
record = session.get(ResultItem, "R1")
132+
print(record.age) # 47 — from loaded columns, no query
133+
print(record.prepost) # "PRE" — queried DB, cached until next commit
134+
135+
# computed_hybrid can be used in queries — the other two cannot
136+
high_results = session.scalars(
137+
select(ResultItem)
138+
.where(ResultItem.numeric_value > 10.0)
139+
.order_by(ResultItem.numeric_value.desc())
140+
).all()
141+
```
142+
143+
### Publish Updates
62144

63145
- Iterate the version number (`poetry version major/minor/patch`)
64146
- Push to GitHub repo
65147
- Create a GitHub release
66-
- GitHub Actions will automatically publish the release to PyPI
148+
- GitHub Actions will automatically publish the release to PyPI

0 commit comments

Comments
 (0)