feat: 为多个模型的外键字段添加级联删除和其他约束

This commit is contained in:
2025-12-23 11:00:09 +08:00
parent 1a78c76d02
commit 96bf447426
15 changed files with 206 additions and 47 deletions

View File

@@ -66,7 +66,11 @@ class DownloadAria2InfoBase(SQLModelBase):
class DownloadAria2Info(DownloadAria2InfoBase, SQLModelBase, table=True):
"""Aria2下载信息模型与Download一对一关联"""
download_id: UUID = Field(foreign_key="download.id", primary_key=True)
download_id: UUID = Field(
foreign_key="download.id",
primary_key=True,
ondelete="CASCADE"
)
"""关联的下载任务UUID"""
# 反向关系
@@ -77,7 +81,11 @@ class DownloadAria2Info(DownloadAria2InfoBase, SQLModelBase, table=True):
class DownloadAria2File(SQLModelBase, TableBaseMixin):
"""Aria2下载文件列表与Download一对多关联"""
download_id: UUID = Field(foreign_key="download.id", index=True)
download_id: UUID = Field(
foreign_key="download.id",
index=True,
ondelete="CASCADE"
)
"""关联的下载任务UUID"""
file_index: int = Field(ge=1)
@@ -145,23 +153,39 @@ class Download(DownloadBase, UUIDTableBaseMixin):
"""目标存储路径"""
# 外键
user_id: UUID = Field(foreign_key="user.id", index=True)
user_id: UUID = Field(
foreign_key="user.id",
index=True,
ondelete="CASCADE"
)
"""所属用户UUID"""
task_id: int | None = Field(default=None, foreign_key="task.id", index=True)
task_id: int | None = Field(
default=None,
foreign_key="task.id",
index=True,
ondelete="SET NULL"
)
"""关联的任务ID"""
node_id: int = Field(foreign_key="node.id", index=True)
node_id: int = Field(
foreign_key="node.id",
index=True,
ondelete="RESTRICT"
)
"""执行下载的节点ID"""
# 关系
aria2_info: DownloadAria2Info | None = Relationship(
back_populates="download",
sa_relationship_kwargs={"uselist": False},
sa_relationship_kwargs={"uselist": False, "cascade": "all, delete-orphan"},
)
"""Aria2下载信息"""
aria2_files: list[DownloadAria2File] = Relationship(back_populates="download")
aria2_files: list[DownloadAria2File] = Relationship(
back_populates="download",
sa_relationship_kwargs={"cascade": "all, delete-orphan"}
)
"""Aria2文件列表"""
user: "User" = Relationship(back_populates="downloads")

View File

@@ -79,7 +79,11 @@ from .policy import GroupPolicyLink
class GroupOptions(GroupOptionsBase, TableBaseMixin):
"""用户组选项模型"""
group_id: UUID = Field(foreign_key="group.id", unique=True)
group_id: UUID = Field(
foreign_key="group.id",
unique=True,
ondelete="CASCADE"
)
"""关联的用户组UUID"""
archive_download: bool = False
@@ -125,7 +129,7 @@ class Group(GroupBase, UUIDTableBaseMixin):
# 一对一关系:用户组选项
options: GroupOptions | None = Relationship(
back_populates="group",
sa_relationship_kwargs={"uselist": False}
sa_relationship_kwargs={"uselist": False, "cascade": "all, delete-orphan"}
)
# 多对多关系:用户组可以关联多个存储策略

View File

@@ -48,7 +48,12 @@ class Aria2ConfigurationBase(SQLModelBase):
class Aria2Configuration(Aria2ConfigurationBase, TableBaseMixin):
"""Aria2配置模型与Node一对一关联"""
node_id: int = Field(foreign_key="node.id", unique=True, index=True)
node_id: int = Field(
foreign_key="node.id",
unique=True,
index=True,
ondelete="CASCADE"
)
"""关联的节点ID"""
# 反向关系
@@ -90,7 +95,7 @@ class Node(SQLModelBase, TableBaseMixin):
# 关系
aria2_config: Aria2Configuration | None = Relationship(
back_populates="node",
sa_relationship_kwargs={"uselist": False},
sa_relationship_kwargs={"uselist": False, "cascade": "all, delete-orphan"},
)
"""Aria2配置"""

