【Python Web】一文搞懂Flask框架:从入门到实战的完整指南

【Python Web】一文搞懂Flask框架:从入门到实战的完整指南

文章目录

【Python Web】一文搞懂Flask框架:从入门到实战的完整指南

1. 引言

Flask是Python Web开发领域最受欢迎的微框架之一,以其轻量、灵活和易于扩展的特性赢得了众多开发者的青睐。无论是构建简单的API服务,还是开发功能完备的Web应用,Flask都能提供优雅而高效的解决方案。本文将全面介绍Flask框架的核心概念、基本用法和实战技巧,帮助读者快速掌握这一强大的Web开发工具。

无论你是Web开发新手,还是想从其他框架迁移到Flask,这篇指南都将为你提供系统化的学习路径,帮助你构建专业、高效且安全的Python Web应用。

2. Flask 简介

2.1 什么是Flask

Flask是一个轻量级的Python Web应用框架,由Armin Ronacher设计开发,基于Werkzeug WSGI工具包和Jinja2模板引擎。Flask被称为"微框架",因为它保持核心简单但可扩展,不强制依赖特定的库或工具,给予开发者极大的灵活性和控制力。

Flask的主要特点包括:

  • 轻量且高效:核心简洁,启动迅速,资源占用低
  • 灵活性:不强制特定项目结构或组件选择
  • 易于学习:API设计直观,学习曲线平缓
  • 可扩展性:通过丰富的扩展生态系统增强功能
  • 强大的路由系统:支持URL变量和HTTP方法
  • 内置开发服务器:便于本地测试和开发
  • RESTful支持:轻松构建符合REST规范的API

2.2 Flask vs. Django

Flask和Django是Python Web开发中最流行的两个框架,它们各有优势:

特性FlaskDjango
架构理念微框架,灵活定制全能框架,内置齐全
学习曲线较低,容易上手较高,概念较多
项目规模适合小到中型项目适合中到大型项目
自由度高,可自由选择组件低,遵循"Django方式"
数据库支持通过扩展支持ORM内置
管理后台需要自行实现或使用扩展内置强大的管理后台

2.3 安装Flask

使用pip安装Flask非常简单:

pip install flask 

建议在虚拟环境中安装Flask,以避免依赖冲突:

# 创建虚拟环境 python -m venv venv # 激活虚拟环境(Windows) venv\Scripts\activate # 激活虚拟环境(Linux/Mac) source venv/bin/activate # 安装Flask pip install flask 

验证安装:

python -c "import flask; print(flask.__version__)"

3. Flask 基础知识

3.1 第一个Flask应用

创建一个最简单的Flask应用只需几行代码:

from flask import Flask # 创建Flask应用实例 app = Flask(__name__)# 定义路由和视图函数@app.route('/')defhello_world():return'Hello, World!'# 启动应用if __name__ =='__main__': app.run(debug=True)

将上述代码保存为app.py并运行:

python app.py 

打开浏览器访问http://127.0.0.1:5000/即可看到"Hello, World!"消息。

3.2 应用实例

Flask应用的核心是Flask类的实例,通常命名为app

app = Flask(__name__)

参数__name__是Python的特殊变量,它会传递当前模块的名称给Flask。这有助于Flask找到资源文件的位置。

3.3 路由系统

路由是将URL映射到视图函数的机制。Flask使用装饰器来定义路由:

@app.route('/user/<username>')defshow_user_profile(username):returnf'User {username}'
3.3.1 URL变量

Flask支持在URL中包含变量,类型可以是:

  • 字符串(默认):<username>
  • 整数:<int:post_id>
  • 浮点数:<float:score>
  • 路径:<path:subpath>
  • UUID:<uuid:id>

示例:

@app.route('/post/<int:post_id>')defshow_post(post_id):returnf'Post {post_id}'
3.3.2 HTTP方法

路由可以限定接受的HTTP方法:

@app.route('/login', methods=['GET','POST'])deflogin():if request.method =='POST':# 处理表单提交return'处理登录'else:# 显示登录表单return'显示登录表单'

3.4 视图函数

视图函数是处理请求并返回响应的Python函数。视图函数可以返回:

  • 字符串:直接显示为HTML
  • HTML模板渲染结果
  • JSON响应
  • 重定向
  • 自定义响应对象

示例:

from flask import render_template, jsonify, redirect, url_for @app.route('/template')deftemplate_example():return render_template('example.html', name='Flask')@app.route('/api/data')defapi_data():return jsonify({"name":"Flask","type":"framework"})@app.route('/redirect')defredirect_example():return redirect(url_for('hello_world'))

3.5 请求对象

Flask通过request对象提供对客户端请求数据的访问:

from flask import request @app.route('/submit', methods=['POST'])defsubmit():# 获取表单数据 username = request.form.get('username')# 获取URL参数 page = request.args.get('page',1,type=int)# 获取JSON数据 data = request.get_json()# 获取文件file= request.files.get('upload')returnf'Received: {username}'

3.6 响应对象

视图函数可以返回一个元组来设置响应的状态码和头信息:

@app.route('/response')defcustom_response():return'Custom response',201,{'X-Custom-Header':'value'}

也可以使用make_response函数创建自定义响应:

from flask import make_response @app.route('/cookie')defset_cookie(): resp = make_response('Cookie设置成功') resp.set_cookie('username','flask_user')return resp 

4. 模板系统

Flask使用Jinja2作为默认的模板引擎,它功能强大且易于使用。

4.1 Jinja2模板基础

