删除父表记录时同步删除关联VStatus记录的SQLModel配置方法
解决SQLModel中删除父表记录时同步删除关联VStatus记录的问题
问题背景
使用pydantic、sqlmodel、fastapi和python开发,存在Social、Email等业务表,每创建一条业务表记录会同步生成一条VStatus记录用于追踪验证状态。需求是删除业务表(如Social)记录时,自动删除对应的VStatus记录,但当前配置未实现该效果,需调整Relationship的sa_relationship_kwargs配置。
当前配置代码:
Social表
class Social(BaseUUIDModel, SocialBase, table=True): vstatus_id: Optional[UUID] = Field( default=None, foreign_key='VStatus.id', nullable=False) project: Optional["Project"] = Relationship(back_populates="socials") vstatus: "VStatus" = Relationship( sa_relationship_kwargs={ "lazy": "selectin", "primaryjoin": "Social.vstatus_id==VStatus.id", 'cascade': 'all, delete', 'single_parent': True})
VStatus表
class VStatusBase(SQLModel): veri_status: VerificationStatusEnum = Field( default=VerificationStatusEnum.Unverified, nullable=False) vdesc: str = Field(default='', sa_column=Column("vdesc", String)) is_verified: Optional[bool] = Field(default=False) class VStatus(BaseUUIDModel, VStatusBase, table=True): social: "Social" = Relationship( sa_relationship_kwargs={ "lazy": "selectin", "primaryjoin": "VStatus.id==Social.vstatus_id", 'cascade': 'all, delete, delete-orphan', 'single_parent': True}) email: "Email" = Relationship( sa_relationship_kwargs={ "lazy": "selectin", "primaryjoin": "VStatus.id==Email.vstatus_id", 'cascade': 'all, delete, delete-orphan', 'single_parent': True}) coin: "Coin" = Relationship( sa_relationship_kwargs={ "lazy": "selectin", "primaryjoin": "VStatus.id==Coin.vstatus_id", 'cascade': 'all, delete, delete-orphan', 'single_parent': True})
最小可复现代码:
from sqlmodel import SQLModel class VStatusBase(SQLModel): veri_status: str = Field( default="Unverified", nullable=False) vdesc: str = Field(default='', sa_column=Column("vdesc", String)) is_verified: Optional[bool] = Field(default=False) class VStatus(VStatusBase, table=True): id: Optional[int] = Field( primary_key=True, index=True, nullable=False, ) social: "Social" = Relationship( sa_relationship_kwargs={ "lazy": "selectin", "primaryjoin": "VStatus.id==Social.vstatus_id", 'cascade': 'all, delete, delete-orphan', 'single_parent': True}) class SocialBase(SQLModel): name: str = Field(nullable=False) url: AnyHttpUrl = Field( nullable=False, sa_column_kwargs={'unique': True}) class Social(SocialBase, table=True): id: Optional[int] = Field( primary_key=True, index=True, nullable=False, ) vstatus_id: Optional[UUID] = Field( default=None, foreign_key='VStatus.id', nullable=False) vstatus: "VStatus" = Relationship( sa_relationship_kwargs={ "lazy": "selectin", "primaryjoin": "Social.vstatus_id==VStatus.id", 'cascade': 'all, delete, delete-orphan', 'single_parent': True})
问题原因
当前表结构的外键方向是Social.vstatus_id指向VStatus.id,即VStatus是父表,Social是子表。SQLAlchemy的cascade规则默认是父表操作级联到子表,因此删除子表(Social)记录不会触发父表(VStatus)的删除。同时VStatus与多个业务表关联,delete-orphan规则无法生效(因为VStatus可能被其他业务表关联)。
解决方案
方案一:调整外键方向(推荐)
由于VStatus是依附于业务表的附属记录,应让VStatus依赖业务表,将外键移至VStatus表,明确业务表为关系的拥有者。
修改后代码:
Social表
class Social(SocialBase, table=True): id: Optional[int] = Field( primary_key=True, index=True, nullable=False, ) # 移除原vstatus_id字段 vstatus: "VStatus" = Relationship( back_populates="social", sa_relationship_kwargs={ "lazy": "selectin", "cascade": "all, delete-orphan", "uselist": False # 明确一对一关系 })
VStatus表
class VStatus(VStatusBase, table=True): id: Optional[int] = Field( primary_key=True, index=True, nullable=False, ) # 添加关联业务表的外键,以Social为例 social_id: Optional[int] = Field(default=None, foreign_key='social.id', nullable=False) social: "Social" = Relationship( back_populates="vstatus", sa_relationship_kwargs={ "lazy": "selectin", }) # 同理修改email、coin的关联配置,添加对应外键字段 email_id: Optional[int] = Field(default=None, foreign_key='email.id', nullable=False) email: "Email" = Relationship( back_populates="vstatus", sa_relationship_kwargs={ "lazy": "selectin", })
此配置下,删除Social记录时,ORM会自动级联删除对应的VStatus记录,因为Social是关系的拥有者,cascade="all, delete-orphan"会处理一对一关系的附属对象删除。
方案二:不修改表结构,手动级联删除
若无法调整表结构,可在删除业务表记录时,手动先删除对应的VStatus:
def delete_social(db: Session, social_id: int): social = db.get(Social, social_id) if social: # 先删除关联的VStatus db.delete(social.vstatus) # 再删除Social记录 db.delete(social) db.commit() return social
内容的提问来源于stack exchange,提问作者Jim
相关产品推荐
相关产品推荐

