Now we can play ā¦ we want to use hash_funcs
to map a python data type like this orm.query.Query
to a particular value that is easily compared and stored.
For a SQL ORM query, my first idea is to compile and generate the SQL string statement, which looks possible, so when I get the same query multiple times, the generated query statement should be in the cache with the result and I should just retrieve its result. so the following works for me :
def serialize_orm_query(q: Query):
"""Build SQL statement with inlined parameters
https://stackoverflow.com/questions/5631078/sqlalchemy-print-the-actual-query
"""
return q.statement.compile(compile_kwargs={"literal_binds": True}).string
letās use that function to compare ORM queries in input of function and in cache :
import sqlite3
import pandas as pd
import sqlalchemy
import streamlit as st
from sqlalchemy import Column
from sqlalchemy import create_engine
from sqlalchemy import Integer
from sqlalchemy import String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm.query import Query
from sqlalchemy.sql.compiler import StrSQLCompiler
# ...
def serialize_orm_query(q: Query):
"""Build SQL statement with inlined parameters
https://stackoverflow.com/questions/5631078/sqlalchemy-print-the-actual-query
"""
return q.statement.compile(compile_kwargs={"literal_binds": True}).string
@st.cache(
hash_funcs={
Session: id,
Query: serialize_orm_query, # if we get a query, stringify it with inbound params
StrSQLCompiler: lambda _: None, # we don't really care about the compiler
},
suppress_st_warning=True,
)
def run_orm_query(q: Query, session: Session):
st.warning("CACHE MISS")
return pd.read_sql(q.statement, session.bind)
session: Session = get_session("test.db")
if st.button("Create test table"):
create_test_db(session)
if st.checkbox("Show sqlite test table using raw sqlite3"):
check_sqlite_table("test.db")
if st.checkbox("Show sqlite test table using ORM query"):
param = st.text_input("Input name of company to look for :")
q: Query = session.query(Company.age).filter(Company.name == param)
st.write(serialize_orm_query(q))
df = run_orm_query(q, session)
st.dataframe(df)
Nowā¦I donāt use a lot of ORM, so there may be other ways to serialize the Query statement, and maybe it doesnāt work for every bit of ORM function or custom functions, so Iāll let you come up with more examples that break this (lie the select()
instead of query()
that I did not try but should be similar).
Also this is generic so maybe you need to inject your own dialect instead, this is pretty well explained in the SO answer.