Jinja2模板是包含静态内容和动态内容占位符的文件。默认情况下,Flask在应用的templates目录中查找模板。

一个基本的HTML模板示例(templates/index.html):

<!DOCTYPEhtml><html><head><title>{{ title }}</title></head><body><h1>Hello, {{ name }}!</h1> {% if messages %} <ul> {% for message in messages %} <li>{{ message }}</li> {% endfor %} </ul> {% else %} <p>No messages.</p> {% endif %} </body></html>

在视图中渲染该模板:

@app.route('/')defindex():return render_template('index.html', title='Flask Template', name='User', messages=['Message 1','Message 2'])

4.2 模板语法

Jinja2模板支持三种主要的语法结构:

  1. 变量{{ variable }}
  2. 控制结构{% if condition %} ... {% endif %}
  3. 注释{# This is a comment #}
4.2.1 变量与过滤器

变量可以通过过滤器进行转换:

{{ name|capitalize }} {{ text|truncate(100) }} {{ data|tojson }} 

常用的过滤器:

  • safe: 标记内容为安全,不进行转义
  • escape: HTML转义
  • capitalize: 首字母大写
  • lower/upper: 转换大小写
  • trim: 去除首尾空白
  • striptags: 移除HTML标签
  • default: 提供默认值
4.2.2 控制结构

条件语句:

{% if user.is_authenticated %} <ahref="{{ url_for('logout') }}">Logout</a> {% else %} <ahref="{{ url_for('login') }}">Login</a> {% endif %} 

循环:

<ul> {% for item in items %} <li>{{ loop.index }} - {{ item.name }}</li> {% else %} <li>No items found.</li> {% endfor %} </ul>

4.3 模板继承

Jinja2支持模板继承,这是一种强大的组织模板的方式。

基础模板(base.html):

<!DOCTYPEhtml><html><head><title>{% block title %}Default Title{% endblock %}</title><linkrel="stylesheet"href="{{ url_for('static', filename='style.css') }}"> {% block styles %}{% endblock %} </head><body><header><nav>{% block nav %}{% endblock %}</nav></header><main> {% block content %}{% endblock %} </main><footer> {% block footer %}© 2023 Flask应用{% endblock %} </footer> {% block scripts %}{% endblock %} </body></html>

子模板(page.html):

{% extends "base.html" %} {% block title %}页面标题{% endblock %} {% block content %} <h1>页面内容</h1><p>这是页面的具体内容。</p> {% endblock %} 

4.4 静态文件

Flask自动为静态文件添加路由。默认情况下,静态文件应放在应用的static目录中。

在模板中引用静态文件:

<linkrel="stylesheet"href="{{ url_for('static', filename='css/style.css') }}"><imgsrc="{{ url_for('static', filename='images/logo.png') }}"><scriptsrc="{{ url_for('static', filename='js/script.js') }}"></script>

4.5 URL生成

使用url_for()函数动态生成URL,避免硬编码:

<ahref="{{ url_for('index') }}">首页</a><ahref="{{ url_for('user_profile', username='john') }}">用户资料</a><ahref="{{ url_for('static', filename='style.css') }}">样式表</a>

5. 表单处理

Web应用几乎都需要处理用户输入的表单数据。Flask提供了多种方式处理表单提交。

5.1 基本表单处理

最简单的表单处理方式是直接使用Flask的request对象:

from flask import request, redirect, url_for, render_template @app.route('/login', methods=['GET','POST'])deflogin():if request.method =='POST': username = request.form.get('username') password = request.form.get('password')# 验证用户名和密码if username =='admin'and password =='secret':return redirect(url_for('dashboard'))else: error ='无效的用户名或密码'return render_template('login.html', error=error)# GET请求显示表单return render_template('login.html')

对应的模板(login.html):

<!DOCTYPEhtml><html><head><title>登录</title></head><body><h1>登录</h1> {% if error %} <pstyle="color: red;">{{ error }}</p> {% endif %} <formmethod="post"><div><label>用户名:</label><inputtype="text"name="username"required></div><div><label>密码:</label><inputtype="password"name="password"required></div><buttontype="submit">登录</button></form></body></html>

5.2 使用Flask-WTF扩展

对于复杂表单,推荐使用Flask-WTF扩展,它结合了WTForms库,提供了表单验证、CSRF保护等功能。

安装Flask-WTF:

pip install flask-wtf 

配置应用:

app = Flask(__name__) app.config['SECRET_KEY']='your-secret-key'# 用于CSRF保护

定义表单类:

from flask_wtf import FlaskForm from wtforms import StringField, PasswordField, SubmitField from wtforms.validators import DataRequired, Email, Length classLoginForm(FlaskForm): email = StringField('邮箱', validators=[DataRequired(), Email()]) password = PasswordField('密码', validators=[DataRequired(), Length(min=6)]) submit = SubmitField('登录')

在视图中使用表单:

@app.route('/login', methods=['GET','POST'])deflogin(): form = LoginForm()if form.validate_on_submit():# 表单验证通过 email = form.email.data password = form.password.data # 处理登录逻辑return redirect(url_for('dashboard'))return render_template('login_wtf.html', form=form)

带有WTForms的模板(login_wtf.html):

<!DOCTYPEhtml><html><head><title>登录</title></head><body><h1>登录</h1><formmethod="post"> {{ form.hidden_tag() }} <div> {{ form.email.label }} {{ form.email }} {% if form.email.errors %} <ul> {% for error in form.email.errors %} <li>{{ error }}</li> {% endfor %} </ul> {% endif %} </div><div> {{ form.password.label }} {{ form.password }} {% if form.password.errors %} <ul> {% for error in form.password.errors %} <li>{{ error }}</li> {% endfor %} </ul> {% endif %} </div> {{ form.submit }} </form></body></html>

5.3 文件上传

处理文件上传需要在表单中添加enctype="multipart/form-data"属性:

<formmethod="post"enctype="multipart/form-data"><inputtype="file"name="file"><buttontype="submit">上传</button></form>

在Flask中处理上传文件:

from werkzeug.utils import secure_filename import os UPLOAD_FOLDER ='uploads' ALLOWED_EXTENSIONS ={'txt','pdf','png','jpg','jpeg','gif'} app.config['UPLOAD_FOLDER']= UPLOAD_FOLDER defallowed_file(filename):return'.'in filename and \ filename.rsplit('.',1)[1].lower()in ALLOWED_EXTENSIONS @app.route('/upload', methods=['GET','POST'])defupload_file():if request.method =='POST':# 检查是否有文件部分if'file'notin request.files:return'没有文件部分'file= request.files['file']# 如果用户未选择文件,浏览器会提交一个没有文件名的空部分iffile.filename =='':return'未选择文件'iffileand allowed_file(file.filename): filename = secure_filename(file.filename)file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))returnf'文件 {filename} 上传成功'return''' <!doctype html> <title>上传文件</title> <h1>上传文件</h1> <form method="post" enctype="multipart/form-data"> <input type="file" name="file"> <input type="submit" value="上传"> </form> '''

5.4 表单验证

WTForms提供了丰富的验证器:

  • DataRequired:字段不能为空
  • Email:必须是有效的电子邮件地址
  • Length:字符串长度限制
  • NumberRange:数值范围限制
  • EqualTo:字段必须与另一个字段值相等(如密码确认)
  • URL:必须是有效的URL
  • Regexp:必须匹配正则表达式

自定义验证示例:

from wtforms import ValidationError classRegistrationForm(FlaskForm): username = StringField('用户名', validators=[DataRequired(), Length(min=4,max=20)]) email = StringField('邮箱', validators=[DataRequired(), Email()]) password = PasswordField('密码', validators=[DataRequired(), Length(min=6)]) confirm_password = PasswordField('确认密码', validators=[DataRequired(), EqualTo('password')]) submit = SubmitField('注册')defvalidate_username(self, username):# 检查用户名是否已存在if username.data =='admin':raise ValidationError('该用户名已被使用,请选择其他用户名。')

6. 数据库集成

Flask本身不包含数据库抽象层,但可以与各种数据库解决方案集成。最常用的是SQLAlchemy ORM通过Flask-SQLAlchemy扩展。

6.1 Flask-SQLAlchemy基础

Flask-SQLAlchemy是一个为Flask应用提供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' app.config['SQLALCHEMY_TRACK_MODIFICATIONS']=False db = SQLAlchemy(app)

数据库URL格式因数据库类型而异:

  • SQLite: sqlite:///site.db
  • MySQL: mysql://username:password@localhost/db_name
  • PostgreSQL: postgresql://username:password@localhost/db_name

6.2 定义模型

使用SQLAlchemy定义数据库模型(表):

classUser(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) password = db.Column(db.String(60), nullable=False)# 一对多关系 posts = db.relationship('Post', backref='author', lazy=True)def__repr__(self):returnf"User('{self.username}', '{self.email}')"classPost(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) date_posted = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)# 外键 user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)def__repr__(self):returnf"Post('{self.title}', '{self.date_posted}')"

6.3 创建和迁移数据库

创建数据库表:

# 在Python交互式shell中from app import db db.create_all()

对于更复杂的迁移,可以使用Flask-Migrate扩展(基于Alembic):

pip install flask-migrate 

配置Flask-Migrate:

from flask_migrate import Migrate migrate = Migrate(app, db)

然后可以使用命令行管理迁移:

flask db init # 初始化迁移仓库 flask db migrate # 创建迁移脚本 flask db upgrade # 应用迁移到数据库 

6.4 基本CRUD操作

创建记录
@app.route('/add_user', methods=['POST'])defadd_user(): username = request.form.get('username') email = request.form.get('email') password = request.form.get('password') user = User(username=username, email=email, password=password) db.session.add(user) db.session.commit()returnf'用户 {username} 已添加'
查询记录
@app.route('/users')defget_users(): users = User.query.all()return render_template('users.html', users=users)@app.route('/user/<int:user_id>')defget_user(user_id): user = User.query.get_or_404(user_id)return render_template('user.html', user=user)

常用查询方法:

# 获取所有记录 User.query.all()# 获取指定ID的记录 User.query.get(1) User.query.get_or_404(1)# ID不存在时返回404错误# 条件查询 User.query.filter_by(username='john').first() User.query.filter(User.email.endswith('@example.com')).all()# 排序 User.query.order_by(User.username).all()# 限制结果数量 User.query.limit(10).all()# 分页 users = User.query.paginate(page=2, per_page=20)for user in users.items:print(user.username)
更新记录
@app.route('/update_user/<int:user_id>', methods=['POST'])defupdate_user(user_id): user = User.query.get_or_404(user_id) user.username = request.form.get('username', user.username) user.email = request.form.get('email', user.email) db.session.commit()returnf'用户 {user.username} 已更新'
删除记录
@app.route('/delete_user/<int:user_id>', methods=['POST'])defdelete_user(user_id): user = User.query.get_or_404(user_id) db.session.delete(user) db.session.commit()returnf'用户 {user.username} 已删除'

6.5 使用其他数据库

SQLite

开发中的默认选择,无需额外配置:

app.config['SQLALCHEMY_DATABASE_URI']='sqlite:///site.db'
MySQL

需要安装额外的依赖:

pip install mysqlclient 

配置:

app.config['SQLALCHEMY_DATABASE_URI']='mysql://username:password@localhost/db_name'
PostgreSQL

需要安装依赖:

pip install psycopg2-binary 

配置:

app.config['SQLALCHEMY_DATABASE_URI']='postgresql://username:password@localhost/db_name'
MongoDB (NoSQL)

对于MongoDB等NoSQL数据库,可以使用Flask-PyMongo或者Flask-MongoEngine扩展。

安装Flask-MongoEngine:

pip install flask-mongoengine 

配置:

from flask_mongoengine import MongoEngine app = Flask(__name__) app.config['MONGODB_SETTINGS']={'db':'your_database','host':'localhost','port':27017} db = MongoEngine(app)classUser(db.Document): email = db.StringField(required=True, unique=True) username = db.StringField(required=True, unique=True) password = db.StringField(required=True)

7. 用户认证与授权

大多数Web应用需要用户认证与授权功能。Flask通过扩展提供了丰富的认证解决方案。

7.1 Flask-Login扩展

Flask-Login提供了用户session管理、登录、登出等功能。

安装:

pip install flask-login 

配置:

from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user app = Flask(__name__) app.config['SECRET_KEY']='your-secret-key'# 初始化LoginManager login_manager = LoginManager(app) login_manager.login_view ='login'# 未登录用户重定向的视图 login_manager.login_message ='请先登录再访问此页面。'# 自定义消息# 加载用户回调函数@login_manager.user_loaderdefload_user(user_id):return User.query.get(int(user_id))

修改用户模型以支持Flask-Login:

classUser(db.Model, UserMixin):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) password = db.Column(db.String(60), nullable=False)# UserMixin提供了以下方法:# is_authenticated, is_active, is_anonymous, get_id()

