This is how it can be achieved.
from typing import Dict
from pydantic import BaseModel
from sqlalchemy import TypeDecorator
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import Session
from sqlalchemy.orm import (
Mapped,
Session,
mapped_column,
)
# Pydantic
class UserMetadataSchema(BaseModel):
device_map: Dict[str, str] = {}
class UserdataMetadataJSONB(TypeDecorator):
impl = JSONB
cache_ok = True
def process_bind_param(self, value, dialect):
if value is not None:
return value.model_dump()
return value
def process_result_value(self, value, dialect):
if value is not None:
return UserMetadataSchema(**value)
return value
class User(Base):
__tablename__ = "user"
id: Mapped[int] = mapped_column(init=False, autoincrement=True, primary_key=True)
user_metadata: Mapped[UserMetadataSchema] = mapped_column(
UserdataMetadataJSONB, nullable=False
)
def main():
session: Session = None # fill with DB session
metadata = UserMetadataSchema(
device_map={"device": "samsung"},
)
# Create User with pydantic mapped column
user = User(
name="BirdWatcher",
user_metadata=UserMetadataSchema(**metadata.model_dump()),
)
session.add(user)
session.flush()
session.commit()
if __name__ == "__main__":
main()