惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

F
Fortinet All Blogs
云风的 BLOG
云风的 BLOG
M
MIT News - Artificial intelligence
WordPress大学
WordPress大学
T
Tailwind CSS Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
S
Secure Thoughts
博客园 - 【当耐特】
Know Your Adversary
Know Your Adversary
NISL@THU
NISL@THU
博客园 - 司徒正美
Last Week in AI
Last Week in AI
C
Cybersecurity and Infrastructure Security Agency CISA
P
Privacy & Cybersecurity Law Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
B
Blog
The GitHub Blog
The GitHub Blog
小众软件
小众软件
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Spread Privacy
Spread Privacy
Martin Fowler
Martin Fowler
博客园 - 叶小钗
Security Archives - TechRepublic
Security Archives - TechRepublic
T
Tenable Blog
S
Securelist
博客园 - 三生石上(FineUI控件)
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Microsoft Security Blog
Microsoft Security Blog
Apple Machine Learning Research
Apple Machine Learning Research
罗磊的独立博客
T
Threat Research - Cisco Blogs
Application and Cybersecurity Blog
Application and Cybersecurity Blog
F
Full Disclosure
Cloudbric
Cloudbric
The Cloudflare Blog
Y
Y Combinator Blog
Hugging Face - Blog
Hugging Face - Blog
Microsoft Azure Blog
Microsoft Azure Blog
H
Hacker News: Front Page
腾讯CDC
L
Lohrmann on Cybersecurity
C
CERT Recently Published Vulnerability Notes
V2EX - 技术
V2EX - 技术
GbyAI
GbyAI
TaoSecurity Blog
TaoSecurity Blog
I
Intezer
The Last Watchdog
The Last Watchdog
G
GRAHAM CLULEY
Google Online Security Blog
Google Online Security Blog
T
The Blog of Author Tim Ferriss

Dvel's Blog

Rime 全拼双拼混输 CHD 油猴脚本:每日签到自动答题 Rime 配置:雾凇拼音 Hammerspoon 自动切换输入法 修复 Hugo 本地图片的累计布局偏移(CLS)问题 macOS 利用 Karabiner 修改 Emacs 键位为 Vim 键位 macOS 修改 Emacs 键位为 Vim 键位 Surge 配置 汉字的混乱 Netflix 中英双语字幕的好办法 优化 Rime 英文输入体验 判断字符是否为简体字或繁体字 Cloudflare 转入域名提示「出现了问题。请重试转移此域。尚未向您收费。」 图片压缩:Squoosh、TinyPNG、ImageOptim、WebP macOS grep + sed 批量替换多个文件的内容 在 macOS 根目录创建文件夹 博客图床小妙招 折腾 Hugo & PaperMod 主题 利用 Cloudflare Workers 进行批量 301 重定向 将博客部署在 Cloudflare Pages Alfred File Navigation(文件导航器)详解 在 Hugo Goldmark Markdown 中设置以新标签打开链接 日期与时间(UTC、GMT、时间戳、时区) 让 Alfred 以新标签的方式打开 Finder 用 git filter-branch 给 Git 仓库瘦身 qBittorrent 设置教程 联通光猫破解+桥接记录 配置 Mac 终端走代理 Steam for Mac 中文游戏推荐 我的 Mac 软件/工具列表 用 sleepwatcher 让 Mac 在睡眠及唤醒时自动执行一些脚本 解决 pyenv 导致的 brew warning 《SQL 必知必会》笔记 Git 自总结 Python 异步 IO 笔记 再见,双点医院! 用 CloudXNS 解决 CNAME 与 MX 记录冲突 你好,Hugo! 为 Mac 打造 zsh command line 环境 新 Mac 新装修 秦皇岛暑期自驾的烦恼 苹果公司开发者账号申请流程 UIScrollView 中使用 Masonry 解决 CocoaPods 的类库 import 没有提示 iOS 使用 CocoaPods 的快速回顾 GitBook 简单生成本地静态 HTML 的方法 你好, Hexo! About
《Flask Web 开发》笔记
Dvel · 2020-02-22 · via Dvel's Blog

《Flask Web 开发》第二版

豆瓣地址:https://book.douban.com/subject/30293851/

第 1 章 - 安装

Flask 哲学

Flask 自开发伊始就被设计为可扩展的框架,它具有一个包含基本服务的强健核心,其他功能则可通过扩展实现。

Flask 有 3 个主要依赖:

  • 路由、调试和 Web 服务器网关接口(WSGI,Web server gateway interface)子系统由 Werkzeug 提供;
  • 模板系统由 Jinja2 提供;
  • 命令行集成由 Click 提供。