登录视图:

@app.route('/login', methods=['GET','POST'])deflogin():if current_user.is_authenticated:return redirect(url_for('index')) form = LoginForm()if form.validate_on_submit(): user = User.query.filter_by(email=form.email.data).first()if user and check_password(form.password.data, user.password): login_user(user, remember=form.remember.data)# 获取登录后重定向的页面 next_page = request.args.get('next')if next_page:return redirect(next_page)else:return redirect(url_for('index'))else: flash('登录失败。请检查邮箱和密码。')return render_template('login.html', form=form)

登出视图:

@app.route('/logout')deflogout(): logout_user()return redirect(url_for('login'))

保护路由:

@app.route('/profile')@login_requireddefprofile():return render_template('profile.html')

在模板中使用当前用户:

{% if current_user.is_authenticated %} <p>当前用户: {{ current_user.username }}</p><ahref="{{ url_for('logout') }}">退出登录</a> {% else %} <ahref="{{ url_for('login') }}">登录</a><ahref="{{ url_for('register') }}">注册</a> {% endif %} 

7.2 密码哈希

不应该明文存储密码,应使用加密哈希。Flask-Bcrypt是一个优秀的密码哈希扩展:

pip install flask-bcrypt 

配置:

from flask_bcrypt import Bcrypt bcrypt = Bcrypt(app)

