下面是一个与前面不同的综合示例,展示如何使用 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. 功能说明
- 注册与登录:用户可以注册和登录。
- 发布帖子:登录用户可以发布帖子。
- 帖子列表:用户可以查看所有帖子。
- 回复帖子:登录用户可以回复帖子。
8. 扩展功能
- 添加帖子分类和搜索功能。
- 增加用户个人中心,查看自己发布的帖子和回复。
- 使用 AJAX 实现无刷新发布帖子和回复。
希望这个示例对你有帮助!如果有任何问题,欢迎随时提问!