Bootstrap

Flask应用开发综合示例——在线论坛系统

下面是一个与前面不同的综合示例,展示如何使用 Python + Flask + HTML + CSS + JavaScript + jQuery + Bootstrap + SQLAlchemy 开发一个简单的 在线论坛系统。用户可以注册、登录、发布帖子、回复帖子以及浏览帖子列表。


1. 项目结构

online_forum/
│
├── app.py                  # Flask 应用主文件
├── models.py               # SQLAlchemy 模型定义
├── requirements.txt        # 依赖文件
├── static/                 # 静态文件(CSS, JS, 图片等)
│   ├── css/
│   │   └── styles.css      # 自定义 CSS
│   └── js/
│       └── scripts.js      # 自定义 JavaScript
│
├── templates/              # HTML 模板文件
│   ├── base.html           # 基础模板
│   ├── index.html          # 主页模板
│   ├── register.html       # 注册页面模板
│   ├── login.html          # 登录页面模板
│   ├── create_post.html    # 发布帖子模板
│   ├── posts.html          # 帖子列表模板
│   ├── post_detail.html    # 帖子详情模板
│   └── reply_post.html     # 回复帖子模板
│
└── database.db             # SQLite 数据库文件

2. 安装依赖

在项目根目录下创建 requirements.txt 文件,内容如下:

Flask==2.3.2
Flask-SQLAlchemy==3.0.5
Flask-Login==0.6.2
Bootstrap==5.3.0
jQuery==3.6.0

安装依赖:

pip install -r requirements.txt

3. Flask 应用 (app.py)

from flask import Flask, render_template, request, redirect, url_for, flash
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.db'  # SQLite 数据库
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SECRET_KEY'] = 'your_secret_key'  # 用于 Flask-Login 的密钥
db = SQLAlchemy(app)

# Flask-Login 配置
login_manager = LoginManager(app)
login_manager.login_view = 'login'

# 用户模型
class User(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(50), unique=True, nullable=False)
    password = db.Column(db.String(100), nullable=False)
    posts = db.relationship('Post', backref='author', lazy=True)
    replies = db.relationship('Reply', backref='author', lazy=True)

# 帖子模型
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)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    replies = db.relationship('Reply', backref='post', lazy=True)

# 回复模型
class Reply(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    content = db.Column(db.Text, nullable=False)
    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))

# 主页路由
@app.route('/')
def index():
    return render_template('index.html'))

# 注册路由
@app.route('/register', methods=['GET', 'POST'])
def register():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        user = User(username=username, password=password)
        db.session.add(user)
        db.session.commit()
        flash('注册成功!请登录。', 'success')
        return redirect(url_for('login'))
    return render_template('register.html'))

# 登录路由
@app.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.password == password:
            login_user(user)
            flash('登录成功!', 'success')
            return redirect(url_for('posts'))
        else:
            flash('用户名或密码错误!', 'danger')
    return render_template('login.html'))

# 注销路由
@app.route('/logout')
@login_required
def logout():
    logout_user()
    flash('您已注销!', 'success')
    return redirect(url_for('index'))

# 发布帖子路由
@app.route('/create_post', methods=['GET', 'POST'])
@login_required
def create_post():
    if request.method == 'POST':
        title = request.form['title']
        content = request.form['content']
        new_post = Post(title=title, content=content, author=current_user)
        db.session.add(new_post)
        db.session.commit()
        flash('帖子发布成功!', 'success')
        return redirect(url_for('posts'))
    return render_template('create_post.html'))

# 帖子列表路由
@app.route('/posts')
def posts():
    posts = Post.query.all()
    return render_template('posts.html', posts=posts)

# 帖子详情路由
@app.route('/post/<int:id>')
def post_detail(id):
    post = Post.query.get_or_404(id)
    return render_template('post_detail.html', post=post)

# 回复帖子路由
@app.route('/reply/<int:id>', methods=['GET', 'POST'])
@login_required
def reply_post(id):
    post = Post.query.get_or_404(id)
    if request.method == 'POST':
        content = request.form['content']
        new_reply = Reply(content=content, author=current_user, post=post)
        db.session.add(new_reply)
        db.session.commit()
        flash('回复提交成功!', 'success')
        return redirect(url_for('post_detail', id=post.id))
    return render_template('reply_post.html', post=post)

if __name__ == '__main__':
    with app.app_context():
        db.create_all()
    app.run(debug=True)

4. HTML 模板

基础模板 (templates/base.html)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>在线论坛系统</title>
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
    <link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
</head>
<body>
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
        <div class="container">
            <a class="navbar-brand" href="{{ url_for('index') }}">在线论坛系统</a>
            <div class="navbar-nav">
                {% if current_user.is_authenticated %}
                    <a class="nav-link" href="{{ url_for('create_post') }}">发布帖子</a>
                    <a class="nav-link" href="{{ url_for('posts') }}">帖子列表</a>
                    <a class="nav-link" href="{{ url_for('logout') }}">注销</a>
                {% else %}
                    <a class="nav-link" href="{{ url_for('login') }}">登录</a>
                    <a class="nav-link" href="{{ url_for('register') }}">注册</a>
                {% endif %}
            </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://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
    <script src="{{ url_for('static', filename='js/scripts.js') }}"></script>