密码哈希与验证:

# 生成密码哈希 hashed_password = bcrypt.generate_password_hash('password').decode('utf-8')# 验证密码 valid = bcrypt.check_password_hash(hashed_password,'password')# 返回True

在用户注册中使用:

@app.route('/register', methods=['GET','POST'])defregister(): form = RegistrationForm()if form.validate_on_submit(): hashed_password = bcrypt.generate_password_hash(form.password.data).decode('utf-8') user = User(username=form.username.data, email=form.email.data, password=hashed_password) db.session.add(user) db.session.commit() flash(f'账户已创建,现在可以登录了!')return redirect(url_for('login'))return render_template('register.html', form=form)

7.3 基于角色的访问控制

对于更复杂的权限控制,可以使用Flask-Principal或实现自定义角色系统:

# 扩展User模型添加角色classUser(db.Model, UserMixin):# ...其他字段 role = db.Column(db.String(20), nullable=False, default='user')defis_admin(self):return self.role =='admin'

创建自定义装饰器控制访问权限:

from functools import wraps from flask import abort defadmin_required(f):@wraps(f)defdecorated_function(*args,**kwargs):ifnot current_user.is_authenticated ornot current_user.is_admin(): abort(403)# 返回禁止访问return f(*args,**kwargs)return decorated_function @app.route('/admin')@login_required@admin_requireddefadmin_panel():return render_template('admin/index.html')

7.4 Flask-Security扩展

对于更全面的安全解决方案,Flask-Security集成了多种安全扩展:

pip install flask-security-too 

Flask-Security提供:

  • 用户认证
  • 角色管理
  • 密码哈希
  • 基本HTTP认证
  • 令牌认证
  • 用户注册
  • 密码重置
  • 邮件确认

基本配置:

