什么是 Flask?
Flask 是一个轻量级的 Python Web 框架,被称为 “微框架”,因为它核心简单但可扩展性强。Flask 提供了构建 Web 应用所需的基本工具,同时允许开发者自由选择数据库、模板引擎等其他组件。
Flask 核心特点:
- 轻量级:核心简单,易于学习和使用
- 灵活性:不强制项目结构,可按需扩展
- Jinja2 模板引擎:强大的模板系统
- 内置开发服务器:快速测试和调试
- RESTful 请求处理:优雅处理 HTTP 请求
- WSGI 兼容:可部署在各种生产服务器
安装 Flask
# 创建虚拟环境(推荐)
python -m venv venv
# 激活虚拟环境
# Windows:
venv\Scripts\activate
# macOS/Linux:
source venv/bin/activate
# 安装 Flask
pip install flask
第一个 Flask 应用
创建 app.py
文件:
from flask import Flask
# 创建 Flask 应用实例
app = Flask(__name__)
# 定义路由和视图函数
@app.route('/')
def home():
return "欢迎来到 Flask 世界!"
@app.route('/hello/<name>')
def hello(name):
return f"你好, {name}!"
# 运行应用
if __name__ == '__main__':
app.run(debug=True)
运行应用:
python app.py
访问:
核心概念
1. 路由系统
@app.route('/')
def index():
return '首页'
@app.route('/user/<username>')
def show_user(username):
return f'用户: {username}'
@app.route('/post/<int:post_id>')
def show_post(post_id):
return f'文章 #{post_id}'
@app.route('/path/<path:subpath>')
def show_subpath(subpath):
return f'子路径: {subpath}'
路由变量类型:
string
: 默认类型,接受任何不包含斜杠的文本int
: 接受正整数float
: 接受正浮点数path
: 类似 string,但接受斜杠uuid
: 接受 UUID 字符串
2. 请求处理
from flask import request
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
# 获取表单数据
username = request.form['username']
password = request.form['password']
# 处理登录逻辑
return f'用户 {username} 已登录'
else:
# 显示登录表单
return '''
<form method="post">
用户名: <input type="text" name="username"><br>
密码: <input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
'''
常用请求对象属性:
request.method
: HTTP 方法(GET, POST等)request.form
: 表单数据request.args
: URL 查询参数request.files
: 上传的文件request.cookies
: cookiesrequest.headers
: 请求头request.json
: JSON 数据(Content-Type为application/json时)
3. 响应处理
from flask import make_response, redirect, url_for, jsonify
@app.route('/custom-response')
def custom_response():
# 创建自定义响应
response = make_response("自定义响应", 201)
response.headers['X-Custom-Header'] = 'Flask'
return response
@app.route('/redirect')
def redirect_example():
# 重定向到其他路由
return redirect(url_for('home'))
@app.route('/json')
def json_example():
# 返回JSON数据
return jsonify({'name': 'Alice', 'email': 'alice@example.com'})
@app.route('/set-cookie')
def set_cookie():
response = make_response("Cookie已设置")
response.set_cookie('theme', 'dark', max_age=60*60*24) # 24小时
return response
4. 模板渲染(Jinja2)
创建 templates
目录,添加 index.html
:
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
<h1>欢迎, {{ user.username }}!</h1>
{% if user.is_admin %}
<p>您有管理员权限</p>
{% else %}
<p>您是普通用户</p>
{% endif %}
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
</body>
</html>
在 Flask 中使用模板:
from flask import render_template
@app.route('/dashboard')
def dashboard():
user = {'username': '张三', 'is_admin': True}
items = ['苹果', '香蕉', '橙子']
return render_template('index.html',
title='控制面板',
user=user,
items=items)
5. 静态文件
创建 static
目录,存放 CSS、JS 和图片文件。
<!-- 在模板中引用静态文件 -->
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<script src="{{ url_for('static', filename='app.js') }}"></script>
<img src="{{ url_for('static', filename='logo.png') }}">
6. 错误处理
@app.errorhandler(404)
def page_not_found(error):
return render_template('404.html'), 404
@app.errorhandler(500)
def internal_error(error):
return render_template('500.html'), 500
创建 templates/404.html
:
<!DOCTYPE html>
<html>
<head>
<title>页面不存在</title>
</head>
<body>
<h1>404 - 页面未找到</h1>
<p>您访问的页面不存在,请检查URL是否正确</p>
<a href="{{ url_for('home') }}">返回首页</a>
</body>
</html>
数据库集成(SQLite + SQLAlchemy)
安装依赖
pip install flask-sqlalchemy
配置数据库
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
db = SQLAlchemy(app)
# 定义数据模型
class User(db.Model):
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)
def __repr__(self):
return f"User('{self.username}', '{self.email}')"
# 创建数据库
with app.app_context():
db.create_all()
数据库操作
@app.route('/add-user')
def add_user():
# 创建新用户
new_user = User(username='李四', email='lisi@example.com')
db.session.add(new_user)
db.session.commit()
return '用户已添加'
@app.route('/users')
def list_users():
# 查询所有用户
users = User.query.all()
return render_template('users.html', users=users)
@app.route('/user/<int:user_id>')
def show_user(user_id):
# 查询单个用户
user = User.query.get_or_404(user_id)
return render_template('user.html', user=user)
@app.route('/delete-user/<int:user_id>')
def delete_user(user_id):
# 删除用户
user = User.query.get_or_404(user_id)
db.session.delete(user)
db.session.commit()
return redirect(url_for('list_users'))
表单处理
安装依赖
pip install flask-wtf
创建表单类
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Length, Email
class RegistrationForm(FlaskForm):
username = StringField('用户名',
validators=[DataRequired(), Length(min=2, max=20)])
email = StringField('邮箱',
validators=[DataRequired(), Email()])
password = PasswordField('密码',
validators=[DataRequired()])
submit = SubmitField('注册')
在视图函数中使用表单
from flask import flash
@app.route('/register', methods=['GET', 'POST'])
def register():
form = RegistrationForm()
if form.validate_on_submit():
# 创建新用户
user = User(username=form.username.data, email=form.email.data)
db.session.add(user)
db.session.commit()
flash('账号创建成功!', 'success')
return redirect(url_for('home'))
return render_template('register.html', title='注册', form=form)
注册模板 (register.html
)
{% extends "layout.html" %}
{% block content %}
<h1>注册账号</h1>
<form method="POST" action="">
{{ form.hidden_tag() }}
<div>
{{ form.username.label }}
{{ form.username(class="form-control") }}
{% if form.username.errors %}
<div class="error">
{% for error in form.username.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% endif %}
</div>
<div>
{{ form.email.label }}
{{ form.email(class="form-control") }}
{% if form.email.errors %}
<div class="error">
{% for error in form.email.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% endif %}
</div>
<div>
{{ form.password.label }}
{{ form.password(class="form-control") }}
</div>
<div>
{{ form.submit(class="btn") }}
</div>
</form>
{% endblock %}
用户会话管理
from flask import session, redirect, url_for
@app.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
if user and user.password == form.password.data:
# 设置会话
session['user_id'] = user.id
flash('登录成功!', 'success')
return redirect(url_for('dashboard'))
else:
flash('登录失败,请检查邮箱和密码', 'danger')
return render_template('login.html', form=form)
@app.route('/dashboard')
def dashboard():
if 'user_id' not in session:
return redirect(url_for('login'))
user = User.query.get(session['user_id'])
return render_template('dashboard.html', user=user)
@app.route('/logout')
def logout():
# 清除会话
session.pop('user_id', None)
flash('您已成功登出', 'info')
return redirect(url_for('home'))
项目结构
典型的 Flask 项目结构:
my_flask_app/
├── venv/ # 虚拟环境
├── app/
│ ├── templates/ # HTML模板
│ │ ├── layout.html
│ │ ├── index.html
│ │ ├── register.html
│ │ └── ...
│ ├── static/ # 静态文件
│ │ ├── css/
│ │ ├── js/
│ │ └── images/
│ ├── models.py # 数据模型
│ ├── forms.py # 表单类
│ ├── routes.py # 路由和视图函数
│ └── __init__.py # 应用工厂
├── migrations/ # 数据库迁移(如果使用Flask-Migrate)
├── config.py # 配置文件
├── requirements.txt # 依赖列表
└── run.py # 启动脚本
完整示例:待办事项应用
文件结构
todo_app/
├── app/
│ ├── __init__.py
│ ├── models.py
│ ├── routes.py
│ ├── templates/
│ │ ├── base.html
│ │ ├── index.html
│ │ └── edit_task.html
│ └── static/
│ └── style.css
├── config.py
└── run.py
代码实现
config.py
import os
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY') or 'your-secret-key'
SQLALCHEMY_DATABASE_URI = 'sqlite:///todos.db'
SQLALCHEMY_TRACK_MODIFICATIONS = False
app/init.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from config import Config
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
app.config.from_object(Config)
db.init_app(app)
from app import routes
app.register_blueprint(routes.bp)
return app
app/models.py
from app import db
class Task(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
description = db.Column(db.Text)
completed = db.Column(db.Boolean, default=False)
def __repr__(self):
return f'<Task {self.title}>'
app/routes.py
from flask import Blueprint, render_template, redirect, url_for, request, flash
from app import db
from app.models import Task
bp = Blueprint('main', __name__)
@bp.route('/')
def index():
tasks = Task.query.all()
return render_template('index.html', tasks=tasks)
@bp.route('/add', methods=['POST'])
def add_task():
title = request.form['title']
description = request.form.get('description', '')
if not title:
flash('任务标题不能为空', 'danger')
return redirect(url_for('main.index'))
new_task = Task(title=title, description=description)
db.session.add(new_task)
db.session.commit()
flash('任务已添加', 'success')
return redirect(url_for('main.index'))
@bp.route('/complete/<int:task_id>')
def complete_task(task_id):
task = Task.query.get_or_404(task_id)
task.completed = not task.completed
db.session.commit()
flash('任务状态已更新', 'info')
return redirect(url_for('main.index'))
@bp.route('/edit/<int:task_id>', methods=['GET', 'POST'])
def edit_task(task_id):
task = Task.query.get_or_404(task_id)
if request.method == 'POST':
task.title = request.form['title']
task.description = request.form['description']
db.session.commit()
flash('任务已更新', 'success')
return redirect(url_for('main.index'))
return render_template('edit_task.html', task=task)
@bp.route('/delete/<int:task_id>')
def delete_task(task_id):
task = Task.query.get_or_404(task_id)
db.session.delete(task)
db.session.commit()
flash('任务已删除', 'warning')
return redirect(url_for('main.index'))
templates/base.html
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>待办事项</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
<div class="container">
<header>
<h1>待办事项清单</h1>
</header>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<div class="flash-messages">
{% for category, message in messages %}
<div class="flash {{ category }}">{{ message }}</div>
{% endfor %}
</div>
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
</div>
</body>
</html>
templates/index.html
```html
{% extends “base.html” %}
{% block content %}
文章版权声明:除非注明,否则均为柳三千运维录原创文章,转载或复制请以超链接形式并注明出处。