现在位置: 首页 > FastAPI 教程 > 正文

收藏功能与后台管理

本章你将学会用多对多关系实现收藏功能,并用 sqladmin 搭建后台管理。


多对多关系设计

实例

# 文件路径:models.py 新增关联表和修改 Post 模型
from sqlalchemy import Table

# 关联表:User 和 Post 的多对多中间表
favorites = Table(
    "favorites",
    Base.metadata,
    Column("user_id", Integer, ForeignKey("users.id"), primary_key=True),
    Column("post_id", Integer, ForeignKey("posts.id"), primary_key=True),
    Column("created_at", DateTime, default=datetime.utcnow)
)

执行迁移:

(venv) $ alembic revision --autogenerate -m "新增收藏关联表"
(venv) $ alembic upgrade head

收藏与取消收藏路由

实例

# 文件路径:routers/favorites.py
from fastapi import APIRouter, Depends, HTTPException, Request
from sqlalchemy.orm import Session
from database import get_db
from models import Post, favorites
from routers.users import get_current_user
from fastapi.templating import Jinja2Templates

router = APIRouter(prefix="/favorites", tags=["收藏"])
templates = Jinja2Templates(directory="templates")

@router.post("/toggle/{post_id}")
def toggle_favorite(
    post_id: int,
    request: Request,
    db: Session = Depends(get_db),
    current_user = Depends(get_current_user)    # 需要登录
):
    """切换收藏状态"""
    post = db.query(Post).filter(Post.id == post_id).first()
    if not post:
        raise HTTPException(status_code=404, detail="文章不存在")

    # 检查是否已收藏
    is_faved = db.query(favorites).filter(
        favorites.c.user_id == current_user.id,
        favorites.c.post_id == post.id
    ).first() is not None

    if is_faved:
        # 取消收藏
        db.execute(
            favorites.delete().where(
                (favorites.c.user_id == current_user.id) &
                (favorites.c.post_id == post.id)
            )
        )
        db.commit()
    else:
        # 添加收藏
        db.execute(
            favorites.insert().values(user_id=current_user.id, post_id=post.id)
        )
        db.commit()

    from fastapi.responses import RedirectResponse
    return RedirectResponse(url=request.headers.get("referer", "/"), status_code=303)


@router.get("/", name="favorites_list")
def favorites_list(
    request: Request,
    db: Session = Depends(get_db),
    current_user = Depends(get_current_user)
):
    """我的收藏列表"""
    faved_posts = db.query(Post).join(
        favorites, (favorites.c.post_id == Post.id)
    ).filter(
        favorites.c.user_id == current_user.id
    ).order_by(favorites.c.created_at.desc()).all()

    return templates.TemplateResponse("favorites.html", {
        "request": request,
        "posts": faved_posts
    })

在详情页添加收藏按钮

实例

<!-- 文件路径:templates/post_detail.html 标题下方 -->
<div class="post-header">
    <h1>{{ post.title }}</h1>
    <form method="post" action="/favorites/toggle/{{ post.id }}">
        <button type="submit" class="fav-btn">♡ 收藏</button>
    </form>
</div>

sqladmin — 后台管理

sqladmin 是 FastAPI 生态中对标 Django Admin 的后台管理扩展。

它专为 SQLAlchemy 设计,与 FastAPI 无缝集成。

(venv) $ pip install sqladmin

实例

# 文件路径:main.py 中配置 sqladmin
from sqladmin import Admin, ModelView
from database import engine
from models import Post, Category, User

class PostAdmin(ModelView, model=Post):
    column_list = [Post.id, Post.title, Post.category, Post.created_at]
    column_searchable_list = [Post.title, Post.summary]
    column_sortable_list = [Post.created_at, Post.title]
    form_columns = [Post.title, Post.slug, Post.summary, Post.content, Post.category_id]

class CategoryAdmin(ModelView, model=Category):
    column_list = [Category.id, Category.name, Category.slug]
    form_columns = [Category.name, Category.slug]

class UserAdmin(ModelView, model=User):
    column_list = [User.id, User.username, User.email]
    column_searchable_list = [User.username, User.email]
    # 不显示密码字段(安全考虑)
    form_excluded_columns = [User.hashed_password]

# 创建 Admin 实例,绑定到 FastAPI app
admin = Admin(app, engine, title="RUNOOB 博客后台")
admin.add_view(PostAdmin)
admin.add_view(CategoryAdmin)
admin.add_view(UserAdmin)

访问 /admin 进入后台管理界面。

sqladmin vs Flask-Admin vs Django Admin

特性Django AdminFlask-Adminsqladmin
注册方式admin.site.register()admin.add_view(ModelView())Admin + add_view
ORM 支持Django ORMSQLAlchemy / MongoEngineSQLAlchemy(仅)
认证支持内置需手动集成内置 authentication_backend
异步支持不支持不支持原生 async

sqladmin 默认不需要登录就能访问。生产环境需要配置 authentication_backend 来添加认证保护,详见 sqladmin 官方文档。


本章小结

本章你实现了两个重要功能:用 Table 关联表实现收藏的多对多关系;用 sqladmin 快速搭建后台管理系统。

现在博客的用户交互(注册、登录、收藏)和管理功能(Admin 后台)都已完备。