from flask_security import Security, SQLAlchemyUserDatastore # 定义安全相关的模型classRole(db.Model, RoleMixin):id= db.Column(db.Integer, primary_key=True) name = db.Column(db.String(80), unique=True) description = db.Column(db.String(255))classUser(db.Model, UserMixin):id= db.Column(db.Integer, primary_key=True) email = db.Column(db.String(255), unique=True) password = db.Column(db.String(255)) active = db.Column(db.Boolean()) confirmed_at = db.Column(db.DateTime()) roles = db.relationship('Role', secondary=roles_users, backref=db.backref('users', lazy='dynamic'))# 初始化Flask-Security user_datastore = SQLAlchemyUserDatastore(db, User, Role) security = Security(app, user_datastore)

8. REST API开发

Flask非常适合构建RESTful API。本节将介绍如何使用Flask开发API服务。

8.1 基本API端点

最简单的API可以直接使用Flask的视图函数和jsonify函数:

from flask import jsonify @app.route('/api/users')defget_users(): users = User.query.all() user_list =[]for user in users: user_data ={'id': user.id,'username': user.username,'email': user.email } user_list.append(user_data)return jsonify({'users': user_list})@app.route('/api/user/<int:user_id>')defget_user(user_id): user = User.query.get_or_404(user_id) user_data ={'id': user.id,'username': user.username,'email': user.email }return jsonify(user_data)

创建和更新资源:

@app.route('/api/users', methods=['POST'])defcreate_user():ifnot request.json ornot'username'in request.json: abort(400)# 错误请求 user = User( username=request.json['username'], email=request.json.get('email','')) db.session.add(user) db.session.commit()return jsonify({'user':{'id': user.id,'username': user.username,'email': user.email }}),201# 创建成功状态码@app.route('/api/user/<int:user_id>', methods=['PUT'])defupdate_user(user_id): user = User.query.get_or_404(user_id)ifnot request.json: abort(400) user.username = request.json.get('username', user.username) user.email = request.json.get('email', user.email) db.session.commit()return jsonify({'user':{'id': user.id,'username': user.username,'email': user.email }})

8.2 使用Flask-RESTful扩展

Flask-RESTful提供了更结构化的API开发方式:

pip install flask-restful 

基本配置:

from flask import Flask from flask_restful import Api, Resource, reqparse, fields, marshal_with app = Flask(__name__) api = Api(app)# 定义响应字段格式 user_fields ={'id': fields.Integer,'username': fields.String,'email': fields.String,'uri': fields.Url('user')# 生成资源URL}# 请求解析器 user_parser = reqparse.RequestParser() user_parser.add_argument('username',type=str, required=True,help='用户名不能为空') user_parser.add_argument('email',type=str, required=True,help='邮箱不能为空')# 用户资源classUserResource(Resource):@marshal_with(user_fields)defget(self, user_id): user = User.query.get_or_404(user_id)return user @marshal_with(user_fields)defput(self, user_id): args = user_parser.parse_args() user = User.query.get_or_404(user_id) user.username = args['username'] user.email = args['email'] db.session.commit()return user defdelete(self, user_id): user = User.query.get_or_404(user_id) db.session.delete(user) db.session.commit()return'',204# 无内容返回# 用户列表资源classUserListResource(Resource):@marshal_with(user_fields)defget(self): users = User.query.all()return users @marshal_with(user_fields)defpost(self): args = user_parser.parse_args() user = User(username=args['username'], email=args['email']) db.session.add(user) db.session.commit()return user,201# 注册API路由 api.add_resource(UserListResource,'/api/users') api.add_resource(UserResource,'/api/user/<int:user_id>', endpoint='user')

8.3 API认证

API通常需要认证机制保护接口。常见方法有:

基本认证
from flask_httpauth import HTTPBasicAuth auth = HTTPBasicAuth()@auth.verify_passworddefverify_password(username, password): user = User.query.filter_by(username=username).first()if user and bcrypt.check_password_hash(user.password, password):return user returnNoneclassProtectedResource(Resource):@auth.login_requireddefget(self):return{'message':'只有认证用户才能看到'}
令牌认证
from flask_httpauth import HTTPTokenAuth auth = HTTPTokenAuth()@auth.verify_tokendefverify_token(token):# 验证令牌并返回用户 user = User.verify_token(token)if user:return user returnNone# 在User模型中生成令牌classUser(db.Model):# ...其他字段defgenerate_token(self, expiration=3600): s = Serializer(app.config['SECRET_KEY'], expires_in=expiration)return s.dumps({'id': self.id}).decode('utf-8')@staticmethoddefverify_token(token): s = Serializer(app.config['SECRET_KEY'])try: data = s.loads(token)except:returnNonereturn User.query.get(data['id'])

8.4 API文档生成

自动生成API文档可以使用Flask-RESTPlus或Swagger-UI:

pip install flask-restplus 

基本配置:

from flask import Flask from flask_restplus import Api, Resource, fields app = Flask(__name__) api = Api(app, version='1.0', title='用户API', description='用户管理API文档')# 定义命名空间 ns = api.namespace('users', description='用户操作')# 定义模型 user_model = api.model('User',{'id': fields.Integer(readonly=True, description='用户ID'),'username': fields.String(required=True, description='用户名'),'email': fields.String(required=True, description='邮箱地址')})@ns.route('/')classUserList(Resource):@ns.doc('列出所有用户')@ns.marshal_list_with(user_model)defget(self):"""获取所有用户列表"""return User.query.all()@ns.doc('创建用户')@ns.expect(user_model)@ns.marshal_with(user_model, code=201)defpost(self):"""创建新用户""" user = User(username=api.payload['username'], email=api.payload['email']) db.session.add(user) db.session.commit()return user,[email protected]('/<int:id>')@ns.response(404,'用户未找到')@ns.param('id','用户ID')classUserAPI(Resource):@ns.doc('获取用户')@ns.marshal_with(user_model)defget(self,id):"""获取指定ID的用户"""return User.query.get_or_404(id)

