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

收藏功能与后台管理

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


多对多关系设计

收藏功能的需求:一个用户可以收藏多篇文章,一篇文章可以被多个用户收藏

在 SQLAlchemy 中,多对多关系通过关联表(Association Table)实现。

实例

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

# 关联表:不需要定义为模型类,用 db.Table 直接创建
favorites = db.Table('favorites',
    db.Column('user_id', db.Integer, db.ForeignKey('users.id'), primary_key=True),
    db.Column('post_id', db.Integer, db.ForeignKey('posts.id'), primary_key=True),
    db.Column('created_at', db.DateTime, default=datetime.utcnow)
)

class Post(db.Model):
    __tablename__ = 'posts'
    # ... 已有字段不变 ...

然后执行迁移:

(venv) $ flask db migrate -m "新增收藏关联表"
(venv) $ flask db upgrade

收藏与取消收藏视图

实例

# 文件路径:app/blueprints/user.py(新建文件)
from flask import Blueprint, render_template, redirect, url_for, request, flash
from flask_login import login_required, current_user
from app.models import Post, db, favorites

user_bp = Blueprint('user', __name__)

@user_bp.route("/favorite/<int:post_id>", methods=['POST'])
@login_required
def toggle_favorite(post_id):
    """切换收藏状态"""
    post = Post.query.get_or_404(post_id)

    # 检查是否已收藏:查询关联表中是否有此用户-文章组合
    is_faved = db.session.query(favorites).filter(
        favorites.c.user_id == current_user.id,
        favorites.c.post_id == post.id
    ).first() is not None

    if is_faved:
        # 取消收藏
        db.session.execute(
            favorites.delete().where(
                (favorites.c.user_id == current_user.id) &
                (favorites.c.post_id == post.id)
            )
        )
        db.session.commit()
        flash(f'已取消收藏「{post.title}」', 'info')
    else:
        # 添加收藏
        db.session.execute(
            favorites.insert().values(user_id=current_user.id, post_id=post.id)
        )
        db.session.commit()
        flash(f'已收藏「{post.title}」', 'success')

    return redirect(request.referrer or url_for('main.index'))


@user_bp.route("/favorites")
@login_required
def favorites_list():
    """当前用户的收藏列表"""
    # 通过关联表查询用户收藏的文章
    faved_posts = Post.query.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 render_template('favorites.html', posts=faved_posts)

在 app.py 中注册 Blueprint

实例

from app.blueprints.user import user_bp
app.register_blueprint(user_bp)

在详情页和列表页添加收藏按钮

实例

<!-- 在 post_detail.html 和 index.html 的文章操作区加入 -->
{% if current_user.is_authenticated %}
<form method="post" action="{{ url_for('user.toggle_favorite', post_id=post.id) }}">
    <button type="submit" class="fav-btn">
        {% set is_faved = false %}
        {# 检查当前用户是否在文章的收藏者列表中 #}
        ♡ 收藏
    </button>
</form>
{% endif %}

收藏操作用 POST 请求(form 提交),而非 GET 链接。GET 请求不应该产生副作用(修改数据库),这是 Web 开发的基本原则。


Flask-Admin — 后台管理

Flask-Admin 是 Flask 生态中对标 Django Admin 的扩展。

配置后,你可以在浏览器中管理文章和分类数据。

(venv) $ pip install flask-admin

实例

# 文件路径:app.py 或新建 admin_setup.py
from flask_admin import Admin
from flask_admin.contrib.sqla import ModelView
from app.models import Post, Category, User

# 自定义 ModelView:控制后台显示哪些字段
class PostAdmin(ModelView):
    column_list = ['title', 'category', 'created_at', 'updated_at']
    column_searchable_list = ['title', 'summary']
    column_filters = ['category', 'created_at']
    form_columns = ['title', 'slug', 'summary', 'content', 'category']

class CategoryAdmin(ModelView):
    column_list = ['name', 'slug']
    form_columns = ['name', 'slug']

# 创建 Admin 实例
admin = Admin(app, name='RUNOOB 博客后台', template_mode='bootstrap4')
admin.add_view(PostAdmin(Post, db.session, name='文章'))
admin.add_view(CategoryAdmin(Category, db.session, name='分类'))
admin.add_view(ModelView(User, db.session, name='用户'))

启动服务器,访问 /admin,即可进入后台管理界面。

Flask-Admin vs Django Admin

特性Flask-AdminDjango Admin
集成方式第三方扩展,需 pip install内置在 django.contrib.admin
注册模型admin.add_view(ModelView(Model, session))admin.site.register(Model)
列表字段column_listlist_display
搜索column_searchable_listsearch_fields
过滤column_filterslist_filter
需要认证不内置(需手动加 @login_required)内置管理员权限

Flask-Admin 默认不需要登录就能访问,需要结合 Flask-Login 加权限控制。一个简单的方式是在视图函数前加 @login_required,或者覆盖 ModelView 的 is_accessible 方法检查管理员权限。


本章小结

本章你实现了两个重要功能:用 db.Table 关联表 + db.session.execute 操作实现收藏的多对多关系;用 Flask-Admin 快速搭建后台管理系统。

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