View File

@@ -168,7 +168,12 @@ class DirectoryResponse(SQLModelBase):
class FileMetadata(FileMetadataBase, UUIDTableBaseMixin):
"""文件元数据模型与Object一对一关联"""
object_id: UUID = Field(foreign_key="object.id", unique=True, index=True)
object_id: UUID = Field(
foreign_key="object.id",
unique=True,
index=True,
ondelete="CASCADE"
)
"""关联的对象UUID"""
# 反向关系
@@ -228,13 +233,26 @@ class Object(ObjectBase, UUIDTableBaseMixin):
# ==================== 外键 ====================
parent_id: UUID | None = Field(default=None, foreign_key="object.id", index=True)
parent_id: UUID | None = Field(
default=None,
foreign_key="object.id",
index=True,
ondelete="CASCADE"
)
"""父目录UUIDNULL 表示这是用户的根目录"""
owner_id: UUID = Field(foreign_key="user.id", index=True)
owner_id: UUID = Field(
foreign_key="user.id",
index=True,
ondelete="CASCADE"
)
"""所有者用户UUID"""
policy_id: UUID = Field(foreign_key="policy.id", index=True)
policy_id: UUID = Field(
foreign_key="policy.id",
index=True,
ondelete="RESTRICT"
)
"""存储策略UUID文件直接使用目录作为子文件的默认策略"""
# ==================== 关系 ====================
@@ -252,20 +270,29 @@ class Object(ObjectBase, UUIDTableBaseMixin):
)
"""父目录"""
children: list["Object"] = Relationship(back_populates="parent")
children: list["Object"] = Relationship(
back_populates="parent",
sa_relationship_kwargs={"cascade": "all, delete-orphan"}
)
"""子对象(文件和子目录)"""
# 仅文件有效的关系
file_metadata: FileMetadata | None = Relationship(
back_populates="object",
sa_relationship_kwargs={"uselist": False},
sa_relationship_kwargs={"uselist": False, "cascade": "all, delete-orphan"},
)
"""文件元数据(仅文件有效)"""
source_links: list["SourceLink"] = Relationship(back_populates="object")
source_links: list["SourceLink"] = Relationship(
back_populates="object",
sa_relationship_kwargs={"cascade": "all, delete-orphan"}
)
"""源链接列表(仅文件有效)"""
shares: list["Share"] = Relationship(back_populates="object")
shares: list["Share"] = Relationship(
back_populates="object",
sa_relationship_kwargs={"cascade": "all, delete-orphan"}
)
"""分享列表"""
# ==================== 业务属性 ====================

View File

@@ -55,7 +55,12 @@ class Order(SQLModelBase, TableBaseMixin):
"""订单状态"""
# 外键
user_id: UUID = Field(foreign_key="user.id", index=True, description="所属用户UUID")
user_id: UUID = Field(
foreign_key="user.id",
index=True,
ondelete="CASCADE"
)
"""所属用户UUID"""
# 关系
user: "User" = Relationship(back_populates="orders")

View File

@@ -15,10 +15,18 @@ if TYPE_CHECKING:
class GroupPolicyLink(SQLModelBase, table=True):
"""用户组与存储策略的多对多关联表"""
group_id: UUID = Field(foreign_key="group.id", primary_key=True)
group_id: UUID = Field(
foreign_key="group.id",
primary_key=True,
ondelete="CASCADE"
)
"""用户组UUID"""
policy_id: UUID = Field(foreign_key="policy.id", primary_key=True)
policy_id: UUID = Field(
foreign_key="policy.id",
primary_key=True,
ondelete="CASCADE"
)
"""存储策略UUID"""
@@ -52,7 +60,11 @@ class PolicyOptionsBase(SQLModelBase):
class PolicyOptions(PolicyOptionsBase, UUIDTableBaseMixin):
"""存储策略选项模型与Policy一对一关联"""
policy_id: UUID = Field(foreign_key="policy.id", unique=True)
policy_id: UUID = Field(
foreign_key="policy.id",
unique=True,
ondelete="CASCADE"
)
"""关联的策略UUID"""
# 反向关系
@@ -105,7 +117,7 @@ class Policy(SQLModelBase, UUIDTableBaseMixin):
# 一对一关系:策略选项
options: PolicyOptions | None = Relationship(
back_populates="policy",
sa_relationship_kwargs={"uselist": False},
sa_relationship_kwargs={"uselist": False, "cascade": "all, delete-orphan"},
)
"""策略的扩展选项"""

