01.初识FLASK框架
Flask是使用Python编写的Web微框架。Web框架可以不用关心底层的请求响应处理,更加方便、高效的编写Web程序。因为Flask框架简单且易于扩展,所以被称之为微框架。
Flask有两个主要依赖:
-
WSGI(Web服务器 网关接口)工具集
WSGI(Web Server Gateway Interface)是Python中用来规定Web服 务器如何与Python Web程序进行沟通的标准
-
Jinjia2(模板引擎)
Flask只保留了Web开发的核心功能,其他的功能都是由外部扩展来实现的,比如数据库集成、表单验证、文件上传等。
如果没有合适的扩展,甚至还可以自己开发。
Flask不会替自己做决定,但绝不会限制开发。
以下所有的案例都是基于CentOS 7 下测试。
一、搭建开发环境
这里将会详细的介绍Flask的搭建方式。后续所有的示例均为CentOS 7 上演示。
1.Python环境搭建
详情请跳转,点击跳转
2.创建虚拟环境
在python中,虚拟环境就是隔离Python解释器环境,通过创建虚拟环境,可以拥有一个独立的Python解释器环境。这样做的好处就是可以为每一个项目创建独立的Python解释器环境,因为不同的项目常常会依赖于不同版本的第三方库或者Python版本。使用虚拟环境可以保持全局Python解释器环境的干净,避免包和版本的混乱,并且可以方便的区分和记录每个项目的依赖,以便在新的环境中复用现有的依赖环境。
mkvirtualenv py3env -p /usr/bin/python3
-p 参数,用于指定解释器的版本创建虚拟环境。例如指向Python2.7则创建的虚拟环境的Python版本为2.7
此命令用于创建虚拟环境,默认解释器路径为
用户主目录/.virtualenvs
中
相关命令:
- 列出当前所有的虚拟环境
workon
- 进入某一个虚拟环境
workon py3env
- 退出当前虚拟环境
deactivate
3.安装Flask
使用上述所创建的虚拟解释器安装Flask
workon py3env
pip install flask
如果安装Python解释器为上述中的安装方式,此处安装Flask会非常快,因为已经更换了国内源
安装的过程中会发现,除了Flask包外,同时被安装的还有5个依赖包,如下:
名称与版本 | 说明 |
---|---|
Jinjia2(3.0.1) | 模板渲染引擎 |
MarkupSafe(2.0.1) | HTML字符转义(escape)工具 |
Werkzeug(2..0.1) | WSGI工具集,处理请求与响应,内置WSGI开发服务器、调试器、重载器 |
click(8.0.1) | 命令行工具 |
itsdangerous(2.0.1) | 提供各种加密签名功能 |
4.集成开发环境
Python编辑器有很多种,例如notepad++、Sublime Text等,甚至可以用记事本编辑。
这里推荐使用Jet家族的PyCharm集成开发环境。
PyCharm的具体操作如下:
①下载并安装PyCharm
必须要清楚,PyCharm的专业版是收费的,而社区版的是免费的。这里选择试用的专业版
安装过程比较比较简单,详细的操作步骤请参考
②创建项目
安装成功后,初始界面提供了多种方式创建新项目。这里可以单 击“Open”,选择一个任意的文件夹。打开项目后的界面如图所示,左边是项目目录树,右边是代码编辑区域。单击左下角的方形图标可以隐藏和显示工具栏,显示工具栏后,可以看到常用的Python交互控制台(Python Console)和终端(Terminal,即命令行工具)。
③设置Python解释器
因为是远程解释器,所以这里需要配置SSH远程虚拟环境,具体步骤如下
- 单击“File”、“Settings”、“Project”、“Python Interpreter”
-
右上角锯齿状按钮点击“Add”添加解释器
-
切换到“SSH Interpreter”,填入PythonServer的服务器信息,点击“Next”即可跳转到下一步
-
输入服务器密码之后即可连接到服务器
-
选择解释器路径
-
确定好解释器之后,PyCharm会自动同步第三方库信息
二、Hello Flask
创建一个app.py
文件
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return "<h1>Hello Flask !!!</h1>"
这个py文件中包含一个最小的Flask程序。
对于简单的程序来说,程序的主模块一般命令为app.py
。也可以使用其他名称,比如hello.py
,但是要避免使用flask.py
,这样回和本身的Flask程序相冲突。
1.创建程序实例
安装Flask时,会在Python解释器中创建一个flask包,可以通过flask包的构造文件导入所有开放的类和函数。先从flask包导 入Flask类,这个类表示一个Flask程序。实例化这个类,就得到程序实例APP
from flask import Flask
app = Flask(__name__)
传入Flask类构造方法的第一个参数是模块或包的名称,应该使用特殊变量__name__。Python会根据所处的模块来赋予__name__变量相 应的值,对于程序来说(app.py),这个值为app。除此之外,这也会帮助Flask在相应的文件夹里找到需要的资源,比如模板和静态文 件。
Flask类是Flask的核心类,它提供了很多与程序相关的属性和方法。在后面,经常会直接在程序实例app上调用这些属性和方法来实现相关功能。在第一次提及Flask类中的某个方法或属性时,会直接以实例方法或者属性的形式写出,比如存储程序名称的属性为app.name。
2.注册路由
在一个Web应用里,客户端和服务器上的Flask程序的交互可以简单概括为以下几步:
- 用户在浏览器输入URL访问某个资源。
- Flask接收用户请求并分析请求的URL。
- 为这个URL找到对应的处理函数。
- 执行函数并生成响应,返回给浏览器。
- 浏览器接收并解析响应,将信息显示在页面中。
在上面这些步骤中,大部分都由Flask完成,需要做的只是建立处理请求的函数,并为其定义对应的URL规则。只需为函数附加 app.route()装饰器,并传入URL规则作为参数,就可以让URL与函数建立关联。这个过程称为注册路由(route),路由负责管理 URL和函数之间的映射,而这个函数则被称为视图函数(view function)。
在这个程序里,app.route()装饰器把根地址/和index()函数绑定起来,当用户访问这个URL时就会触发index()函数。这个视图函数可以像其他普通函数一样执行任意操作,比如从数据库中获取信息, 获取请求信息,对用户输入的数据进行计算和处理等。最后,视图函数返回的值将作为响应的主体,一般来说,响应的主体就是呈现在浏览器 窗口的HTML页面。在最小程序中,视图函数index()返回一行问候:
@app.route("/")
def index():
return "<h1>Hello World !!!</h1>"
虽然这个程序相当简单,但它却是大部分Flask程序的基本模式。在复杂的程序中,会有许多个视图函数分别处理不同URL的请求,在视图函数中会完成更多的工作,并且返回包含各种链接、表单、图片的 HTML文件,而不仅仅是一行字符串。返回的页面中的链接又会指向其他URL,被单击后触发对应的视图函数,获得不同的返回值,从而显示不同的页面,这就是浏览网页时的体验。
route()
装饰器的第一个参数是URL规则,用字符串表示,必须以斜杠/
开始。这里的URL是相对URL(又称为内部URL),即不包含域名的URL。以域名www.helloflask.com为例,“/”对应的是根地址 (即www.helloflask.com),如果把URL规则改为“/hello”,则实际的绝 对地址(外部地址)是www.helloflask.com/hello。 假如这个程序部署在域名为www.helloflask.com的服务器上,当启 动服务器后,只要在浏览器里访问www.helloflask.com,就会看到浏览器上显示一行“Hello,Flask!”问候。
①为视图绑定多个URL
@app.router("/hi")
@app.router("/hello")
def say_hello():
return "Hello World!!!"
一个视图函数可以绑定多个URL,比如上边的代码中,把hi和hello都绑定到say_hello函数上,这就会为say_hello视图注册两个路由。
用户访问这两个url均会触发say_hello函数,获得相同的响应。
②动态URL
@app.route('/greet/<name>')
def greet(name):
return '<h1>Hello, %s!</h1>' % name
FLask不仅可以为视图函数绑定多个URL,还可以在URL规则中添加变量部分,使用“<变量名>”的形式表示。
Flask处理请求时会把变量传入 视图函数,所以可以添加参数获取这个变量值。
当URL规则中包含变量时,如果用户访问的URL中没有添加变量, 比如/greet,那么Flask在匹配失败后会返回一个404错误响应。一个很常见的行为是在app.route()装饰器里使用defaults参数设置URL变量的默认值,这个参数接收字典作为输入,存储URL变量和默认值的映射。在 下面的代码中,greet视图新添加了一个app.route()装饰器, 为/greet设置了默认的name值:
@app.route('/greet', defaults={'name': 'ChanceySolo'})
@app.route('/greet/<name>')
def greet(name):
return '<h1>Hello, %s!</h1>' % name
三、启动开发服务器
Flask内置了一个简单的开发服务器(由依赖包Werkzeug提供), 足够在开发和测试阶段使用。
1.命令行启动
Flask通过依赖包Click内置了一个CLI系统。在安装了Flask后,会自动添加一个flask命令脚本,通过flask命令执行内置命令、扩展提供的命令或者是自己定义的命令。
其中,flask run命令用来启动内置的开发服务器。
注意:
确保执行命令前激活了虚拟服务器,即先执行命令
workon py3env
,再执行flask run
;如果执行
flask run
命令后显示命令未找到提示(command not found)或者其他错误,可以尝试使用python -m fkask run
来启动
flask run命令的开发服务器默认会监听http://127.0.0.1:5000/
地址,并开启多线程支持。
打开浏览器访问该地址
老版的启动开发服务器的方式是使用
app.run()
方法,目前已不推荐
自动发现程序实例
一般来说,在执行
flask run
命令运行之前,需要提供程序实例所在模块的位置。以上可以直接运行,是因为FLask会自动探测程序实例,自动探测存在如下的规则:
- 从当前目录寻找
app.py
和wsgi.py
模块,并从中寻找名为app
或application
的程序实例- 从环境变量
FLASK_APP
对应的值寻找名为app
或application
的程序实例- 第三条规则后边介绍:管理环境变量
因为程序主模块命名为app.py,所以
flask run
命令会自动在 其中寻找程序实例。如果程序主模块是其他名称,比如hello.py, 那么需要设置环境变量FLASK_APP
,将包含程序实例的模块名赋值给这个变量。Linux或macOS系统使用export命令:export FLASK_APP=hello
在Windows系统中使用set命令:
set FLASK_APP=hello
管理环境变量
Flask的自动发现程序实例机制还有第三条规则:如果安装
python-dotenv
,那么在使用flask run
或其他命令的时候会自动从.flaskenv
文件和.env
文件中加载环境变量当安装了python-dotenv时,Flask在加载环境变量的优先级是:
手动设置的环境变量>.env中设置的环境变量>.flaskenv设置的环境变量
除了FLASK_APP,在后面还会用到其他环境变量。
环境变量在新创建命令行窗口或重启电脑后就清除了,每次都要重设变量有些麻烦。而且如果同时开发多个Flask程序,这个FLASK_APP就需要在不同的值之间切换。为了避免频繁设置环境变量,使用python-dotenv管理项目的环境变量,首先使用Pipenv将它安装到虚拟环境:
pip install python-dotenv
在项目根目录下分别创建两个文件:
.env
和.flaskenv
。.flaskenv
用来存储和Flask相关的公开环境变量,比如FLASK_APP
;而.env
用来存储包含敏感信息的环境变量,比如用来配置Email服务器的账户名与密码。在.flaskenv
或.env
文件中,环境变量使用键值对的形式定义,每行一个,以#开头的为注释,如下所示:SOME_VAR=1 #这是注释 FOO="BAR"
2.使用PyCharm启动
在PyCharm中,虽然可以使用内置的命令行窗口执行命令以启动开发服务器,但是在开发时使用PyCharm内置的运行功能更加方便。 专业版添加了Flask命令行支持,在旧版本或社区版中,如果要使用PyCharm运行程序,还需要进行一些设置。
-
首先,在PyCharm中,单击菜单栏中的
Run
→Edit Configurations
打开运行配置窗口。- 步骤1 单击左侧的“+”符号打开下拉列表
- 步骤2 新建一个Python类型的运行配置(如果是专业版,则可以直接选择Flask server),并在右侧的Name字段输入一个合适的名称,比如“Run hello”
- 步骤3 勾选“Store as project file”
- 步骤4 将第一项配置字段通过下列选项选为“Module Name”
- 步骤5 填入模块名称flask
- 步骤6 第二栏的“Parameters”填入要执行的命令run,可以附加其他启动选项
- 步骤7 在“Working directory”字段中选择程序所在的目录作为工作目录
更多的启动选项
-
使服务器外部可见
上述的启动方式是外部设备访问不到的,可以在run命令后添加
--host
选项将主机地址设置为0.0.0.0
使其对外可见。或者使用命令
flask run --host="0.0.0.0"
-
改变默认的端口
Flask提供的Web服务器默认监听5000端口,可以通过启动的命令或者编辑启动PyCharm来更改
或者使用命令
flask run --port=8088
执行flask run命令时的host和port选项也可以通过环境变量
FLASK_RUN_HOST
和FLASK_RUN_PORT
设置。Flask内置的命令都可以使用这种模式定义默认选项值, 即“FLASK__”,使用
flask --help
命令查看所有可用的命令。
-
设置运行环境
开发环境(development enviroment)和生产环境(production enviroment)会频繁接触到。
开发环境是指在本地编写和测试程序时的计算机环境,而生产环境与开发环境相对,它指的是网站部署上线供用户访问时的服务器环境。
根据运行环境的不同,Flask程序、扩展以及其他程序会改变相应的行为和设置。为了区分程序运行环境,Flask提供了一个
FLASK_ENV
环境变量用来设置环境,默认为production(生产)。在开发时,将其设为development(开发),这会开启所有支持开发的特性。为了方便管理,把环境变量FLASK_ENV的值写入.flaskenv文件中:
FLASK_ENV=development
在开发环境下,调试模式将被打开,这是执行
flask run
启动程序会自动激活Werkzeug内置的调试器和重载器。如果需要单独控制调试开关,可以通过
FLASK_DEBUG
环境变量设置,设置为1则开启,设置为0则关闭。不过不推荐此项来设置调试模式-
调试器
Werkzeug提供的调试器非常强大,当程序出错时,可以在网页上看到详细的错误追踪信息,这在调试错误时非常有用。调试器允许在错误页面上执行Python代码。单击错误信息右侧的命令行图标,会弹出窗口要求输入PIN码,也就是在启动服务器时命令行窗口打印出的调试器PIN码(Debugger PIN)。输入PIN码后,可以单击错误堆栈的某个节点右侧的命令行界面图标,这会打开一个包含代码执行上下文信息的Python Shell,可以利用它来进行调试。
-
重载器
在修改代码之后,期望的行为是这些改动立刻作用到程序上。重载器的作用就是监测文件变动,然后重新启动开发服务器。修改了脚本内容并保存后,会在命令行看到重新启动的服务器。
默认会使用Werkzeug内置的stat重载器,它的缺点是耗电较严重, 而且准确性一般。为了获得更优秀的体验,可以安装另一个用于监测文件变动的Python库Watchdog,安装后Werkzeug会自动使用它来监测文件变动:
pip install watchdog --dev #因为这个包只在开发时才会用到,所以在安装命令后添加了一个--dev选项,这用来把这个包声明为开发依赖。在Pipfile文件中,这个包会被添加到dev-packages部分。不过,如果项目中使用了单独的CSS或JavaScript文件时,那么浏览器可能会缓存这些文件,从而导致对文件做出的修改不能立刻生效。在浏览器中,可以按下Crtl+F5或Shift+F5执行硬重载(hardreload),即忽略缓存并重载(刷新)页面。
-
四、PythonShell
在开发过程中,有许多的命令需要在Flask Shell
中执行。
执行这个命令之前,需要确保程序实例可以被正常找到。
Python Shell可以执行exit()
或quit()
退出,在Windows系统上可 以使用Crtl+Z并按Enter退出;在Linux和macOS则可以使用Crtl+D退出。 使用flask shell命令打开的Python Shell自动包含程序上下文,并且已经导入了app实例:
上下文(context)可以理解为环境。为了正常运行程序,一些操作相关的状态和数据需要被临时保存下来,这些状态和数据被统称为上下文。在Flask中,上下文有两种,分别为程序上下文和请求上下文。
五、Flask扩展
扩展(extension)即使用 Flask提供的API接口编写的Python库,可以为Flask程序添加各种各样的功能。大部分Flask扩展用来集成其他库,作为Flask和其他库之间的薄薄一层胶水。因为Flask扩展的编写有一些约定,所以初始化的过程大致相似。大部分扩展都会提供一个扩展类,实例化这个类,并传入创建的程序实例app作为参数,即可完成初始化过程。通常,扩展会在传入的程序实例上注册一些处理函数,并加载一些配置。
以某扩展实现了Foo功能为例,这个扩展的名称将是Flask-Foo或 Foo-Flask;程序包或模块的命名使用小写加下划线,即flask_foo(即导入时的名称);用于初始化的类一般为Foo,实例化的类实例一般使用 小写,即foo。初始化这个假想中的Flask-Foo扩展的示例如下所示:
from flask import Flask
from flask_foo import Foo
app = Flask(__name__)
foo = Foo(app)
在日常开发中,大多数情况下,没有必要重复制造轮子,所以选用扩展可以避免让项目变得臃肿和复杂。尽管使用扩展可以简化操作,快速集成某个功能,但同时也会降低灵活性。如果过度使用扩展, 在不需要的地方引入,那么相应也会导致代码不容易维护。更糟糕的是,质量差的扩展可能还会带来潜在的Bug,而不同扩展之间也可能会出现冲突。因此,在编写程序时,应该尽量从实际需求出发,只在需要的时候使用扩展,并把扩展的质量和兼容性作为考虑因素,尽量在效率和灵活性之间达到平衡。
早期版本的Flask扩展使用flaskext.foo或flask.ext.something的形式导 入,在实际使用中带来了许多问题,因此Flask官方推荐以 flask_something形式导入扩展。在1.0版本以后的Flask中,旧的扩展导入方式已被移除。
六、项目配置
在很多情况下,需要设置程序的某些行为,这时就需要使用配置变量。
在Flask中,配置变量就是一些大写形式的Python变量,也可称之为配置参数或配置键。使用统一的配置变量可以避免在程序中以硬编码(hard coded)的形式设置程序。
在一个项目中,会用到许多配置:
- Flask提供的配置
- 扩展提供的配置
- 程序特定的配置
和平时使用变量不同,这些配置变量都通过Flask对象的app.config属性作为统一的接口来设置和获取,它指向的 Config类实际上是字典的子类,所以可以像操作其他字典一样操作它。
Flask提供了很多种方式来加载配置。比如,像在字典中添加一个键值对一样来设置一个配置:
app.config["ADMIN_NAME"] = "ChanceySolo"
配置的名称必须是全大写形式,小写的变量将不会被读取。
使用update()
可以一次性加载多个值
app.config.update(
TESTING = True,
SECRET_KEY = "ChacneySolo1314"
)
除此之外,还可以把配置变量存储在单独的Python脚本、JSON 格式的文件或是Python类中,config对象提供了相应的方法来导入配置。和操作字典一样,读取一个配置就是从config字典里通过将配置变量的名称作为键读取对应的值:
value = app.config["ADMIN_NAME"]
某些扩展需要读取配置值来完成初始化操作,比如Flask-Mail,因此尽量将加载配置的操作提前,最好在程序实例app创建后就加载配置。
七、URL与端点
在Web程序中,URL无处不在。如果程序中的URL都是以硬编码的方式写出,那么将会大大降低代码的易用性。比如,修改了某个路由的URL规则,那么程序里对应的URL都要一个一个进行修改。更好的解决办法是使用Flask提供的url_for()
函数获取URL,当路由中定义的URL规则被修改时,这个函数总会返回正确的URL。
调用url_for()
函数时,第一个参数为端点(endpoint)值。在 Flask中,端点用来标记一个视图函数以及对应的URL规则。端点的默认值为视图函数的名称。
@app.router("/")
def idnex():
return "Hello World"
这个路由的端点即视图函数的名称index,调用
url_for("index")
即可获取对应的URL,即“/”。
在app.route()
装饰器中使用endpoint
参数可以自定义端点值,不过通常不需要这样做。 如果URL含有动态部分,那么需要在url_for()
函数里传入相应的参数,以下面的视图函数为例:
@app.route('/hello/<name>')
def greet(name):
return 'Hello %s!' % name
这时使用
url_for("say_hello", name="ChanceySolo")
得到的URL为/hello/ChanceySolo
使用url_for()
函数生成的URL是相对URL(即内部URL), 即URL中的path部分,比如“/hello”,不包含根URL。相对URL只能在程序内部使用。如果要生成供外部使用的绝对URL,可以在使用 url_for()
函数时,将_external
参数设为True
,这会生成完整的URL, 比如http://helloflask.com/hello,在本地运行程序时则会获得 http://localhost:5000/hello。
八、Flask命令
除了Flask内置的flask run
等命令,还可以自己定义命令。在虚拟环境中安装了Flask之后,包含许多内置命令可以使用。
通过创建任意一个函数,并为其添加app.cli.command()
装饰器,就可以注册一个Flask命令。
@app.cli.command("say_hello")
def hello():
click.echo("Hello Flask!!!")
函数的名称即为命令名称,这里注册的命令即hello,使用 flask hello
命令来触发函数。作为替代,也可以在app.cli.command()
装饰器中传入参数来设置命令名称,比如app.cli.command('say-hello')
会把命令名称设置为say-hello,完整的命令即flask say-hello
。
九、模板与静态文件
一个完整的网站当然不能只返回用户一句“Hello,World!”,这时就需要模板(template)和静态文件(static file)来生成更加丰富的网页。
模板即包含程序页面的HTML文件,静态文件则是需要在HTML文件中加载的CSS和JavaScript文件,以及图片、字体文件等资源文件。
默认情况下,模板文件存放在项目根目录中的templates
文件夹中,静态文件存放在static
文件夹下,这两个文件夹需要和包含程序实例的模块处于同一个目录下,对应的项目结构示例如下所示:
HelloFlask/
- templates/
- static/
- app.py
在开发Flask程序时,使用CSS框架和JavaScript库是很常见的需求, 而且有很多扩展都提供了对CSS框架和JavaScript库的集成功能。使用这些扩展时都需要加载对应的CSS和JavaScript文件,通常这些扩展都会提供一些可以在HTML模板中使用的加载方法或者函数,使用这些方法即可渲染出对应的link标签和script标签。这些方法一般会直接从CDN加载资源,有些提供了手动传入资源URL的功能,有些提供了内置的本地资源。
在开发环境下建议使用本地资源,这样可以提高加载速度。最好自己下载到static目录下,统一管理,出于方便的考虑也可以使用扩展内置的本地资源。在过渡到生产环境时,自己手动管理所有本地资源或自己设置CDN,避免使用扩展内置的资源。这个建议主要基于下面这些考虑因素:
- 鉴于国内的网络状况,扩展默认使用的国外CDN可能会无法访问,或访问过慢。
- 不同扩展内置的加载方法可能会加载重复的依赖资源,比如 jQuery。
- 在生产环境下,将静态文件集中在一起更方便管理。
- 扩展内置的资源可能会出现版本过旧的情况。
CDN指分布式服务器系统。服务商把需要的资源存储在分布于不同地理位置的多个服务器,它会根据用户的地理位置来就近分配服务器 提供服务(服务器越近,资源传送就越快)。
使用CDN服务可以加快网 页资源的加载速度,从而优化用户体验。
对于开源的CSS和JavaScript 库,CDN提供商通常会免费提供服务。
十、Flask与MVC架构
MVC架构最初是用来设计桌面程序的,后来也被用于Web程序,应用了这种架构的Web框架有Django、Ruby on Rails等。在MVC架构中, 程序被分为三个组件:数据处理(Model)、用户界面(View)、交互逻辑(Controller)。如果套用MVC架构的内容,那么Flask中视图函数的名称其实并不严谨,使用控制器函数(Controller Function)似乎更合适些,虽然它也附带处理用户界面。严格来说,Flask并不是MVC架构 的框架,因为它没有内置数据模型支持。
粗略归类,如果想要使用Flask来编写一个MVC架构的程序,那么 视图函数可以作为控制器(Controller),视图(View)就是使用Jinja2渲染的HTML模板,而模型(Model)可以使用其他库来实现。