开发者可以任意挑选符合项目需求的扩展,甚至可以自行开发。这和大型框架的做法相反,大型框架往往已经替你做出了大多数决定,难以(有时甚至不允许)使用替代方案。

venv

在 Python 3 中由标准库 venv 创建虚拟环境:

$ python3 -m venv virtual-environment-name

激活虚拟环境:

$ source venv/bin/activate

取消:

$ deactivate

另一种使用虚拟环境的方法:

在终端中执行 $ venv/bin/python 直接激活虚拟环境中的交互模式,退出时直接按下 ctrl + D。


第 2 章 - 应用的基本结构

初始化

所有 Flask 应用都必须创建一个应用实例,通常由下述代码创建:

from flask import Flask
app = Flask(__name__)

Web 服务器使用 WSGI 协议,把接受自客户端的所有请求转发给这个对象处理。

路由和视图函数

客户端(浏览器)把请求发送给 Web 服务器,Web 服务器再把请求发送给 Flask 应用实例。

应用实例需要知道对每个 URL 的请求要运行哪些代码,所有保存了一个 URL 到 Python 函数的映射关系。

处理 URL 和函数之间关系的程序称为路由

Flask 使用 app.route 装饰器或者更传统的 app.add_url_rule() 方法构建映射:

@app.route('/user/<name>')
def user(name):
    return '<h1>Hello {}!</h1>'.format(name)
def index():
    return '<h1>Hello World!</h1>'
app.add_url_rule('/', 'index', index)

index() 这样处理入站请求的函数称为视图函数

一个完整的应用

from flask import Flask
app = Flask(__name__)