View File

@@ -24,7 +24,12 @@ class Report(SQLModelBase, TableBaseMixin):
description: str | None = Field(default=None, max_length=255, description="补充描述")
# 外键
share_id: int = Field(foreign_key="share.id", index=True, description="被举报的分享ID")
share_id: int = Field(
foreign_key="share.id",
index=True,
ondelete="CASCADE"
)
"""被举报的分享ID"""
# 关系
share: "Share" = Relationship(back_populates="reports")

View File

@@ -30,7 +30,11 @@ class Share(SQLModelBase, TableBaseMixin):
password: str | None = Field(default=None, max_length=255)
"""分享密码(加密后)"""
object_id: UUID = Field(foreign_key="object.id", index=True)
object_id: UUID = Field(
foreign_key="object.id",
index=True,
ondelete="CASCADE"
)
"""关联的对象UUID"""
views: int = Field(default=0, sa_column_kwargs={"server_default": "0"})
@@ -55,7 +59,11 @@ class Share(SQLModelBase, TableBaseMixin):
"""兑换此分享所需的积分"""
# 外键
user_id: UUID = Field(foreign_key="user.id", index=True)
user_id: UUID = Field(
foreign_key="user.id",
index=True,
ondelete="CASCADE"
)
"""创建分享的用户UUID"""
# 关系
@@ -65,7 +73,10 @@ class Share(SQLModelBase, TableBaseMixin):
object: "Object" = Relationship(back_populates="shares")
"""关联的对象"""
reports: list["Report"] = Relationship(back_populates="share")
reports: list["Report"] = Relationship(
back_populates="share",
sa_relationship_kwargs={"cascade": "all, delete-orphan"}
)
"""举报列表"""
@property

View File

@@ -25,7 +25,11 @@ class SourceLink(SQLModelBase, TableBaseMixin):
"""通过此链接的下载次数"""
# 外键
object_id: UUID = Field(foreign_key="object.id", index=True)
object_id: UUID = Field(
foreign_key="object.id",
index=True,
ondelete="CASCADE"
)
"""关联的对象UUID必须是文件类型"""
# 关系

View File

@@ -20,7 +20,12 @@ class StoragePack(SQLModelBase, TableBaseMixin):
size: int = Field(description="容量包大小(字节)")
# 外键
user_id: UUID = Field(foreign_key="user.id", index=True, description="所属用户UUID")
user_id: UUID = Field(
foreign_key="user.id",
index=True,
ondelete="CASCADE"
)
"""所属用户UUID"""
# 关系
user: "User" = Relationship(back_populates="storage_packs")

View File

@@ -39,7 +39,12 @@ class Tag(SQLModelBase, TableBaseMixin):
expression: str | None = Field(default=None, description="自动标签的匹配表达式")
# 外键
user_id: UUID = Field(foreign_key="user.id", index=True, description="所属用户UUID")
user_id: UUID = Field(
foreign_key="user.id",
index=True,
ondelete="CASCADE"
)
"""所属用户UUID"""
# 关系
user: "User" = Relationship(back_populates="tags")

View File

@@ -49,7 +49,11 @@ class TaskPropsBase(SQLModelBase):
class TaskProps(TaskPropsBase, TableBaseMixin):
"""任务属性模型与Task一对一关联"""
task_id: int = Field(foreign_key="task.id", primary_key=True)
task_id: int = Field(
foreign_key="task.id",
primary_key=True,
ondelete="CASCADE"
)
"""关联的任务ID"""
# 反向关系
@@ -79,13 +83,17 @@ class Task(SQLModelBase, TableBaseMixin):
"""错误信息"""
# 外键
user_id: UUID = Field(foreign_key="user.id", index=True)
user_id: UUID = Field(
foreign_key="user.id",
index=True,
ondelete="CASCADE"
)
"""所属用户UUID"""
# 关系
props: TaskProps | None = Relationship(
back_populates="task",
sa_relationship_kwargs={"uselist": False},
sa_relationship_kwargs={"uselist": False, "cascade": "all, delete-orphan"},
)
"""任务属性"""

