Flask
Flask 是一个轻量级的 Python Web 框架,被称为"微框架",因为它保持核心简单但可扩展。
🚀 学习路线图 (Roadmap)
不要试图一口气学完所有东西,按以下步骤循序渐进:
- 基础准备 (Prerequisites)
- Python 基础: 必须熟悉函数、类(Class)、模块导入、装饰器(Decorator,Flask 路由的核心)。
- HTTP 基础: 了解什么是 GET/POST 请求,什么是状态码(200, 404, 500)。
- 前端基础: 至少能看懂简单的 HTML 和 CSS。
- Flask 入门 (The Basics)
- Hello World: 安装 Flask,运行第一个“Hello World”页面。
- 路由 (Routing): 学习 @app.route,如何处理动态 URL(如 /user/)。
- 模板引擎 (Jinja2): 学习如何把 Python 变量传给 HTML,使用 和 {% if/for %} 循环。
- 请求与响应: 使用 request 对象获取表单数据,使用 jsonify 返回 JSON 数据。
- 进阶核心 (Core Skills)
- 表单处理: 学习 Flask-WTF 扩展,进行表单验证(必填、邮箱格式等)。
- 数据库操作: 学习 Flask-SQLAlchemy(ORM)。不要直接写 SQL 语句,学会定义模型(Model)和进行 CRUD(增删改查)操作。
- 用户认证: 学习 Flask-Login,实现注册、登录、登出、记住我功能。
- 架构与部署 (Architecture & Deployment)
- 项目结构: 当代码超过 100 行时,学习使用 Blueprints(蓝图) 将项目拆分为多个文件/模块。
- 配置管理: 区分开发环境(Development)和生产环境(Production)的配置。
- 部署: 学习使用 Gunicorn + Nginx 在 Linux 服务器上部署,或者使用 Docker。
📦 快速开始
安装 Flask
bash
# 创建虚拟环境
python -m venv venv
# 激活虚拟环境
source venv/bin/activate # macOS/Linux
# 或
venv\Scripts\activate # Windows
# 安装 Flask
pip install flaskHello World
python
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello, World!'
if __name__ == '__main__':
app.run(debug=True)运行应用:
bash
python app.py
# 访问 http://127.0.0.1:5000🛣️ 路由 (Routing)
基本路由
python
@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}'
@app.route('/path/<path:subpath>')
def show_subpath(subpath):
return f'子路径: {subpath}'HTTP 方法
python
from flask import request
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
return '登录处理'
return '登录页面'
@app.route('/api/data', methods=['GET', 'POST', 'PUT', 'DELETE'])
def api_data():
if request.method == 'GET':
return jsonify({'message': 'GET 请求'})
elif request.method == 'POST':
return jsonify({'message': 'POST 请求'})
# ...URL 构建
python
from flask import url_for, redirect
@app.route('/')
def index():
return f'首页URL: {url_for("index")}'
@app.route('/user/<name>')
def user(name):
return f'用户: {name}'
@app.route('/redirect-example')
def redirect_example():
return redirect(url_for('user', name='张三'))📄 模板引擎 (Jinja2)
基本语法
html
<!-- 变量 -->
<p>欢迎, {{ username }}!</p>
<!-- 条件语句 -->
{% if user %}
<p>欢迎, {{ user.name }}!</p>
{% else %}
<p>请先登录</p>
{% endif %}
<!-- 循环 -->
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
<!-- 过滤器 -->
{{ name|upper }}
{{ price|round(2) }}
{{ content|safe }} <!-- 渲染 HTML -->在 Flask 中使用模板
python
from flask import render_template
@app.route('/')
def index():
return render_template('index.html')
@app.route('/user/<name>')
def user(name):
return render_template('user.html', name=name, items=['苹果', '香蕉', '橙子'])模板继承
base.html (基模板):
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>
<footer>
{% block footer %}{% endblock %}
</footer>
</body>
</html>index.html (子模板):
html
{% extends "base.html" %}
{% block title %}首页{% endblock %}
{% block content %}
<h1>欢迎来到我的网站</h1>
<p>这是首页内容</p>
{% endblock %}📝 请求与响应
获取请求数据
python
from flask import Flask, request, jsonify
@app.route('/form', methods=['POST'])
def form():
# 获取表单数据
username = request.form.get('username')
password = request.form.get('password')
# 获取 JSON 数据
# data = request.get_json()
# 获取查询参数
# page = request.args.get('page', default=1, type=int)
return f'用户名: {username}'
@app.route('/api', methods=['POST'])
def api():
data = request.get_json()
return jsonify({
'status': 'success',
'data': data
})Cookie 和 Session
python
from flask import session, make_response
# 设置 secret_key 用于 session 加密
app.secret_key = 'your-secret-key'
@app.route('/set-cookie')
def set_cookie():
resp = make_response('设置 Cookie')
resp.set_cookie('username', '张三')
return resp
@app.route('/get-cookie')
def get_cookie():
username = request.cookies.get('username')
return f'Cookie 中的用户名: {username}'
@app.route('/set-session')
def set_session():
session['user_id'] = 123
session['username'] = '张三'
return 'Session 已设置'
@app.route('/get-session')
def get_session():
user_id = session.get('user_id')
username = session.get('username')
return f'Session: {user_id}, {username}'
@app.route('/logout')
def logout():
session.clear()
return '已登出'📋 表单处理 (Flask-WTF)
安装
bash
pip install flask-wtf email_validator创建表单类
python
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Email, Length
class LoginForm(FlaskForm):
username = StringField('用户名', validators=[DataRequired()])
password = PasswordField('密码', validators=[DataRequired(), Length(min=6)])
email = StringField('邮箱', validators=[DataRequired(), Email()])
submit = SubmitField('提交')
class RegisterForm(FlaskForm):
username = StringField('用户名', validators=[DataRequired(), Length(3, 20)])
email = StringField('邮箱', validators=[DataRequired(), Email()])
password = PasswordField('密码', validators=[DataRequired(), Length(min=6)])
confirm_password = PasswordField('确认密码', validators=[DataRequired()])
submit = SubmitField('注册')
def validate_confirm_password(self, field):
if field.data != self.password.data:
raise ValueError('两次输入的密码不一致')在路由中使用表单
python
@app.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
username = form.username.data
password = form.password.data
# 验证逻辑
return f'登录成功: {username}'
return render_template('login.html', form=form)模板中渲染表单
html
<form method="POST">
{{ form.hidden_tag() }}
<p>
{{ form.username.label }}<br>
{{ form.username(size=20) }}
{% if form.username.errors %}
<span style="color: red;">{{ form.username.errors[0] }}</span>
{% endif %}
</p>
<p>
{{ form.password.label }}<br>
{{ form.password() }}
</p>
<p>{{ form.submit() }}</p>
</form>🗄️ 数据库操作 (Flask-SQLAlchemy)
安装
bash
pip install flask-sqlalchemy配置数据库
python
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
# SQLite 配置(开发环境)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# MySQL 配置(生产环境)
# app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://user:password@localhost/db_name'
db = SQLAlchemy(app)定义模型
python
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password = db.Column(db.String(60), nullable=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
# 定义关系
posts = db.relationship('Post', backref='author', lazy=True)
def __repr__(self):
return f"User('{self.username}', '{self.email}')"
class Post(db.Model):
__tablename__ = 'posts'
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, default=datetime.utcnow)
# 外键
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
def __repr__(self):
return f"Post('{self.title}', '{self.date_posted}')"创建数据库表
python
# 在 Python shell 中
with app.app_context():
db.create_all()CRUD 操作
python
# 创建
user = User(username='张三', email='zhang@example.com', password='hashed_password')
db.session.add(user)
db.session.commit()
# 读取
# 获取所有用户
users = User.query.all()
# 获取单个用户
user = User.query.get(1)
user = User.query.filter_by(username='张三').first()
user = User.query.filter(User.email.endswith('@example.com')).all()
# 更新
user = User.query.get(1)
user.username = '李四'
db.session.commit()
# 删除
user = User.query.get(1)
db.session.delete(user)
db.session.commit()
# 计数
count = User.query.count()
# 排序
users = User.query.order_by(User.created_at.desc()).all()
# 分页
page = request.args.get('page', 1, type=int)
users = User.query.paginate(page=page, per_page=10)🔐 用户认证 (Flask-Login)
安装
bash
pip install flask-login
pip install bcrypt # 密码加密配置
python
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
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))UserMixin
python
class User(db.Model, UserMixin):
# ... 其他字段 ...
def set_password(self, password):
self.password = bcrypt.generate_password_hash(password).decode('utf-8')
def check_password(self, password):
return bcrypt.check_password_hash(self.password, password)登录/登出
python
@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, remember=form.remember.data)
next_page = request.args.get('next')
return redirect(next_page) if next_page else redirect(url_for('index'))
flash('登录失败,请检查用户名和密码', 'danger')
return render_template('login.html', title='登录', form=form)
@app.route('/logout')
@login_required
def logout():
logout_user()
return redirect(url_for('index'))
@app.route('/register', methods=['GET', 'POST'])
def register():
form = RegisterForm()
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', title='注册', form=form)
@app.route('/account')
@login_required
def account():
return render_template('account.html', title='账户')在模板中判断登录状态
html
{% if current_user.is_authenticated %}
<p>欢迎, {{ current_user.username }}!</p>
<a href="{{ url_for('logout') }}">登出</a>
{% else %}
<a href="{{ url_for('login') }}">登录</a>
<a href="{{ url_for('register') }}">注册</a>
{% endif %}🧩 蓝图 (Blueprints)
创建蓝图
python
# auth.py
from flask import Blueprint, render_template, redirect, url_for
auth = Blueprint('auth', __name__)
@auth.route('/login')
def login():
return '登录页面'
@auth.route('/register')
def register():
return '注册页面'
@auth.route('/logout')
def logout():
return '登出'python
# main.py
from flask import Blueprint
main = Blueprint('main', __name__)
@main.route('/')
def index():
return '首页'
@main.route('/about')
def about():
return '关于我们'注册蓝图
python
# app.py
from flask import Flask
from auth import auth as auth_blueprint
from main import main as main_blueprint
app = Flask(__name__)
# 注册蓝图,指定 URL 前缀
app.register_blueprint(auth_blueprint, url_prefix='/auth')
app.register_blueprint(main_blueprint)
# 访问路径:
# /auth/login
# /auth/register
# /
# /about项目结构
myproject/
├── app/
│ ├── __init__.py # Flask 应用工厂
│ ├── auth/ # 认证蓝图
│ │ ├── __init__.py
│ │ ├── routes.py
│ │ ├── forms.py
│ │ └── templates/
│ ├── main/ # 主蓝图
│ │ ├── __init__.py
│ │ ├── routes.py
│ │ └── templates/
│ ├── static/ # 静态文件
│ └── templates/ # 共享模板
├── config.py # 配置文件
├── requirements.txt
└── run.py # 启动文件应用工厂模式
python
# config.py
class Config:
SECRET_KEY = 'your-secret-key'
SQLALCHEMY_DATABASE_URI = 'sqlite:///site.db'
class DevelopmentConfig(Config):
DEBUG = True
class ProductionConfig(Config):
DEBUG = False
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://user:password@localhost/db_name'
config = {
'development': DevelopmentConfig,
'production': ProductionConfig
}python
# app/__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
db = SQLAlchemy()
login_manager = LoginManager()
def create_app(config_name='development'):
app = Flask(__name__)
app.config.from_object(config[config_name])
db.init_app(app)
login_manager.init_app(app)
from app.main import main as main_blueprint
from app.auth import auth as auth_blueprint
app.register_blueprint(main_blueprint)
app.register_blueprint(auth_blueprint, url_prefix='/auth')
return apppython
# run.py
from app import create_app
app = create_app('development')
if __name__ == '__main__':
app.run(debug=True)🚀 部署
Gunicorn + Nginx
bash
# 安装 Gunicorn
pip install gunicorn
# 运行 Gunicorn
gunicorn -w 4 -b 127.0.0.1:5000 run:appNginx 配置示例:
nginx
server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /static {
alias /path/to/your/app/static;
}
}Docker 部署
dockerfile
# Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:5000", "run:app"]yaml
# docker-compose.yml
version: '3.8'
services:
web:
build: .
ports:
- "5000:5000"
environment:
- FLASK_ENV=production
volumes:
- ./data:/app/data
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: flask_db📚 推荐教程资源 (Resources)
- 视频教程 (首选)
- Corey Schafer (YouTube/Bilibili搬运):
- 评价: 神级教程。这是公认的最好的 Flask 视频教程。
- 内容: 从零开始构建一个完整的博客系统,涵盖了数据库、用户系统、密码加密等所有核心内容。
- 搜索关键词: "Corey Schafer Flask Tutorial" (B站有中文字幕版)。
- 李辉 (Grey Li) 的相关分享:
- 他是 Flask 核心维护团队成员(中国人),他的分享非常权威。
- 文字教程 & 书籍
- 官方文档 (The Flask Documentation):
- 评价: Flask 的官方文档写得非常通俗易懂,包含一个 "Tutorial" 章节,手把手教你写一个 Flaskr 博客。
- 地址: flask.palletsprojects.com
- The Flask Mega-Tutorial (作者: Miguel Grinberg):
- 评价: 这是一个非常经典的系列博客,后来集结成书。它非常详细,适合想深入了解的人。
- 中文版: 网上有很多翻译版本,搜索 "Flask Mega-Tutorial 中文"。
- 书籍推荐:
- 《Flask Web 开发:基于 Python 的 Web 应用开发实战》(作者也是 Miguel Grinberg,俗称“狗书”)。