Python实例题:基于 Flask 的博客系统

发布于:2025-06-29 ⋅ 阅读:(21) ⋅ 点赞:(0)

目录

Python实例题

题目

要求:

解题思路:

代码实现:

1. base.html

2. index.html

3. post.html

4. create_post.html

5. login.html

6. register.html

Python实例题

题目

基于 Flask 的博客系统

要求

  • 使用 Flask 框架构建一个简单的博客系统。
  • 实现用户认证(注册、登录、注销)。
  • 支持博客文章的创建、编辑、删除和查看。
  • 使用 SQLite 数据库存储用户和文章数据。
  • 添加评论功能,允许已登录用户对文章发表评论。
  • 实现简单的权限控制(如只有文章作者才能编辑 / 删除文章)。
  • 添加分页功能,美化界面(使用 Bootstrap)。

解题思路

  • 使用 Flask 蓝图组织代码结构。
  • 通过 Flask-Login 处理用户认证。
  • 使用 Flask-SQLAlchemy 管理数据库操作。
  • 结合 WTForms 实现表单验证。
  • 使用 Bootstrap 美化前端界面。

代码实现

from flask import Flask, render_template, request, redirect, url_for, flash, Blueprint
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
from werkzeug.security import generate_password_hash, check_password_hash
from datetime import datetime
import os

# 初始化 Flask 应用
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key-here'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(app.root_path, 'blog.db')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

# 初始化数据库
db = SQLAlchemy(app)

# 初始化 Flask-Login
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'auth.login'

# 数据模型
class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password_hash = db.Column(db.String(120), nullable=False)
    posts = db.relationship('Post', backref='author', lazy=True)
    comments = db.relationship('Comment', backref='author', lazy=True)
    
    def set_password(self, password):
        self.password_hash = generate_password_hash(password)
    
    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100), nullable=False)
    content = db.Column(db.Text, nullable=False)
    date_posted = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    comments = db.relationship('Comment', backref='post', lazy=True)

class Comment(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    content = db.Column(db.Text, nullable=False)
    date_posted = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    post_id = db.Column(db.Integer, db.ForeignKey('post.id'), nullable=False)

# 用户加载回调
@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))

# 认证蓝图
auth = Blueprint('auth', __name__)

@auth.route('/register', methods=['GET', 'POST'])
def register():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        
        if User.query.filter_by(username=username).first():
            flash('用户名已存在', 'error')
            return redirect(url_for('auth.register'))
        
        user = User(username=username)
        user.set_password(password)
        db.session.add(user)
        db.session.commit()
        
        flash('注册成功,请登录', 'success')
        return redirect(url_for('auth.login'))
    
    return render_template('register.html')

@auth.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        
        user = User.query.filter_by(username=username).first()
        
        if user and user.check_password(password):
            login_user(user)
            flash('登录成功', 'success')
            return redirect(url_for('main.index'))
        else:
            flash('用户名或密码错误', 'error')
    
    return render_template('login.html')

@auth.route('/logout')
@login_required
def logout():
    logout_user()
    flash('已注销', 'success')
    return redirect(url_for('main.index'))

# 主蓝图
main = Blueprint('main', __name__)

@main.route('/')
def index():
    page = request.args.get('page', 1, type=int)
    posts = Post.query.order_by(Post.date_posted.desc()).paginate(page=page, per_page=5)
    return render_template('index.html', posts=posts)

@main.route('/post/new', methods=['GET', 'POST'])
@login_required
def new_post():
    if request.method == 'POST':
        title = request.form['title']
        content = request.form['content']
        
        post = Post(title=title, content=content, author=current_user)
        db.session.add(post)
        db.session.commit()
        
        flash('文章发布成功', 'success')
        return redirect(url_for('main.index'))
    
    return render_template('create_post.html', title='新文章')

@main.route('/post/<int:post_id>')
def post(post_id):
    post = Post.query.get_or_404(post_id)
    return render_template('post.html', post=post)

@main.route('/post/<int:post_id>/update', methods=['GET', 'POST'])
@login_required
def update_post(post_id):
    post = Post.query.get_or_404(post_id)
    
    if post.author != current_user:
        flash('你无权编辑此文章', 'error')
        return redirect(url_for('main.post', post_id=post.id))
    
    if request.method == 'POST':
        post.title = request.form['title']
        post.content = request.form['content']
        db.session.commit()
        flash('文章已更新', 'success')
        return redirect(url_for('main.post', post_id=post.id))
    
    return render_template('create_post.html', title='更新文章', post=post)