访问/路径即可看到自动生成的Swagger UI文档。

8.5 API版本控制

API版本控制可以通过URL路径、请求头或查询参数实现:

URL路径版本控制
@app.route('/api/v1/users')defget_users_v1():# v1版本实现[email protected]('/api/v2/users')defget_users_v2():# v2版本实现pass
使用蓝图实现版本控制
from flask import Blueprint api_v1 = Blueprint('api_v1', __name__, url_prefix='/api/v1') api_v2 = Blueprint('api_v2', __name__, url_prefix='/api/v2')@api_v1.route('/users')defget_users_v1():# v1版本实现pass@api_v2.route('/users')defget_users_v2():# v2版本实现pass app.register_blueprint(api_v1) app.register_blueprint(api_v2)

9. 蓝图与应用结构

随着应用复杂度增加,将所有代码放在一个文件中变得不可维护。Flask蓝图(Blueprint)提供了模块化应用的方式。

9.1 Flask蓝图

蓝图是一种组织一组相关视图和其他代码的方式,可以被注册到应用,但不是一个完整的应用。

创建蓝图:

from flask import Blueprint # 创建蓝图实例 users_bp = Blueprint('users', __name__, url_prefix='/users')# 定义蓝图路由@users_bp.route('/')defuser_list():return'User list'@users_bp.route('/<int:user_id>')defuser_detail(user_id):returnf'User {user_id} detail'

注册蓝图:

from flask import Flask # 导入蓝图from.views.users import users_bp app = Flask(__name__)# 注册蓝图 app.register_blueprint(users_bp)

9.2 蓝图高级用法

蓝图可以有自己的静态文件和模板目录:

admin_bp = Blueprint('admin', __name__, url_prefix='/admin', template_folder='templates/admin', static_folder='static/admin')

蓝图特定的中间件:

@admin_bp.before_requestdefrestrict_to_admins():ifnot current_user.is_admin: abort(403)

嵌套蓝图:

from flask import Blueprint main = Blueprint('main', __name__) admin = Blueprint('admin', __name__, url_prefix='/admin')@main.route('/')defindex():return'Main index'@admin.route('/')defadmin_index():return'Admin index'# 可以将两个蓝图组合为一个更大的蓝图 super_bp = Blueprint('super', __name__) super_bp.register_blueprint(main) super_bp.register_blueprint(admin)# 然后注册到应用 app.register_blueprint(super_bp, url_prefix='/portal')

9.3 应用工厂模式

应用工厂是一种设计模式,用于延迟创建应用实例,有助于测试和多实例部署:

# app/__init__.pyfrom flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_login import LoginManager from config import config # 初始化扩展但不传递应用实例 db = SQLAlchemy() login_manager = LoginManager() login_manager.login_view ='auth.login'defcreate_app(config_name): app = Flask(__name__) app.config.from_object(config[config_name])# 初始化扩展 db.init_app(app) login_manager.init_app(app)# 注册蓝图from.main import main as main_blueprint from.auth import auth as auth_blueprint app.register_blueprint(main_blueprint) app.register_blueprint(auth_blueprint, url_prefix='/auth')return app 

配置类:

# config.pyimport os classConfig: SECRET_KEY = os.environ.get('SECRET_KEY','hard-to-guess-string') SQLALCHEMY_TRACK_MODIFICATIONS =False@staticmethoddefinit_app(app):passclassDevelopmentConfig(Config): DEBUG =True SQLALCHEMY_DATABASE_URI = os.environ.get('DEV_DATABASE_URL','sqlite:///dev.db')classTestingConfig(Config): TESTING =True SQLALCHEMY_DATABASE_URI = os.environ.get('TEST_DATABASE_URL','sqlite:///:memory:')classProductionConfig(Config): SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL','sqlite:///prod.db') config ={'development': DevelopmentConfig,'testing': TestingConfig,'production': ProductionConfig,'default': DevelopmentConfig }

使用应用工厂:

# run.pyimport os from app import create_app, db from app.models import User, Role app = create_app(os.getenv('FLASK_CONFIG','default'))@app.shell_context_processordefmake_shell_context():returndict(db=db, User=User, Role=Role)

9.4 推荐的项目结构

对于中大型Flask应用,推荐以下目录结构:

myapp/ ├── app/ │ ├── __init__.py # 应用工厂 │ ├── models/ # 数据库模型 │ │ ├── __init__.py │ │ ├── user.py │ │ └── post.py │ ├── views/ # 视图函数和蓝图 │ │ ├── __init__.py │ │ ├── main.py │ │ ├── auth.py │ │ └── api.py │ ├── forms/ # 表单类 │ │ ├── __init__.py │ │ ├── auth.py │ │ └── main.py │ ├── static/ # 静态文件 │ │ ├── css/ │ │ ├── js/ │ │ └── img/ │ ├── templates/ # HTML模板 │ │ ├── base.html │ │ ├── main/ │ │ ├── auth/ │ │ └── errors/ │ └── utils/ # 工具函数 │ ├── __init__.py │ └── helpers.py ├── migrations/ # 数据库迁移 ├── tests/ # 测试用例 │ ├── __init__.py │ ├── test_user.py │ └── test_api.py ├── venv/ # 虚拟环境 ├── config.py # 配置文件 ├── requirements.txt # 依赖包列表 ├── run.py # 应用入口 └── README.md # 项目说明 