</body>
</html>
主页模板 (templates/index.html)
{% extends 'base.html' %}

{% block content %}
<h1>欢迎来到在线论坛系统!</h1>
<p>发布帖子或参与讨论。</p>
{% if current_user.is_authenticated %}
    <a href="{{ url_for('create_post') }}" class="btn btn-primary">发布帖子</a>
{% else %}
    <a href="{{ url_for('login') }}" class="btn btn-primary">登录</a>
    <a href="{{ url_for('register') }}" class="btn btn-secondary">注册</a>
{% endif %}
{% endblock %}
注册页面模板 (templates/register.html)
{% extends 'base.html' %}

{% block content %}
<h1>注册</h1>
<form method="POST">
    <div class="mb-3">
        <label for="username" class="form-label">用户名</label>
        <input type="text" class="form-control" id="username" name="username" required>
    </div>
    <div class="mb-3">
        <label for="password" class="form-label">密码</label>
        <input type="password" class="form-control" id="password" name="password" required>
    </div>
    <button type="submit" class="btn btn-primary">注册</button>
</form>
{% endblock %}
登录页面模板 (templates/login.html)
{% extends 'base.html' %}

{% block content %}
<h1>登录</h1>
<form method="POST">
    <div class="mb-3">
        <label for="username" class="form-label">用户名</label>
        <input type="text" class="form-control" id="username" name="username" required>
    </div>
    <div class="mb-3">
        <label for="password" class="form-label">密码</label>
        <input type="password" class="form-control" id="password" name="password" required>
    </div>
    <button type="submit" class="btn btn-primary">登录</button>
</form>
{% endblock %}
发布帖子模板 (templates/create_post.html)
{% extends 'base.html' %}

{% block content %}
<h1>发布帖子</h1>
<form method="POST">
    <div class="mb-3">
        <label for="title" class="form-label">标题</label>
        <input type="text" class="form-control" id="title" name="title" required>
    </div>
    <div class="mb-3">
        <label for="content" class="form-label">内容</label>
        <textarea class="form-control" id="content" name="content" rows="5" required></textarea>
    </div>
    <button type="submit" class="btn btn-primary">发布</button>
</form>
{% endblock %}
帖子列表模板 (templates/posts.html)
{% extends 'base.html' %}

{% block content %}
<h1>帖子列表</h1>
<ul class="list-group">
    {% for post in posts %}
    <li class="list-group-item">
        <h5>{{ post.title }}</h5>
        <p>{{ post.content }}</p>
        <p>作者: {{ post.author.username }}</p>
        <a href="{{ url_for('post_detail', id=post.id) }}" class="btn btn-primary btn-sm">查看详情</a>
    </li>
    {% endfor %}
</ul>
{% endblock %}
帖子详情模板 (templates/post_detail.html)
{% extends 'base.html' %}

{% block content %}
<h1>{{ post.title }}</h1>
<p>{{ post.content }}</p>
<p>作者: {{ post.author.username }}</p>
<h3>回复</h3>
<ul class="list-group">
    {% for reply in post.replies %}
    <li class="list-group-item">
        <p>{{ reply.content }}</p>
        <p>回复者: {{ reply.author.username }}</p>
    </li>
    {% endfor %}
</ul>
<a href="{{ url_for('reply_post', id=post.id) }}" class="btn btn-primary mt-3">回复</a>
<a href="{{ url_for('posts') }}" class="btn btn-secondary mt-3">返回帖子列表</a>
{% endblock %}
回复帖子模板 (templates/reply_post.html)
{% extends 'base.html' %}

{% block content %}
<h1>回复帖子: {{ post.title }}</h1>
<form method="POST">
    <div class="mb-3">
        <label for="content" class="form-label">回复内容</label>
        <textarea class="form-control" id="content" name="content" rows="5" required></textarea>
    </div>
    <button type="submit" class="btn btn-primary">提交</button>
</form>
<a href="{{ url_for('post_detail', id=post.id) }}" class="btn btn-secondary mt-3">返回帖子详情</a>
{% endblock %}

5. 静态文件

CSS (static/css/styles.css)
body {
    padding: 20px;
}

h1 {
    color: #333;
}
JavaScript (static/js/scripts.js)
$(document).ready(function() {
    console.log("Online Forum is ready!");
});

6. 运行应用

在项目根目录下运行:

python app.py

访问 http://127.0.0.1:5000/ 即可看到在线论坛系统。


7. 功能说明

  1. 注册与登录:用户可以注册和登录。
  2. 发布帖子:登录用户可以发布帖子。
  3. 帖子列表:用户可以查看所有帖子。
  4. 回复帖子:登录用户可以回复帖子。

8. 扩展功能

  • 添加帖子分类和搜索功能。
  • 增加用户个人中心,查看自己发布的帖子和回复。
  • 使用 AJAX 实现无刷新发布帖子和回复。

希望这个示例对你有帮助!如果有任何问题,欢迎随时提问!

;