@app.route('/)
def index():
    return '<h1>Hello World!</h1>'

Web 开发服务器

Flask 自带 Web 开发服务器,通过 flask run 命令启动。这个命令在 FLASK_APP 环境变量指定的 Python 脚本中寻找应用实例。

$ export FLASK_APP=hello.py
$ flask run

动态路由

@app.route('/user/<name>')
def user(name):
    return f'<h1>Hello, {name}!</h1>'

调试模式

Flask 应用可以在调试模式中运行,在调试模式下,开发服务器会默认加载重载器调试器

**重载器:**Flask 会监视项目中的所有源码文件,发现变动时自动重启服务器。

**调试器:**调试器是一个基于 Web 的工具,当应用抛出未处理的异常时,它会出现在浏览器中。

$ export FLASK_APP=hello.py
$ export FLASK_DEBUG=1
$ export FLASK_ENV=development (效果同上)
$ flask run

命令行选项

flask shell 命令在应用上下文中打开一个 Python shell 会话,可以运行维护任务、测试、调试问题。

请求-响应循环

上下文

为了避免大量可有可无的参数把视图函数弄得一团糟,Flask 使用上下文临时把某些对象变为全局可访问。在多线程服务器中,Flask 使用上下文让特点的变量在一个线程中全局可访问,与此同时不会干扰其他线程。

变量名 上下文 说明
current_app 应用上下文 当前应用的应用实例
g 应用上下文 处理请求时用作临时存储的对象,每次请求都会重设这个变量
request 请求上下文 请求对象,封装了客户端发出的 HTTP 请求中的内容
session 请求上下文 用户会话,值为一个字典,存储请求之间需要『记住』的值

Flask 请求对象 (request)

属性或方法 说明
form 一个字典,存储请求提交的所有表单字段
args 一个字典,存储通过 URL 查询字符串传递的所有参数
values 一个字典,form 和 args 的合集
cookies 一个字典,存储请求的所有 cookies
headers 一个字典,存储请求的所有 HTTP 首部
files 一个字典,存储请求上传的所有文件
get_data() 返回请求主体缓冲的数据
get_json() 返回一个 Python 字典,包含解析请求主体后得到的 JSON
blueprint 处理请求的 Flask 蓝本的名称;蓝本在第 7 章介绍
endpoint 处理请求的 Flask 端点的名称;Flask 把视图函数的名称用作路由端点的名称
scheme URL 方案(http 或 https)
is_secure() 通过安全的连接(HTTPS)发送请求时返回 True
host 请求定义的主机名,如果客户端定义了端口号,还包括端口号
path URL 的路径部分
query_string URL 的查询字符串部分,返回原始二进制值
full_path URL 的路径和查询字符串部分
url 客户端请求的完整 URL
base_url 同 url,但没有查询字符串部分
remote_addr 客户端的 IP 地址
environ 请求的原始 WSGI 环境字典

请求钩子

有时在处理请求之前或之后执行代码会很有用,但为了避免每个视图函数中都重复编写代码,Flask 提供了注册通用函数的功能,通过装饰器实现。

装饰器 说明
before_request 注册一个函数,在每次请求之前运行。
before_first_request 注册一个函数,只在处理第一个请求之前运行。可以通过这个钩子添加服务器初始化任务。
after_request 注册一个函数,如果没有未处理的异常抛出,在每次请求之后运行。
teardown_request 注册一个函数,即使有未处理的异常抛出,也在每次请求之后运行。

在请求钩子函数和视图函数之间共享数据一般使用上下文全局变量 g

响应

如果视图函数返回的响应需要使用不同的状态码,可以把数字代码作为第二个返回值。

Flask 视图函数还可以返回一个响应对象——make_response()

重定向,由于使用频繁,Flask 提供了 redirect() 辅助函数。


第 3 章 - 模板

Jinja2 模板引擎

渲染模板

Flask 提供的 render_template() 函数把 Jinja2 模板引擎集成到了应用中。

变量

模板中使用 {{ name }} 结构表示一个变量,支持复杂类型如列表、字典、对象。

支持过滤器:{{ name|capitalize }}

支持结构控制、宏(类似函数)。

支持模板继承:{% extends "bootstrap/base.html" %}{{ super() }}

初始化扩展

bootstrap = Bootstrap(app)

# 第 7 章中大型应用初始化扩展的方式:
bootstrap = Bootstrap()
def create_app(config_name):
  # ...
  bootstrap.init_app(app)
  # ...

自定义错误页面

使用 @app.errorhandler(404) 装饰器。

链接

url_for() 辅助函数帮助在模板中构建正确的 URL。

用法:

# 以视图函数名作为参数,以 app.add_url_route() 定义路由时,使用端点名
url_for('index') # /

# 绝对地址
url_for('index', _external=True) # http://localhost:5000/

# 动态 URL
url_for('user', name='john', page=2, version=1) # /user/john?page=2&version=1

表单

使用 Flask-WTF 定义表单:

from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired

class NameForm(FlaskForm):
    name = StringField('What is your name?', validators=[DataRequired()])
    submit = SubmitField('Submit')

使用 Flask-Bootstrap 提供的高层级辅助函数 quick_form() 渲染 Flask-WTF 表单:

{% import "bootstrap/wtf.html" as wtf %}
{{ wtf.quick_form(form) }}

在视图函数中处理表单:

@app.route('/', methods=['GET', 'POST'])
def index():
    name = None
    form = NameForm()
    if form.validate_on_submit():
        name = form.name.data
        form.name.data = ''
    return render_template('index.html', form=form, name=name)

Post / Redirect / Get 模式

Post / Redirect / Get 简称 PRG,是一种用来防止表单重复提交数据的一种 Web 设计模式。

闪现消息

flaks.flash 的简单使用。


第 5 章 数据库

Flask-SQLAlchemy 简单配置 SQLite 数据库

import os
from flask_sqlalchemy import SQLAlchemy

basedir = os.path.abspath(os.path.dirname(__file__))
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'data.sqlite')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

db = SQLAlchemy(app)

模型 (一对多)

class Role(db.Model):
    __tablename__ = 'roles'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    users = db.relationship('User', backref='role')

    def __repr__(self):
        return '<Role %r>' % self.name


class User(db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), unique=True, index=True)
    role_id = db.column(db.Integer, db.ForeignKey('roles.id'))

    def __repr__(self):
        return '<User %r>' % self.username

数据库操作 (flask shell)

创建表

>>> from hello import db
>>> db.create_all()
# 如果数据库文件已经存在,暴力删除再重新生成:
>>> db.drop_all()
>>> db.create_all()

插入行

>>> from hello import Role,User
>>> admin_role = Role(name='Admin')
>>> mod_role = Role(name='Moderator')
>>> user_role = Role(name='User')
>>> user_john = User(username='john', role=admin_role)
>>> user_susan = User(username='susan', role=user_role)
>>> user_david = User(username='david', role=user_role)
# 数据库会话(事务)
>>> db.session.add_all([admin_role, mod_role, user_role, user_john, user_susan, user_david])
>>> db.session.commit()
# 数据库会话支持回滚
>>> db.session.rollback()

修改行

>>> admin_role.name = 'Administrator'
>>> db.session.add(admin_role)
>>> db.session.commit()

删除行

>>> db.session.delete(mod_role)
>>> db.session.commit()

