"Can't generate DDL for NullType()" when using pydantic.SecretStr #1514
-
First Check
Commit to Help
Example Codefrom pydantic import SecretStr
from sqlmodel import Field, Session, SQLModel, create_engine
class A(SQLModel, table=True):
id: int = Field(primary_key=True)
secret: SecretStr
engine = create_engine("sqlite://")
SQLModel.metadata.create_all(engine)
# sqlalchemy.exc.CompileError: (in table 'a', column 'secret'): Can't generate DDL for NullType(); did you forget to specify a type on this Column? Description
Operating SystemmacOS Operating System DetailsNo response SQLModel Version0.0.4 Python Version3.9.6 Additional ContextWe can specify the column type manually, but it fails with another error: from pydantic import SecretStr
from sqlmodel import Column, Field, Session, String, SQLModel, create_engine
class A(SQLModel, table=True):
id: int = Field(primary_key=True)
secret: SecretStr = Field(sa_column=Column(String))
engine = create_engine("sqlite://")
SQLModel.metadata.create_all(engine)
with Session(engine) as session:
a = A(id=1, secret="secret")
session.add(a)
session.commit()
# sqlalchemy.exc.InterfaceError: (sqlite3.InterfaceError) Error binding parameter 0 - probably unsupported type.
# [SQL: INSERT INTO a (secret, id) VALUES (?, ?)]
# [parameters: (SecretStr('**********'), 1)] |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
If I am not mistaken column secret should be declared as: |
Beta Was this translation helpful? Give feedback.
-
I recently came across a similar task and came across this question. I understand that it has been a long time, but it may be useful to someone. You can use It will look something like this import sqlalchemy as sa
class SecretStrType(sa.types.TypeDecorator):
impl = sa.types.TEXT
def process_bind_param(self, value: SecretStr, dialect):
return value.get_secret_value()
def process_result_value(self, value: str, dialect):
return SecretStr(value) and full example from typing import Optional
import sqlalchemy as sa
from pydantic import SecretStr
from sqlmodel import Field, Session, SQLModel, create_engine, select
class SecretStrType(sa.types.TypeDecorator):
impl = sa.types.TEXT
def process_bind_param(self, value: SecretStr, dialect):
return value.get_secret_value()
def process_result_value(self, value: str, dialect):
return SecretStr(value)
class A(SQLModel, table=True):
id: Optional[int] = Field( # type: ignore[call-overload]
title='Идентификатор',
default=None,
primary_key=True,
description='Идентификатор',
)
access_token: SecretStr = Field( # type: ignore[call-overload]
title='Логин',
min_length=5,
max_length=255,
sa_type=SecretStrType(),
nullable=False,
description='Логин',
)
engine = create_engine(url='postgresql://postgres:postgres@localhost:5432/postgres')
SQLModel.metadata.create_all(engine)
with Session(engine) as session:
a = A.model_validate({"id": 1, "access_token": "secret"})
session.add(a)
session.commit()
with Session(engine) as session:
res = session.exec(select(A))
for i in res:
print(f'{i = }') |
Beta Was this translation helpful? Give feedback.
I recently came across a similar task and came across this question.
I understand that it has been a long time, but it may be useful to someone.
You can use
TypeDecorator
fromsqlalchemy
(https://docs.sqlalchemy.org/en/20/core/custom_types.html#sqlalchemy.types.TypeDecorator)It will look something like this
and full example