9.5 包管理与依赖

管理项目依赖是保持稳定性和可移植性的关键:

创建requirements.txt

pip freeze > requirements.txt 

也可以手动维护,区分生产和开发环境:

# requirements/base.txt flask==2.0.1 flask-sqlalchemy==2.5.1 flask-login==0.5.0 # requirements/dev.txt -r base.txt pytest==6.2.5 coverage==6.0.2 # requirements/prod.txt -r base.txt gunicorn==20.1.0 

安装依赖:

pip install -r requirements/dev.txt 

10. 部署与维护

将Flask应用部署到生产环境需要考虑多方面因素,包括性能、安全和可靠性。

10.1 部署准备

部署前的准备工作:

性能优化:根据需要实施缓存和其他优化

from flask_caching import Cache cache = Cache(app, config={'CACHE_TYPE':'SimpleCache'})@app.route('/expensive-operation')@cache.cached(timeout=60)# 缓存60秒defexpensive_operation():# 复杂计算return result 

配置日志:设置合适的日志级别和处理器

import logging logging.basicConfig( filename='app.log', level=logging.INFO,format='%(asctime)s %(levelname)s: %(message)s')

使用环境变量:敏感设置应通过环境变量配置

app.config['SECRET_KEY']= os.environ.get('SECRET_KEY') app.config['DATABASE_URI']= os.environ.get('DATABASE_URI')

禁用调试模式:确保关闭调试模式,避免暴露敏感信息

app.run(debug=False)

10.2 WSGI服务器

Flask内置的开发服务器不适用于生产环境,应使用生产级WSGI服务器:

Gunicorn
pip install gunicorn 

启动Gunicorn:

gunicorn -w 4 -b 127.0.0.1:5000 wsgi:app 

其中:

  • -w 4: 使用4个工作进程
  • -b 127.0.0.1:5000: 绑定地址和端口
  • wsgi:app: 应用入口点(wsgi.py中的app对象)
uWSGI
pip install uwsgi 

创建uwsgi.ini配置文件:

[uwsgi] module = wsgi:app master = true processes = 4 socket = myapp.sock chmod-socket = 660 vacuum = true die-on-term = true 

启动uWSGI:

uwsgi --ini uwsgi.ini 

10.3 Web服务器配置

为了处理静态文件、SSL和请求分发,通常在WSGI服务器前配置Nginx或Apache。

