User 模型知识点
完整代码
from datetime import datetime
from sqlalchemy import String, DateTime, func, Boolean, Index
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.database import Base
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True, comment="主键ID")
username: Mapped[str] = mapped_column(
String(50),
unique=True,
index=True,
nullable=False,
comment="用户名"
)
__table_args__ = (
Index('idx_username_lower', func.lower(username)),
)
hashed_password: Mapped[str] = mapped_column(
String(255),
nullable=False,
comment="加密后的密码"
)
nickname: Mapped[str] = mapped_column(
String(50),
nullable=False,
default="新用户",
server_default="新用户",
comment="用户昵称"
)
avatar: Mapped[str | None] = mapped_column(
String(255),
nullable=True,
comment="头像URL"
)
bio: Mapped[str | None] = mapped_column(
String(200),
nullable=True,
comment="个人简介"
)
is_active: Mapped[bool] = mapped_column(
Boolean,
default=True,
server_default="1",
comment="账户状态: True=正常, False=禁用"
)
created_at: Mapped[datetime] = mapped_column(
DateTime,
server_default=func.now(),
comment="创建时间"
)
updated_at: Mapped[datetime] = mapped_column(
DateTime,
server_default=func.now(),
onupdate=func.now(),
comment="最后更新时间"
)
# 关联关系
videos: Mapped[list["Video"]] = relationship(
back_populates="author",
cascade="all, delete-orphan",
lazy="select"
)
def __repr__(self) -> str:
return f"<User(id={self.id}, username={self.username}, nickname={self.nickname})>"
def __str__(self) -> str:
return f"User(id={self.id}, username={self.username}, nickname={self.nickname})"
def __eq__(self, other: object) -> bool:
if not isinstance(other, User):
return False
return self.id == other.id
def __hash__(self) -> int:
return hash(self.id)
@property
def is_normal(self) -> bool:
"""账户是否正常状态"""
return self.is_active
def can_login(self) -> bool:
"""用户是否可以登录"""
return self.is_active
def to_dict(self) -> dict:
"""转换为字典格式"""
return {
"id": self.id,
"username": self.username,
"nickname": self.nickname,
"avatar": self.avatar,
"bio": self.bio,
"is_active": self.is_active,
"created_at": self.created_at.isoformat() if self.created_at else None,
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
}
SQLAlchemy 2.0+ 类型注解
from sqlalchemy.orm import Mapped, mapped_column
id: Mapped[int] = mapped_column(primary_key=True)
使用 Mapped 包装类型,mapped_column 定义字段属性
table_args 和 Index
__table_args__ = (
Index('idx_username_lower', func.lower(username)),
)
表级别的额外配置,用于创建数据库索引。func.lower(username) 生成大小写不敏感的索引表达式
@property 装饰器
把方法伪装成属性,访问时不需要 ()。让代码更简洁自然:
@property
def is_normal(self) -> bool:
return self.is_active
魔术方法
__str__():标准字符串表示,便于调试和日志__eq__(other):比较用户对象,通常用 ID 比较__hash__():支持对象作为字典键__repr__():调试用的对象表示,通常用<User(...)>格式
Optional 类型
from typing import Optional
avatar: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
Python 3.10+ 的 str | None 的旧写法,更明确表达可能为空
关系定义的 lazy 参数
videos: Mapped[list["Video"]] = relationship(
back_populates="author",
lazy="select" # 只查询时才加载
)
控制关联查询时的加载策略,默认是 select(按需加载)
lazy 参数的四种模式
| 模式 | 行为 | 适用场景 |
|---|---|---|
select |
查询时才加载(默认) | 避免 N+1 问题,推荐 |
joined |
使用 JOIN 一次性加载 | 减少查询次数 |
subquery |
初始化时一次性加载 | 复杂查询 |
dynamic |
返回 Query 对象,需手动 filter | 大量数据 |
relationship 完整参数解释
```python
videos: Mapped[list["Video"]] = relationship(
back_populates="author", # 双向关系
cascade="all, delete-orphan", # 级联删除
lazy="select" # 懒加载策略
)
back_populates="author" - 双向关系
# User 模型
videos: Mapped[list["Video"]] = relationship(back_populates="author")
# Video 模型
author: Mapped["User"] = relationship(back_populates="videos")
两个模型互相指向对方,确保双向导航:
- 查询 User 时能拿到 videos
- 查询 Video 时能拿到 author
cascade="all, delete-orphan" - 级联删除
user = db.get(User, 1)
db.delete(user) # 删除用户
# 自动删除该用户的所有视频
user.videos # ['video1', 'video2']
| 选项 | 行为 |
|---|---|
all |
所有关联操作都级联 |
save-update |
保存时级联更新 |
merge |
合并时级联 |
expunge |
擦除时级联 |
delete |
删除时级联 |
delete-orphan |
删除孤儿对象(无归属的子对象) |
评论区