Python Web开发基础 - Flask框架
1. Web开发概述
作为Java开发者,您可能已经熟悉了Spring Boot等Web框架。在Python世界中,有多种优秀的Web框架可供选择,其中Flask是一个轻量级、灵活且易于学习的框架,非常适合快速构建Web应用。
2. Flask简介
Flask是一个微型Web框架,由Werkzeug(WSGI工具库)和Jinja(模板引擎)组成。它被设计为简单、灵活且可扩展,适合从小型应用到复杂项目的各种需求。
Flask的特点
- 轻量级:核心简洁,但可通过扩展增加功能
- 灵活性:不强制特定的项目结构或依赖
- 易于学习:API简单直观,文档完善
- 可扩展:丰富的扩展生态系统
- WSGI兼容:基于Werkzeug,符合WSGI标准
Flask vs Django
| 特性 | Flask | Django |
|---|---|---|
| 类型 | 微框架 | 全栈框架 |
| 学习曲线 | 平缓 | 较陡 |
| 灵活性 | 高 | 中等 |
| 内置功能 | 少 | 丰富 |
| 适用场景 | 小型到中型应用,API服务 | 大型复杂应用 |
| 项目结构 | 自由定义 | 相对固定 |
3. 安装Flask
使用pip安装Flask非常简单:
# 创建虚拟环境
python -m venv venv
# 激活虚拟环境
# Windows
venv\Scripts\activate
# macOS/Linux
source venv/bin/activate
# 安装Flask
pip install flask
4. 第一个Flask应用
让我们创建一个简单的"Hello World"应用:
# app.py
from flask import Flask
# 创建Flask应用实例
app = Flask(__name__)
# 定义路由和视图函数
@app.route('/')
def hello_world():
return 'Hello, World!'
# 启动应用
if __name__ == '__main__':
app.run(debug=True)
运行应用:
python app.py
访问 http://127.0.0.1:5000/ 即可看到"Hello, World!"消息。
理解代码
Flask(__name__)创建Flask应用实例,__name__是当前模块的名称@app.route('/')是一个装饰器,将URL路径映射到视图函数app.run(debug=True)启动开发服务器,debug=True启用调试模式
5. 路由和视图
Flask请求处理流程
sequenceDiagram
participant 客户端
participant WSGI服务器
participant Flask应用
participant 路由系统
participant 视图函数
participant 模板引擎
客户端->>WSGI服务器: HTTP请求
WSGI服务器->>Flask应用: 转发请求
Flask应用->>路由系统: 查找匹配路由
路由系统->>视图函数: 调用对应视图函数
视图函数->>模板引擎: 渲染模板(可选)
模板引擎-->>视图函数: 返回渲染结果
视图函数-->>Flask应用: 返回响应
Flask应用-->>WSGI服务器: 返回响应
WSGI服务器-->>客户端: HTTP响应
Note over 路由系统,视图函数: 中间件和钩子函数在请求处理过程中执行
基本路由
@app.route('/')
def index():
return '首页'
@app.route('/about')
def about():
return '关于我们'
动态路由
@app.route('/user/<username>')
def show_user_profile(username):
return f'用户: {username}'
@app.route('/post/<int:post_id>')
def show_post(post_id):
return f'帖子ID: {post_id}'
动态路由部分可以指定类型:
string(默认):接受任何不包含斜杠的文本int:接受正整数float:接受正浮点数path:接受包含斜杠的文本uuid:接受UUID字符串
HTTP方法
默认情况下,路由只响应GET请求。可以通过methods参数指定允许的HTTP方法:
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
# 处理表单提交
return '处理登录'
else:
# 显示登录表单
return '显示登录表单'
URL构建
使用url_for()函数可以根据视图函数名生成URL:
from flask import url_for
@app.route('/')
def index():
# 生成'/user/john'的URL
user_url = url_for('show_user_profile', username='john')
return f'用户页面: {user_url}'
6. 请求和响应
请求对象
Flask提供了全局的request对象来访问请求数据:
from flask import request
@app.route('/login', methods=['POST'])
def login():
username = request.form.get('username')
password = request.form.get('password')
# 处理登录逻辑
if username == 'admin' and password == 'secret':
return '登录成功'
return '登录失败'
常用的请求属性和方法:
request.form:表单数据(POST请求)request.args:URL参数(查询字符串)request.cookies:Cookie数据request.files:上传的文件request.headers:HTTP头信息request.method:HTTP请求方法request.path:请求路径request.remote_addr:客户端IP地址
响应对象
Flask视图函数可以返回多种类型的响应:
from flask import make_response, jsonify, redirect, url_for
# 返回字符串(自动转换为响应对象)
@app.route('/hello')
def hello():
return 'Hello World'
# 返回自定义响应
@app.route('/custom')
def custom_response():
response = make_response('Custom Response')
response.status_code = 200
response.headers['Custom-Header'] = 'Value'
return response
# 返回JSON
@app.route('/api/data')
def get_data():
data = {
'name': '张三',
'age': 30,
'city': '北京'
}
return jsonify(data)
# 重定向
@app.route('/redirect')
def redirect_example():
return redirect(url_for('hello'))
7. 模板渲染
Flask使用Jinja2作为模板引擎,可以将Python变量传递到HTML模板中:
目录结构
myapp/
├── app.py
└── templates/
├── base.html
└── index.html
模板文件
<!-- templates/base.html -->
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}{% endblock %} - 我的应用</title>
</head>
<body>
<nav>
<a href="{{ url_for('index') }}">首页</a>
<a href="{{ url_for('about') }}">关于</a>
</nav>
<div class="content">
{% block content %}{% endblock %}
</div>
</body>
</html>
<!-- templates/index.html -->
{% extends 'base.html' %}
{% block title %}首页{% endblock %}
{% block content %}
<h1>欢迎, {{ name }}!</h1>
<h2>用户列表:</h2>
<ul>
{% for user in users %}
<li>{{ user.name }} - {{ user.email }}</li>
{% endfor %}
</ul>
{% endblock %}
渲染模板
from flask import render_template
@app.route('/')
def index():
name = '张三'
users = [
{'name': '张三', 'email': 'zhang@example.com'},
{'name': '李四', 'email': 'li@example.com'},
{'name': '王五', 'email': 'wang@example.com'}
]
return render_template('index.html', name=name, users=users)
Jinja2模板语法
{{ ... }}:输出表达式的值{% ... %}:控制结构(if、for等){# ... #}:注释{% extends '...' %}:模板继承{% block ... %} {% endblock %}:定义可被子模板覆盖的块
8. 静态文件
Flask自动添加一个static路由来提供静态文件:
目录结构
myapp/
├── app.py
├── static/
│ ├── css/
│ │ └── style.css
│ ├── js/
│ │ └── script.js
│ └── images/
│ └── logo.png
└── templates/
└── index.html
在模板中引用静态文件
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
<script src="{{ url_for('static', filename='js/script.js') }}"></script>
<img src="{{ url_for('static', filename='images/logo.png') }}" alt="Logo">
9. 表单处理
HTML表单
<!-- templates/login.html -->
{% extends 'base.html' %}
{% block title %}登录{% endblock %}
{% block content %}
<h1>登录</h1>
{% if error %}
<div class="error">{{ error }}</div>
{% endif %}
<form method="post">
<div>
<label for="username">用户名:</label>
<input type="text" id="username" name="username" required>
</div>
<div>
<label for="password">密码:</label>
<input type="password" id="password" name="password" required>
</div>
<div>
<button type="submit">登录</button>
</div>
</form>
{% endblock %}
处理表单提交
from flask import request, render_template, redirect, url_for, session
@app.route('/login', methods=['GET', 'POST'])
def login():
error = None
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
# 简单的验证逻辑(实际应用中应使用数据库和密码哈希)
if username == 'admin' and password == 'password':
session['username'] = username # 存储用户会话
return redirect(url_for('dashboard'))
else:
error = '无效的用户名或密码'
return render_template('login.html', error=error)
@app.route('/dashboard')
def dashboard():
# 检查用户是否已登录
if 'username' not in session:
return redirect(url_for('login'))
return render_template('dashboard.html', username=session['username'])
使用Flask-WTF扩展
Flask-WTF是一个集成了WTForms的Flask扩展,提供了更强大的表单处理功能:
pip install flask-wtf
from flask import Flask, render_template, redirect, url_for, flash
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Email, Length
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key' # 用于CSRF保护
# 定义表单类
class LoginForm(FlaskForm):
username = StringField('用户名', validators=[DataRequired()])
password = PasswordField('密码', validators=[DataRequired(), Length(min=6)])
submit = SubmitField('登录')
@app.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
# 表单验证通过
username = form.username.data
password = form.password.data
# 处理登录逻辑
if username == 'admin' and password == 'password':
flash('登录成功!', 'success')
return redirect(url_for('dashboard'))
else:
flash('无效的用户名或密码', 'error')
return render_template('login_wtf.html', form=form)
<!-- templates/login_wtf.html -->
{% extends 'base.html' %}
{% block title %}登录{% endblock %}
{% block content %}
<h1>登录</h1>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="{{ category }}">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
<form method="post">
{{ form.hidden_tag() }}
<div>
{{ form.username.label }}
{{ form.username() }}
{% if form.username.errors %}
<div class="errors">
{% for error in form.username.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% endif %}
</div>
<div>
{{ form.password.label }}
{{ form.password() }}
{% if form.password.errors %}
<div class="errors">
{% for error in form.password.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% endif %}
</div>
<div>
{{ form.submit() }}
</div>
</form>
{% endblock %}
10. 会话和Cookie
会话(Session)
Flask提供了session对象来存储跨请求的用户数据:
from flask import session
app.config['SECRET_KEY'] = 'your-secret-key' # 必须设置密钥
@app.route('/set_session')
def set_session():
session['username'] = '张三'
return '会话已设置'
@app.route('/get_session')
def get_session():
username = session.get('username', '未知用户')
return f'当前用户: {username}'
@app.route('/clear_session')
def clear_session():
session.pop('username', None) # 删除特定键
# session.clear() # 清除整个会话
return '会话已清除'
Cookie
from flask import make_response
@app.route('/set_cookie')
def set_cookie():
response = make_response('Cookie已设置')
response.set_cookie('user_id', '123', max_age=3600) # 过期时间为1小时
return response
@app.route('/get_cookie')
def get_cookie():
user_id = request.cookies.get('user_id', '未知')
return f'用户ID: {user_id}'
@app.route('/clear_cookie')
def clear_cookie():
response = make_response('Cookie已清除')
response.delete_cookie('user_id')
return response
11. 数据库集成
Flask本身不包含数据库抽象层,但可以轻松集成各种数据库。
使用SQLAlchemy(ORM)
SQLAlchemy是Python中最流行的ORM工具,Flask-SQLAlchemy是其Flask扩展:
pip install flask-sqlalchemy
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
# 定义模型
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
posts = db.relationship('Post', backref='author', lazy=True)
def __repr__(self):
return f'<User {self.username}>'
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)
def __repr__(self):
return f'<Post {self.title}>'
# 创建数据库表
with app.app_context():
db.create_all()
# 添加数据
@app.route('/add_user')
def add_user():
user = User(username='张三', email='zhang@example.com')
db.session.add(user)
db.session.commit()
return '用户已添加'
# 查询数据
@app.route('/users')
def list_users():
users = User.query.all()
result = '<h1>用户列表</h1><ul>'
for user in users:
result += f'<li>{user.username} - {user.email}</li>'
result += '</ul>'
return result
12. RESTful API开发
Flask非常适合构建RESTful API:
from flask import Flask, jsonify, request, abort
app = Flask(__name__)
# 模拟数据库
USERS = [
{'id': 1, 'name': '张三', 'email': 'zhang@example.com'},
{'id': 2, 'name': '李四', 'email': 'li@example.com'}
]
# 获取所有用户
@app.route('/api/users', methods=['GET'])
def get_users():
return jsonify({'users': USERS})
# 获取单个用户
@app.route('/api/users/<int:user_id>', methods=['GET'])
def get_user(user_id):
user = next((user for user in USERS if user['id'] == user_id), None)
if user is None:
abort(404) # 返回404错误
return jsonify({'user': user})
# 创建新用户
@app.route('/api/users', methods=['POST'])
def create_user():
if not request.json or 'name' not in request.json or 'email' not in request.json:
abort(400) # 返回400错误
user = {
'id': USERS[-1]['id'] + 1 if USERS else 1,
'name': request.json['name'],
'email': request.json['email']
}
USERS.append(user)
return jsonify({'user': user}), 201 # 返回201状态码
# 更新用户
@app.route('/api/users/<int:user_id>', methods=['PUT'])
def update_user(user_id):
user = next((user for user in USERS if user['id'] == user_id), None)
if user is None:
abort(404)
if not request.json:
abort(400)
user['name'] = request.json.get('name', user['name'])
user['email'] = request.json.get('email', user['email'])
return jsonify({'user': user})
# 删除用户
@app.route('/api/users/<int:user_id>', methods=['DELETE'])
def delete_user(user_id):
user = next((user for user in USERS if user['id'] == user_id), None)
if user is None:
abort(404)
USERS.remove(user)
return jsonify({'result': True})
# 自定义错误处理
@app.errorhandler(404)
def not_found(error):
return jsonify({'error': 'Not found'}), 404
@app.errorhandler(400)
def bad_request(error):
return jsonify({'error': 'Bad request'}), 400
13. 项目结构
随着应用规模增长,良好的项目结构变得重要:
简单的项目结构
myapp/
├── app.py # 应用入口
├── config.py # 配置文件
├── models.py # 数据模型
├── forms.py # 表单定义
├── routes.py # 路由和视图
├── static/ # 静态文件
│ ├── css/
│ ├── js/
│ └── images/
└── templates/ # 模板文件
├── base.html
├── index.html
└── ...
使用蓝图(Blueprint)组织大型应用
蓝图允许将应用分割成多个模块:
myapp/
├── app.py # 应用入口
├── config.py # 配置文件
├── models.py # 数据模型
├── extensions.py # 扩展实例化
├── blueprints/ # 蓝图模块
│ ├── main/ # 主蓝图
│ │ ├── __init__.py
│ │ ├── routes.py
│ │ └── forms.py
│ ├── auth/ # 认证蓝图
│ │ ├── __init__.py
│ │ ├── routes.py
│ │ └── forms.py
│ └── api/ # API蓝图
│ ├── __init__.py
│ └── routes.py
├── static/ # 静态文件
└── templates/ # 模板文件
├── base.html
├── main/
├── auth/
└── ...
蓝图实现示例:
# blueprints/main/__init__.py
from flask import Blueprint
main = Blueprint('main', __name__)
from . import routes
# blueprints/main/routes.py
from flask import render_template
from . import main
@main.route('/')
def index():
return render_template('main/index.html')
@main.route('/about')
def about():
return render_template('main/about.html')
# app.py
from flask import Flask
from config import Config
from extensions import db
from blueprints.main import main
from blueprints.auth import auth
from blueprints.api import api
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(config_class)
# 初始化扩展
db.init_app(app)
# 注册蓝图
app.register_blueprint(main)
app.register_blueprint(auth, url_prefix='/auth')
app.register_blueprint(api, url_prefix='/api')
return app
if __name__ == '__main__':
app = create_app()
app.run(debug=True)
14. 部署Flask应用
生产环境配置
在生产环境中,应该禁用调试模式并使用适当的WSGI服务器:
# 禁用调试模式
app.run(debug=False)
使用Gunicorn部署
Gunicorn是一个Python WSGI HTTP服务器:
# 安装Gunicorn
pip install gunicorn
# 运行应用
gunicorn -w 4 -b 127.0.0.1:8000 app:app
使用uWSGI部署
uWSGI是另一个流行的WSGI服务器:
# 安装uWSGI
pip install uwsgi
# 运行应用
uwsgi --http 127.0.0.1:8000 --module app:app
使用Nginx和WSGI服务器
在生产环境中,通常使用Nginx作为反向代理,配合WSGI服务器:
Client <-> Nginx <-> WSGI Server (Gunicorn/uWSGI) <-> Flask Application
15. 练习:构建简单的博客应用
让我们创建一个简单的博客应用,包含以下功能:
- 查看博客文章列表
- 查看单篇文章详情
- 添加新文章(需要登录)
- 用户注册和登录
项目结构
blog/
├── app.py
├── config.py
├── models.py
├── forms.py
├── static/
│ └── css/
│ └── style.css
└── templates/
├── base.html
├── index.html
├── post.html
├── create_post.html
├── login.html
└── register.html
实现代码
# config.py
import os
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY') or 'dev-key'
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or 'sqlite:///blog.db'
SQLALCHEMY_TRACK_MODIFICATIONS = False
# models.py
from datetime import datetime
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash
db = SQLAlchemy()
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password_hash = db.Column(db.String(128))
posts = db.relationship('Post', backref='author', lazy='dynamic')
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)
def __repr__(self):
return f'<User {self.username}>'
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)
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
def __repr__(self):
return f'<Post {self.title}>'
# forms.py
from flask_wtf import FlaskForm
from wtforms import StringField, TextAreaField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Email, EqualTo, Length, ValidationError
from models import User
class LoginForm(FlaskForm):
username = StringField('用户名', validators=[DataRequired()])
password = PasswordField('密码', validators=[DataRequired()])
submit = SubmitField('登录')
class RegistrationForm(FlaskForm):
username = StringField('用户名', validators=[DataRequired(), Length(min=2, max=20)])
email = StringField('邮箱', validators=[DataRequired(), Email()])
password = PasswordField('密码', validators=[DataRequired()])
confirm_password = PasswordField('确认密码', validators=[DataRequired(), EqualTo('password')])
submit = SubmitField('注册')
def validate_username(self, username):
user = User.query.filter_by(username=username.data).first()
if user:
raise ValidationError('该用户名已被使用')
def validate_email(self, email):
user = User.query.filter_by(email=email.data).first()
if user:
raise ValidationError('该邮箱已被注册')
class PostForm(FlaskForm):
title = StringField('标题', validators=[DataRequired()])
content = TextAreaField('内容', validators=[DataRequired()])
submit = SubmitField('发布')
# app.py
from flask import Flask, render_template, redirect, url_for, flash, request, abort
from flask_login import LoginManager, login_user, logout_user, login_required, current_user
from config import Config
from models import db, User, Post
from forms import LoginForm, RegistrationForm, PostForm
app = Flask(__name__)
app.config.from_object(Config)
# 初始化扩展
db.init_app(app)
login_manager = LoginManager(app)
login_manager.login_view = 'login'
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
# 创建数据库表
with app.app_context():
db.create_all()
# 路由
@app.route('/')
def index():
page = request.args.get('page', 1, type=int)
posts = Post.query.order_by(Post.timestamp.desc()).paginate(page=page, per_page=5)
return render_template('index.html', posts=posts)
@app.route('/post/<int:post_id>')
def post(post_id):
post = Post.query.get_or_404(post_id)
return render_template('post.html', post=post)
@app.route('/post/new', methods=['GET', 'POST'])
@login_required
def new_post():
form = PostForm()
if form.validate_on_submit():
post = Post(title=form.title.data, content=form.content.data, author=current_user)
db.session.add(post)
db.session.commit()
flash('文章已发布!', 'success')
return redirect(url_for('index'))
return render_template('create_post.html', form=form, title='新文章')
@app.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:
abort(403)
form = PostForm()
if form.validate_on_submit():
post.title = form.title.data
post.content = form.content.data
db.session.commit()
flash('文章已更新!', 'success')
return redirect(url_for('post', post_id=post.id))
elif request.method == 'GET':
form.title.data = post.title
form.content.data = post.content
return render_template('create_post.html', form=form, title='更新文章')
@app.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:
abort(403)
db.session.delete(post)
db.session.commit()
flash('文章已删除!', 'success')
return redirect(url_for('index'))
@app.route('/register', methods=['GET', 'POST'])
def register():
if current_user.is_authenticated:
return redirect(url_for('index'))
form = RegistrationForm()
if form.validate_on_submit():
user = User(username=form.username.data, email=form.email.data)
user.set_password(form.password.data)
db.session.add(user)
db.session.commit()
flash('注册成功!现在您可以登录了。', 'success')
return redirect(url_for('login'))
return render_template('register.html', form=form, title='注册')
@app.route('/login', methods=['GET', 'POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('index'))
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.username.data).first()
if user and user.check_password(form.password.data):
login_user(user)
next_page = request.args.get('next')
return redirect(next_page or url_for('index'))
else:
flash('登录失败。请检查用户名和密码。', 'danger')
return render_template('login.html', form=form, title='登录')
@app.route('/logout')
def logout():
logout_user()
return redirect(url_for('index'))
if __name__ == '__main__':
app.run(debug=True)
模板文件
<!-- templates/base.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}博客{% endblock %}</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body>
<header>
<nav>
<div class="container">
<h1><a href="{{ url_for('index') }}">我的博客</a></h1>
<ul>
<li><a href="{{ url_for('index') }}">首页</a></li>
{% if current_user.is_authenticated %}
<li><a href="{{ url_for('new_post') }}">写文章</a></li>
<li><a href="{{ url_for('logout') }}">退出</a></li>
{% else %}
<li><a href="{{ url_for('login') }}">登录</a></li>
<li><a href="{{ url_for('register') }}">注册</a></li>
{% endif %}
</ul>
</div>
</nav>
</header>
<main class="container">
{% 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 %}
</main>
<footer>
<div class="container">
<p>© 2025 我的博客</p>
</div>
</footer>
</body>
</html>
<!-- templates/index.html -->
{% extends 'base.html' %}
{% block title %}首页{% endblock %}
{% block content %}
<h1>最新文章</h1>
{% for post in posts.items %}
<article class="post">
<h2><a href="{{ url_for('post', post_id=post.id) }}">{{ post.title }}</a></h2>
<div class="post-info">
<span>作者: {{ post.author.username }}</span>
<span>发布时间: {{ post.timestamp.strftime('%Y-%m-%d %H:%M') }}</span>
</div>
<div class="post-content">
{{ post.content[:200] }}{% if post.content|length > 200 %}...{% endif %}
</div>
<a href="{{ url_for('post', post_id=post.id) }}" class="read-more">阅读更多</a>
</article>
{% endfor %}
<!-- 分页 -->
<div class="pagination">
{% if posts.has_prev %}
<a href="{{ url_for('index', page=posts.prev_num) }}">« 上一页</a>
{% endif %}
{% for page_num in posts.iter_pages() %}
{% if page_num %}
{% if page_num == posts.page %}
<span class="current-page">{{ page_num }}</span>
{% else %}
<a href="{{ url_for('index', page=page_num) }}">{{ page_num }}</a>
{% endif %}
{% else %}
<span>...</span>
{% endif %}
{% endfor %}
{% if posts.has_next %}
<a href="{{ url_for('index', page=posts.next_num) }}">下一页 »</a>
{% endif %}
</div>
{% endblock %}
CSS样式
/* static/css/style.css */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: #333;
background-color: #f4f4f4;
}
.container {
width: 80%;
max-width: 1200px;
margin: 0 auto;
padding: 0 15px;
}
/* 导航栏 */
header {
background-color: #333;
color: #fff;
padding: 1rem 0;
}
nav {
display: flex;
justify-content: space-between;
align-items: center;
}
nav h1 {
margin: 0;
}
nav a {
color: #fff;
text-decoration: none;
}
nav ul {
display: flex;
list-style: none;
}
nav ul li {
margin-left: 1rem;
}
/* 主内容区 */
main {
padding: 2rem 0;
}
/* 文章 */
.post {
background-color: #fff;
margin-bottom: 2rem;
padding: 1.5rem;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.post h2 {
margin-bottom: 0.5rem;
}
.post h2 a {
color: #333;
text-decoration: none;
}
.post-info {
color: #777;
margin-bottom: 1rem;
font-size: 0.9rem;
}
.post-info span {
margin-right: 1rem;
}
.post-content {
margin-bottom: 1rem;
}
.read-more {
display: inline-block;
background-color: #333;
color: #fff;
padding: 0.5rem 1rem;
text-decoration: none;
border-radius: 3px;
}
/* 表单 */
form {
background-color: #fff;
padding: 1.5rem;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.form-group {
margin-bottom: 1rem;
}
label {
display: block;
margin-bottom: 0.5rem;
}
input, textarea {
width: 100%;
padding: 0.5rem;
border: 1px solid #ddd;
border-radius: 3px;
}
textarea {
height: 200px;
}
button {
background-color: #333;
color: #fff;
padding: 0.5rem 1rem;
border: none;
border-radius: 3px;
cursor: pointer;
}
/* 提示消息 */
.alert {
padding: 1rem;
margin-bottom: 1rem;
border-radius: 3px;
}
.alert-success {
background-color: #d4edda;
color: #155724;
}
.alert-danger {
background-color: #f8d7da;
color: #721c24;
}
/* 分页 */
.pagination {
margin-top: 2rem;
text-align: center;
}
.pagination a, .pagination span {
display: inline-block;
padding: 0.5rem 1rem;
margin: 0 0.2rem;
border-radius: 3px;
}
.pagination a {
background-color: #333;
color: #fff;
text-decoration: none;
}
.pagination .current-page {
background-color: #777;
color: #fff;
}
/* 页脚 */
footer {
background-color: #333;
color: #fff;
text-align: center;
padding: 1rem 0;
margin-top: 2rem;
}
16. 今日总结
- Flask是一个轻量级、灵活的Python Web框架
- Flask的核心组件包括路由、视图函数、模板和静态文件
- Flask使用Jinja2作为模板引擎,可以将Python变量传递到HTML模板中
- Flask提供了处理表单、会话和Cookie的功能
- Flask可以轻松集成数据库,如SQLAlchemy
- Flask适合构建RESTful API
- 随着应用规模增长,可以使用蓝图组织代码
- 在生产环境中,应该使用WSGI服务器(如Gunicorn或uWSGI)部署Flask应用
17. 明日预告
明天我们将学习Django框架,这是Python中另一个流行的Web框架。与Flask不同,Django是一个全栈框架,提供了更多内置功能,如ORM、管理后台、表单处理等。我们将比较Flask和Django的异同,并学习如何使用Django构建Web应用。