Nginx配置
server { listen 80; server_name example.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; } } 
使用HTTPS
server { listen 443 ssl; server_name example.com; ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/key.pem; location / { proxy_pass http://127.0.0.1:5000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } } 

10.4 Docker部署

使用Docker容器化Flask应用:

创建Dockerfile:

FROM python:3.9-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . ENV FLASK_APP=app ENV FLASK_ENV=production CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:5000", "wsgi:app"] 

构建并运行Docker镜像:

docker build -t flask-app . docker run -p 5000:5000 -d flask-app 

使用Docker Compose管理多容器应用:

# docker-compose.ymlversion:'3'services:web:build: . ports:-"5000:5000"environment:- DATABASE_URI=postgresql://user:password@db:5432/app depends_on:- db db:image: postgres:13environment:- POSTGRES_USER=user - POSTGRES_PASSWORD=password - POSTGRES_DB=app volumes:- postgres_data:/var/lib/postgresql/data volumes:postgres_data:

10.5 部署平台

Heroku

创建Procfile:

web: gunicorn wsgi:app 

部署到Heroku:

git push heroku master 
PythonAnywhere

在WSGI配置文件中:

import sys path ='/home/yourusername/myapp'if path notin sys.path: sys.path.append(path)from app import create_app application = create_app('production')
AWS Elastic Beanstalk

创建.ebextensions/01_flask.config

option_settings:aws:elasticbeanstalk:container:python:WSGIPath: wsgi:app 

10.6 性能监控与日志

使用监控工具追踪应用性能:

  1. ELK Stack - 日志收集与分析

Prometheus + Grafana - 指标监控:

pip install prometheus-flask-exporter 
from prometheus_flask_exporter import PrometheusMetrics metrics = PrometheusMetrics(app)

Sentry - 错误跟踪:

pip install sentry-sdk[flask] 
import sentry_sdk from sentry_sdk.integrations.flask import FlaskIntegration sentry_sdk.init( dsn="your-dsn-here", integrations=[FlaskIntegration()])

10.7 自动化部署与CI/CD

使用CI/CD流水线自动化测试和部署:

GitHub Actions配置示例(.github/workflows/deploy.yml):

name: Deploy on:push:branches:[ main ]jobs:test:runs-on: ubuntu-latest steps:-uses: actions/checkout@v2 -name: Set up Python uses: actions/setup-python@v2 with:python-version:'3.9'-name: Install dependencies run:| python -m pip install --upgrade pip pip install pytest pip install -r requirements.txt-name: Test with pytest run:| pytestdeploy:needs: test runs-on: ubuntu-latest steps:-uses: actions/checkout@v2 -name: Deploy to Heroku uses: akhileshns/[email protected] with:heroku_api_key: ${{secrets.HEROKU_API_KEY}}heroku_app_name:"your-app-name"heroku_email:"[email protected]"

11. 总结

本文全面介绍了Flask Web框架的核心功能和开发最佳实践,覆盖了从基础入门到高级应用的各个方面。Flask作为一个灵活而强大的微框架,其简约的设计理念和丰富的扩展生态系统使其成为Python Web开发的理想选择。

11.1 Flask的优势

  • 易于上手:简单直观的API设计,学习曲线平缓
  • 灵活性:不强制特定项目结构或组件选择
  • 可扩展性:通过扩展实现各种高级功能
  • 性能良好:核心轻量且高效
  • 活跃的社区:丰富的文档和第三方库支持

11.2 学习路径建议

对于Flask初学者,建议按以下顺序学习:

  1. 掌握基本路由和视图函数
  2. 学习模板系统和表单处理
  3. 探索数据库集成
  4. 实现用户认证与授权
  5. 熟悉应用结构和蓝图
  6. 学习API开发
  7. 了解高级主题和部署

11.3 进一步学习资源

11.4 结语

Flask通过"微框架"的设计理念,提供了Web开发所需的核心功能,同时保持足够的灵活性让开发者根据自己的需求选择合适的工具和扩展。这种设计使Flask能够适应从简单API到复杂企业应用的各种开发场景。

通过本指南中介绍的概念和技术,你已经具备了使用Flask开发现代Web应用的基础知识。随着实践经验的积累,你将能够构建越来越复杂和高质量的应用,充分发挥Flask框架的潜力。


作者:climber1121
链接:https://blog.ZEEKLOG.net/climber1121
来源:ZEEKLOG
版权声明:本文为博主原创文章,转载请附上原文出处链接和本声明。

Read more

从安装到实测:基于 Claude Code + GLM-4.7 的前端生成与评测实战

从安装到实测:基于 Claude Code + GLM-4.7 的前端生成与评测实战

目录 引言 一、命令行使用 Claude Code(安装与配置) 步骤一:安装 Claude Code(命令行) 步骤二:配置蓝耘MaaS平台 步骤三:常见排查 二、编码工具中使用 claude-code:三个端到端案例(含提示与实测评价) 案例 1:交互式个人血压记录网页 — 前端端到端生成 案例 2:Web 双人对战小游戏(Joy-Con 风格) 案例 3:前端可视化组件生成 三、补充建议(快速 checklist) 总结 引言 近一年来,代码生成类工具逐渐从“写几行示例代码”走向“完整功能交付”,但真正落到工程实践时,很多工具仍停留在 Demo 阶段:要么跑不起来,

By Ne0inhk

图论基础与遍历算法(BFS+DFS)

一、图的核心概念 1. 图的定义:图 G=(V,E) 由顶点(节点 V)和边(E)组成,是描述元素间关联关系的核心数据结构。 2. 图的分类:无向图(边无方向)、有向图(边有方向,从起点指向终点)、带权图(边附带距离、概率等权重信息)。 二、图的存储方式 (一)邻接矩阵 * 结构:n 个顶点的图对应 n×n 矩阵,通过矩阵元素值表示顶点间连接关系。 * 关键特性:无向图的邻接矩阵是对称矩阵(a[i][j]=a[j][i],1 表示有边,0 表示无边);有向图的邻接矩阵不一定对称(a[

By Ne0inhk
【C++进阶系列】:万字详解unordered_set和unordered_map,带你手搓一个哈希表!(附模拟实现unordered_set和unordered_map的源码)

【C++进阶系列】:万字详解unordered_set和unordered_map,带你手搓一个哈希表!(附模拟实现unordered_set和unordered_map的源码)

🔥 本文专栏:c++ 🌸作者主页:努力努力再努力wz 💪 今日博客励志语录:努力不是为了回报,而是不让自己留下任何遗憾 ★★★ 本文前置知识: map和set模拟实现 引入 那么在正式讲解STL的unordered_map以及unordered_set这两个容器之前,我们先来回顾一下,目前我们接触到能够高效查找数据的数据结构,那么首先我们可以想到的能够实现高效查找数据的数据结构便是数组,但是这里的数组不是简单的将元素直接存放到数组中的任意位置,而是会将存储在数组中的元素先进行一次排序,然后借助二分算法来进行查找,由于这里数组的排序只需要一次,那么排序付出的代价可以均摊到每一次的查找操作中,所以这里排序的代价可以忽略不计,而二分查找的时间复杂度则是logN,所以这种方式能够实现高效的数据查找,但是如果涉及到插入以及删除操作的话,如果插入以及删除元素不在数组末尾,那么必然就要移动大量的元素,意味着插入和删除的时间复杂度最坏情况下会到达O(N),效率相比于查找就不那么高效 接着就是在二叉搜索树的基础上优化,压缩其高度的AVL树和红黑树这两个数据结构,这两种数据结

By Ne0inhk
【排序算法全家桶 Level 3】交换排序:从冒泡优化到快排四重奏

【排序算法全家桶 Level 3】交换排序:从冒泡优化到快排四重奏

🏠 个人主页:EXtreme35 📚 个人专栏: 专栏名称专栏主题简述《C语言》C语言基础、语法解析与实战应用《数据结构》线性表、树、图等核心数据结构详解《题解思维》算法思路、解题技巧与高效编程实践 目录 * 一、 冒泡排序 * 1.1 算法思想:气泡升腾的奥秘 * 1.2 为什么你的冒泡排序总是比别人慢? * 1.3 代码实现 * 二、快速排序 * 2.1 初始版本:Hoare 版 * 2.1.1 初始代码 * 2.1.2 优化一:三数取中 * 2.1.2 优化二:小区间优化 * 2.2

By Ne0inhk