查询行

# 基本 all()
>>> Role.query.all()
[<Role 'Administrator'>, <Role 'User'>]
>>> User.query.all()
[<User 'john'>, <User 'susan'>, <User 'david'>]

# 过滤器
>>> User.query.filter_by(role=user_role).all()
[<User 'susan'>, <User 'david'>]

# 查看生成的原生 SQL 语句
>>> str(User.query.filter_by(role=user_role))
'SELECT users.id AS users_id, users.username AS users_username, users.role_id AS users_role_id \nFROM users \nWHERE ? = users.role_id'

# 如果中途退出 shell 会话,加载示例:
user_role = Role.query.filter_by(name='User').first()

# 关闭自动执行查询
>>> user_role.users
[<User 'susan'>, <User 'david'>]
# 在模型中加入 lazy='dynamic' 参数
users = db.relationship('User', backref='role', lazy='dynamic')
# 可以使用过滤器了
>>> user_role.users.order_by(User.username).all()
[<User 'david'>, <User 'susan'>]

集成 Python shell

通过 app.shell_context_processor 装饰器,在每次启动 flask shell 会话时自动导入数据库示例和模型:

@app.shell_context_processor
def make_shell_context():
    return dict(db=db, User=User, Role=Role)

第 6 章 电子邮件

使用 Flask-Mail 提供电子邮件支持,Python 的 smtplib 库不支持 OAuth2 验证方法,需要设置 Google 账户的「允许不够安全的应用的访问权限」。

配置:

app.config['MAIL_SERVER'] = 'smtp.gmail.com'
app.config['MAIL_PORT'] = 587
app.config['MAIL_USE_TLS'] = True
app.config['MAIL_USERNAME'] = os.environ.get('MAIL_USERNAME')
app.config['MAIL_PASSWORD'] = os.environ.get('MAIL_PASSWORD')
app.config['FLASKY_MAIL_SUBJECT_PREFIX'] = '[前缀]'
app.config['FLASKY_MAIL_SENDER'] = 'Flasky Admin <xxx@gmail.com>'
app.config['FLASKY_ADMIN'] = os.environ.get('FLASKY_ADMIN')

将敏感信息写入环境变量:

$ export MAIL_USERNAME=xxx@gmail.com       
$ export MAIL_PASSWORD=xxxxxxxx               
$ export FLASKY_ADMIN=xxx@gmail.com # 接收邮件的管理员

集成邮件发送功能:

from flask_mail import Mail, Message
mail = Mail(app)
def send_email(to, subject, template, **kwargs):
    msg = Message(app.config['FLASKY_MAIL_SUBJECT_PREFIX'] + subject,
                  sender=app.config['FLASKY_MAIL_SENDER'],
                  recipients=[to])
    msg.body = render_template(template + '.txt', **kwargs)
    msg.html = render_template(template + '.html', **kwargs)
    mail.send(msg)

编写视图函数:

# ...
if app.config['FLASKY_ADMIN']:
    send_email(app.config['FLASKY_ADMIN'], 'New User', 'mail/new_user', user=user)
# ...

第 7 章 大型应用的结构

提供一个可供中大型程序使用的应用结构

项目结构:

.
├── app/
│   ├── /__init__.py  
│   ├── /email.py
│   ├── /models.py
│   ├── /static/
│   ├── /templates/
│   ├── /main/
│      ├── /__init__.py
│      ├── /errors.py	
│      ├── /forms.py
│      ├── /views.py
├── migrations/
├── tests/
└── venv/
├── config.py
├── flasky.py
├── requirements.txt

编写配置模块,配合工厂函数可以创建多个实例,便于测试。

Flask 提供蓝本(blueprint)功能,蓝本中定义的路由和错误处理程序处于休眠状态,直到蓝本注册到应用上后才生效。

定义相关的环境变量启动 Flask:

$ export FLASKY_ADMIN=xxx@xxx.com
$ export MAIL_USERNAME=xxx@xxx.com
$ export MAIL_PASSWORD=xxx

$ export FLASK_APP=flasky.py
$ export FLASK_DEBUG=1

$ flask run

需求文件 requirements.txt

用 pip 生成 requirements.txt 文件:

$ pip freeze >requirements.txt

安装或升级包后,需手动更新。

其他人利用 requirements.txt 文件创建副本,先创建好虚拟环境,执行以下命令:

$ pip install -r requirements.txt

蓝本

Flask 之旅

如何理解Flask中的蓝本

其他

后续章节就是实战了,代码量太多了,没有做笔记了。。。