View File

@@ -253,10 +253,18 @@ class User(UserBase, UUIDTableBaseMixin):
"""时区UTC 偏移小时数"""
# 外键
group_id: UUID = Field(foreign_key="group.id", index=True)
group_id: UUID = Field(
foreign_key="group.id",
index=True,
ondelete="RESTRICT"
)
"""所属用户组UUID"""
previous_group_id: UUID | None = Field(default=None, foreign_key="group.id")
previous_group_id: UUID | None = Field(
default=None,
foreign_key="group.id",
ondelete="SET NULL"
)
"""之前的用户组UUID用于过期后恢复"""
@@ -274,16 +282,43 @@ class User(UserBase, UUIDTableBaseMixin):
}
)
downloads: list["Download"] = Relationship(back_populates="user")
objects: list["Object"] = Relationship(back_populates="owner")
downloads: list["Download"] = Relationship(
back_populates="user",
sa_relationship_kwargs={"cascade": "all, delete-orphan"}
)
objects: list["Object"] = Relationship(
back_populates="owner",
sa_relationship_kwargs={"cascade": "all, delete-orphan"}
)
"""用户的所有对象(文件和目录)"""
orders: list["Order"] = Relationship(back_populates="user")
shares: list["Share"] = Relationship(back_populates="user")
storage_packs: list["StoragePack"] = Relationship(back_populates="user")
tags: list["Tag"] = Relationship(back_populates="user")
tasks: list["Task"] = Relationship(back_populates="user")
webdavs: list["WebDAV"] = Relationship(back_populates="user")
authns: list["UserAuthn"] = Relationship(back_populates="user")
orders: list["Order"] = Relationship(
back_populates="user",
sa_relationship_kwargs={"cascade": "all, delete-orphan"}
)
shares: list["Share"] = Relationship(
back_populates="user",
sa_relationship_kwargs={"cascade": "all, delete-orphan"}
)
storage_packs: list["StoragePack"] = Relationship(
back_populates="user",
sa_relationship_kwargs={"cascade": "all, delete-orphan"}
)
tags: list["Tag"] = Relationship(
back_populates="user",
sa_relationship_kwargs={"cascade": "all, delete-orphan"}
)
tasks: list["Task"] = Relationship(
back_populates="user",
sa_relationship_kwargs={"cascade": "all, delete-orphan"}
)
webdavs: list["WebDAV"] = Relationship(
back_populates="user",
sa_relationship_kwargs={"cascade": "all, delete-orphan"}
)
authns: list["UserAuthn"] = Relationship(
back_populates="user",
sa_relationship_kwargs={"cascade": "all, delete-orphan"}
)
def to_public(self) -> "UserPublic":
"""转换为公开 DTO排除敏感字段"""

View File

@@ -50,7 +50,11 @@ class UserAuthn(SQLModelBase, TableBaseMixin):
"""用户自定义的凭证名称,便于识别"""
# 外键
user_id: UUID = Field(foreign_key="user.id", index=True)
user_id: UUID = Field(
foreign_key="user.id",
index=True,
ondelete="CASCADE"
)
"""所属用户UUID"""
# 关系

View File

@@ -22,7 +22,12 @@ class WebDAV(SQLModelBase, TableBaseMixin):
use_proxy: bool = Field(default=False, description="是否使用代理下载")
# 外键
user_id: UUID = Field(foreign_key="user.id", index=True, description="所属用户UUID")
user_id: UUID = Field(
foreign_key="user.id",
index=True,
ondelete="CASCADE"
)
"""所属用户UUID"""
# 关系
user: "User" = Relationship(back_populates="webdavs")