@main.route('/post/<int:post_id>/delete', methods=['POST'])
@login_required
def delete_post(post_id):
    post = Post.query.get_or_404(post_id)
    
    if post.author != current_user:
        flash('你无权删除此文章', 'error')
        return redirect(url_for('main.post', post_id=post.id))
    
    db.session.delete(post)
    db.session.commit()
    flash('文章已删除', 'success')
    return redirect(url_for('main.index'))

@main.route('/post/<int:post_id>/comment', methods=['POST'])
@login_required
def add_comment(post_id):
    post = Post.query.get_or_404(post_id)
    content = request.form['content']
    
    comment = Comment(content=content, author=current_user, post=post)
    db.session.add(comment)
    db.session.commit()
    
    flash('评论已提交', 'success')
    return redirect(url_for('main.post', post_id=post_id))

# 注册蓝图
app.register_blueprint(main)
app.register_blueprint(auth)

# 创建数据库表
with app.app_context():
    db.create_all()

if __name__ == '__main__':
    app.run(debug=True)

对应的 HTML 模板文件(需要放在 templates 文件夹中):

1. base.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Flask博客</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css">
</head>
<body>
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
        <div class="container">
            <a class="navbar-brand" href="{{ url_for('main.index') }}">Flask博客</a>
            <div class="collapse navbar-collapse" id="navbarNav">
                <ul class="navbar-nav me-auto">
                    <li class="nav-item">
                        <a class="nav-link" href="{{ url_for('main.index') }}">首页</a>
                    </li>
                </ul>
                <ul class="navbar-nav">
                    {% if current_user.is_authenticated %}
                        <li class="nav-item">
                            <a class="nav-link" href="{{ url_for('main.new_post') }}">新文章</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" href="{{ url_for('auth.logout') }}">注销 ({{ current_user.username }})</a>
                        </li>
                    {% else %}
                        <li class="nav-item">
                            <a class="nav-link" href="{{ url_for('auth.login') }}">登录</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" href="{{ url_for('auth.register') }}">注册</a>
                        </li>
                    {% endif %}
                </ul>
            </div>
        </div>
    </nav>
    
    <div class="container mt-4">
        {% with messages = get_flashed_messages(with_categories=true) %}
            {% if messages %}
                {% for category, message in messages %}
                    <div class="alert alert-{{ category }}">
                        {{ message }}
                    </div>
                {% endfor %}
            {% endif %}
        {% endwith %}
        
        {% block content %}{% endblock %}
    </div>
    
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

2. index.html

{% extends "base.html" %}

{% block content %}
    <h1 class="mb-4">博客文章</h1>
    
    {% for post in posts.items %}
        <div class="card mb-4">
            <div class="card-body">
                <h2 class="card-title">{{ post.title }}</h2>
                <p class="card-text">{{ post.content[:200] }}{% if post.content|length > 200 %}...{% endif %}</p>
                <a href="{{ url_for('main.post', post_id=post.id) }}" class="btn btn-primary">阅读更多</a>
            </div>
            <div class="card-footer text-muted">
                发布于 {{ post.date_posted.strftime('%Y-%m-%d') }} by {{ post.author.username }}
            </div>
        </div>
    {% endfor %}
    
    <!-- 分页导航 -->
    <nav aria-label="Page navigation">
        <ul class="pagination justify-content-center">
            {% if posts.has_prev %}
                <li class="page-item">
                    <a class="page-link" href="{{ url_for('main.index', page=posts.prev_num) }}">上一页</a>
                </li>
            {% else %}
                <li class="page-item disabled">
                    <span class="page-link">上一页</span>
                </li>
            {% endif %}
            
            {% for page_num in posts.iter_pages(left_edge=1, right_edge=1, left_current=1, right_current=2) %}
                {% if page_num %}
                    {% if posts.page == page_num %}
                        <li class="page-item active">
                            <span class="page-link">{{ page_num }}</span>
                        </li>
                    {% else %}
                        <li class="page-item">
                            <a class="page-link" href="{{ url_for('main.index', page=page_num) }}">{{ page_num }}</a>
                        </li>
                    {% endif %}
                {% else %}
                    <li class="page-item disabled">
                        <span class="page-link">...</span>
                    </li>
                {% endif %}
            {% endfor %}
            
            {% if posts.has_next %}
                <li class="page-item">
                    <a class="page-link" href="{{ url_for('main.index', page=posts.next_num) }}">下一页</a>
                </li>
            {% else %}
                <li class="page-item disabled">
                    <span class="page-link">下一页</span>
                </li>
            {% endif %}
        </ul>
    </nav>
{% endblock %}

