pydantic-sqlalchemy
pydantic-sqlalchemy copied to clipboard
Support for complex custom column types
Currently the conversion inspects the impl
attribute of the column's type to derive the field type for Pydantic, which may be a different Python type from what the SQLAlchemy model actually uses (via process_bind_param
, process_result_value
, etc).
For example, I have this custom, BLOB-backed UUID type, which I use with SQLite:
import uuid
from sqlalchemy.types import BLOB, TypeDecorator
class UUID(TypeDecorator):
impl = BLOB
def load_dialect_impl(self, dialect):
return dialect.type_descriptor(BLOB(16))
def process_bind_param(self, value, dialect):
if value is None:
return value
if not isinstance(value, uuid.UUID):
value = uuid.UUID(value)
return value.bytes
def process_result_value(self, value, dialect):
if value is None:
return value
if isinstance(value, bytes):
return uuid.UUID(bytes=value)
if isinstance(value, uuid.UUID):
return value
raise TypeError(type(value))
This correctly gets me an uuid.UUID
instance in and out of the DB, but the corresponding Pydantic model uses a bytes
type.
It seems SQLAlchemy doesn't have a mechanism to directly specify the mapped Python type, otherwise we would be able to write something like this:
...
class UUID(TypeDecorator):
impl = BLOB
python_type = uuid.UUID
...
And then adapt the logic in sqlalchemy_to_pydantic
:
if hasattr(column.type, "python_type"):
python_type = column.type.python_type
elif hasattr(column.type, "impl"):
if hasattr(column.type.impl, "python_type"):
python_type = column.type.impl.python_type
elif hasattr(column.type, "python_type"):
python_type = column.type.python_type
It would also allow monkey-patching any existing or third-party column types (also see #6).
(Or perhaps the more correct approach would be to create a custom impl
? I'm not sure, seems that's why we have process_bind_param
, process_result_value
, copy
, etc)
I have the same problem with uuid thats implement LargeBinary, I'm getting the following error:
response -> 0 -> id
byte type expected (type=type_error.bytes)
And I have my custom implementation :
class UUID(TypeDecorator):
"""
UUID Binary type decorator
"""
impl = LargeBinary