为啥 sqlalchemy 没有正确设置默认值?
Posted
技术标签:
【中文标题】为啥 sqlalchemy 没有正确设置默认值?【英文标题】:Why is sqlalchemy not setting default value correctly?为什么 sqlalchemy 没有正确设置默认值? 【发布时间】:2022-01-21 02:25:22 【问题描述】:由于某种原因,在此示例中,可选 is_active 属性未设置为默认值。
from pydantic import BaseModel, EmailStr
from datetime import datetime
# Pydantic schemas
# Shared properties
class UserBase(BaseModel):
email: Optional[EmailStr] = None
is_active: Optional[bool] = True
is_superuser: bool = False
username: Optional[str] = None
# Properties to receive via API on creation
class UserCreate(UserBase):
email: EmailStr
password: str
# sqlalchemy model
class User(Base):
id = Column(Integer, primary_key=True, index=True)
username = Column(String(25), index=True, unique=True, nullable=False)
email = Column(String(50), unique=True, index=True, nullable=False)
hashed_password = Column(String(256), nullable=False)
is_active = Column(Boolean(), default=True, nullable=False)
is_superuser = Column(Boolean(), default=False, nullable=False)
__mapper_args__ = "eager_defaults": True
我期望is_active
的默认值是可选输入,是True
。但如果没有明确通过,我会得到None
。
obj_in = UserCreate(email=email, password=password, username=username)
print(obj_in.is_active)
# True
db_obj = User(
email=obj_in.email,
hashed_password=get_password_hash(obj_in.password),
username=obj_in.username,
is_superuser=obj_in.is_superuser,
# is_active=obj_in.is_active,
)
print(db_obj.is_active)
# None
# I have to add the is_active flag explicitly
db_obj = User(
email=obj_in.email,
hashed_password=get_password_hash(obj_in.password),
username=obj_in.username,
is_superuser=obj_in.is_superuser,
is_active=obj_in.is_active,
)
print(db_obj.is_active)
# True
【问题讨论】:
不确定是否是问题所在;但是,我认为您不应该致电Boolean
。请改用,例如:is_active = Column(Boolean, default=True, nullable=False)
即末尾没有括号/括号。
谢谢,但这似乎无关紧要。以String(25)
为例。
刚刚以您编写的方式在 SQLite 上进行了测试,您是对的:似乎无关紧要。它是什么关系型数据库?也许有一些不兼容的东西?
我正在使用 postgres,但应该没关系吧?因为这个问题甚至在插入数据库之前就出现了
嗯,是的,那很可能不是问题,因为它是一个主要的数据库。我刚刚在 SQLAlchemy 文档中读到,如果数据库没有布尔类型,可能会有问题。但是,例如,SQLite 存储一个 1 就可以了。
【参考方案1】:
事后看来,非常明显。 (不是一切)。有两个问题:
在将 db_obj 实际插入数据库之前,我正在检查它的值。无论出于何种原因,我认为 sqlalchemy 模型 db_obj = User(...)
返回的对象也将分配默认值。原来只有在将对象插入数据库后才分配默认值。
影响我的用例的第二个问题是我没有刷新会话并尝试在退出 with session.begin()
块之前访问 obj id,即在提交对 db 的更改之前。
所以,修正这两点,我们得到:
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.ext.asyncio import create_async_engine
from sqlalchemy.orm import sessionmaker
engine = create_async_engine(
SQLALCHEMY_DATABASE_URI,
echo=True,
)
# expire_on_commit=False will prevent attributes from being expired
# after commit.
async_session = sessionmaker(
engine, expire_on_commit=False, class_=AsyncSession
)
obj_in = UserCreate(email=email, password=password, username=username)
db_obj = User(
email=obj_in.email,
hashed_password=get_password_hash(obj_in.password),
username=obj_in.username,
is_superuser=obj_in.is_superuser,
# is_active=obj_in.is_active,
)
# this was my use case for running tests.
# I start session.begin() in a pytest fixture, so I don't have
# control on exiting the with block to allow db commit.
# So in this case, a force `flush` is required.
async with async_session() as session, session.begin():
session.add(db_obj)
# force commit
await session.flush()
# this assertion fails if above flush is removed.
assert db_obj.is_active == obj_in.is_active
【讨论】:
以上是关于为啥 sqlalchemy 没有正确设置默认值?的主要内容,如果未能解决你的问题,请参考以下文章