3. post.html

{% extends "base.html" %}

{% block content %}
    <div class="card mb-4">
        <div class="card-body">
            <h2 class="card-title">{{ post.title }}</h2>
            <p class="card-text">{{ post.content }}</p>
        </div>
        <div class="card-footer text-muted">
            发布于 {{ post.date_posted.strftime('%Y-%m-%d %H:%M') }} by {{ post.author.username }}
            
            {% if current_user.is_authenticated and current_user == post.author %}
                <div class="float-right">
                    <a href="{{ url_for('main.update_post', post_id=post.id) }}" class="btn btn-sm btn-outline-secondary">编辑</a>
                    <form action="{{ url_for('main.delete_post', post_id=post.id) }}" method="POST" class="d-inline">
                        <button type="submit" class="btn btn-sm btn-outline-danger" onclick="return confirm('确定要删除这篇文章吗?')">删除</button>
                    </form>
                </div>
            {% endif %}
        </div>
    </div>
    
    <!-- 评论区 -->
    <h3 class="mb-4">评论 ({{ post.comments|length }})</h3>
    
    {% for comment in post.comments %}
        <div class="card mb-3">
            <div class="card-body">
                <p class="card-text">{{ comment.content }}</p>
                <div class="text-muted">
                    <small>由 {{ comment.author.username }} 发布于 {{ comment.date_posted.strftime('%Y-%m-%d %H:%M') }}</small>
                </div>
            </div>
        </div>
    {% endfor %}
    
    <!-- 添加评论表单 -->
    {% if current_user.is_authenticated %}
        <div class="card mb-4">
            <div class="card-header">添加评论</div>
            <div class="card-body">
                <form method="POST" action="{{ url_for('main.add_comment', post_id=post.id) }}">
                    <div class="form-group">
                        <textarea class="form-control" name="content" rows="3" required></textarea>
                    </div>
                    <button type="submit" class="btn btn-primary mt-3">提交评论</button>
                </form>
            </div>
        </div>
    {% else %}
        <div class="alert alert-info">
            <a href="{{ url_for('auth.login') }}">登录</a> 后可发表评论
        </div>
    {% endif %}
{% endblock %}

4. create_post.html

{% extends "base.html" %}

{% block content %}
    <h1>{{ title }}</h1>
    <form method="POST">
        <div class="form-group">
            <label for="title">标题</label>
            <input type="text" class="form-control" id="title" name="title" 
                   value="{{ post.title if post else '' }}" required>
        </div>
        <div class="form-group mt-3">
            <label for="content">内容</label>
            <textarea class="form-control" id="content" name="content" rows="10" required>{{ post.content if post else '' }}</textarea>
        </div>
        <button type="submit" class="btn btn-primary mt-3">提交</button>
    </form>
{% endblock %}

5. login.html

{% extends "base.html" %}

{% block content %}
    <h1>登录</h1>
    <form method="POST">
        <div class="form-group">
            <label for="username">用户名</label>
            <input type="text" class="form-control" id="username" name="username" required>
        </div>
        <div class="form-group mt-3">
            <label for="password">密码</label>
            <input type="password" class="form-control" id="password" name="password" required>
        </div>
        <button type="submit" class="btn btn-primary mt-3">登录</button>
    </form>
    <p class="mt-3">
        还没有账号? <a href="{{ url_for('auth.register') }}">注册</a>
    </p>
{% endblock %}

6. register.html

{% extends "base.html" %}

{% block content %}
    <h1>注册</h1>
    <form method="POST">
        <div class="form-group">
            <label for="username">用户名</label>
            <input type="text" class="form-control" id="username" name="username" required>
        </div>
        <div class="form-group mt-3">
            <label for="password">密码</label>
            <input type="password" class="form-control" id="password" name="password" required>
        </div>
        <button type="submit" class="btn btn-primary mt-3">注册</button>
    </form>
    <p class="mt-3">
        已有账号? <a href="{{ url_for('auth.login') }}">登录</a>
    </p>
{% endblock %}

网站公告

今日签到

点亮在社区的每一天
去签到