为什么 sqlalchemy 没有正确设置默认值?
Why is sqlalchemy not setting default value correctly?
出于某种原因,在此示例中,可选的 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
事后看来,非常明显。 (不是一切)。有两个问题:
我在实际插入数据库之前检查了 db_obj 的值。无论出于何种原因,我虽然 sqlalchemy Model 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
出于某种原因,在此示例中,可选的 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
事后看来,非常明显。 (不是一切)。有两个问题:
我在实际插入数据库之前检查了 db_obj 的值。无论出于何种原因,我虽然 sqlalchemy Model
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