import os
from pydynamodb import sqlalchemy_dynamodb
from sqlalchemy.engine import create_engine
from sqlalchemy.sql.schema import Column
from sqlalchemy.types import JSON
from sqlmodel import Session, Field, SQLModel, select
import boto3
class LLMTable(SQLModel, table=True):
__tablename__ = "llm_table"
name: str = Field(primary_key=True, unique=True)
spec: dict = Field(default={}, sa_column=Column(JSON))
is_default: bool = Field(default=False)
session = boto3.Session()
client = session.client("dynamodb")
credentials = session.get_credentials()
aws_access_key_id = credentials.access_key
aws_secret_access_key = credentials.secret_key
region_name = session.region_name
conn_str = (
"dynamodb://{aws_access_key_id}:{aws_secret_access_key}@dynamodb.{region_name}.amazonaws.com:443"
).format(
aws_access_key_id=aws_access_key_id,
aws_secret_access_key=aws_secret_access_key,
region_name=region_name,
)
engine = create_engine(conn_str, echo=True)
with Session(engine) as session:
statement = select(LLMTable)
results = session.exec(statement)
llm = results.first()
print(llm)
2025-11-09 14:29:19,663 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2025-11-09 14:29:19,664 INFO sqlalchemy.engine.Engine SELECT name, spec, is_default
FROM llm_table
2025-11-09 14:29:19,664 INFO sqlalchemy.engine.Engine [generated in 0.00010s] ()
2025-11-09 14:29:20,060 INFO sqlalchemy.engine.Engine ROLLBACK
Traceback (most recent call last):
File "/tmp/test/main.py", line 39, in <module>
results = session.exec(statement)
^^^^^^^^^^^^^^^^^^^^^^^
File "/home/user/miniconda3/envs/test/lib/python3.11/site-packages/sqlmodel/orm/session.py", line 83, in exec
results = super().execute(
^^^^^^^^^^^^^^^^
File "/home/user/miniconda3/envs/test/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 2351, in execute
return self._execute_internal(
^^^^^^^^^^^^^^^^^^^^^^^
File "/home/user/miniconda3/envs/test/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 2249, in _execute_internal
result: Result[Any] = compile_state_cls.orm_execute_statement(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/user/miniconda3/envs/test/lib/python3.11/site-packages/sqlalchemy/orm/context.py", line 306, in orm_execute_statement
result = conn.execute(
^^^^^^^^^^^^^
File "/home/user/miniconda3/envs/test/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1419, in execute
return meth(
^^^^^
File "/home/user/miniconda3/envs/test/lib/python3.11/site-packages/sqlalchemy/sql/elements.py", line 526, in _execute_on_connection
return connection._execute_clauseelement(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/user/miniconda3/envs/test/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1641, in _execute_clauseelement
ret = self._execute_context(
^^^^^^^^^^^^^^^^^^^^^^
File "/home/user/miniconda3/envs/test/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1846, in _execute_context
return self._exec_single_context(
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/user/miniconda3/envs/test/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1986, in _exec_single_context
self._handle_dbapi_exception(
File "/home/user/miniconda3/envs/test/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 2358, in _handle_dbapi_exception
raise exc_info[1].with_traceback(exc_info[2])
File "/home/user/miniconda3/envs/test/lib/python3.11/site-packages/sqlalchemy/engine/base.py", line 1983, in _exec_single_context
result = context._setup_result_proxy()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/user/miniconda3/envs/test/lib/python3.11/site-packages/sqlalchemy/engine/default.py", line 1870, in _setup_result_proxy
result = _cursor.CursorResult(self, strategy, cursor_description)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/user/miniconda3/envs/test/lib/python3.11/site-packages/sqlalchemy/engine/cursor.py", line 1447, in __init__
metadata = self._init_metadata(context, cursor_description)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/user/miniconda3/envs/test/lib/python3.11/site-packages/sqlalchemy/engine/cursor.py", line 1497, in _init_metadata
metadata = CursorResultMetaData(self, cursor_description)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/user/miniconda3/envs/test/lib/python3.11/site-packages/sqlalchemy/engine/cursor.py", line 376, in __init__
raw = self._merge_cursor_description(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/user/miniconda3/envs/test/lib/python3.11/site-packages/sqlalchemy/engine/cursor.py", line 626, in _merge_cursor_description
return [
^
File "/home/user/miniconda3/envs/test/lib/python3.11/site-packages/sqlalchemy/engine/cursor.py", line 633, in <listcomp>
context.get_result_processor(
File "/home/user/miniconda3/envs/test/lib/python3.11/site-packages/sqlalchemy/engine/default.py", line 1804, in get_result_processor
return type_._cached_result_processor(self.dialect, coltype)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/user/miniconda3/envs/test/lib/python3.11/site-packages/sqlalchemy/sql/type_api.py", line 950, in _cached_result_processor
rp = d["impl"].result_processor(dialect, coltype)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/user/miniconda3/envs/test/lib/python3.11/site-packages/sqlalchemy/sql/sqltypes.py", line 2813, in result_processor
json_deserializer = dialect._json_deserializer or json.loads
^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'DynamoDBDialect' object has no attribute '_json_deserializer'
The dialect should support mapping JSON columns to DynamoDB Map types.
Environment
Description
Using a
Column(JSON)in a SQLModel or SQLAlchemy table raises an exception because theDynamoDBDialectdoes not implement a_json_deserializerattribute.This prevents reading or writing models that include JSON fields.
Minimal Reproducible Example
Observed Behavior
DynamoDB returns an error:
Expected Behavior
The dialect should support mapping JSON columns to DynamoDB Map types.