淘先锋技术网

首页 1 2 3 4 5 6 7

介绍

Flask是一个基于Python语言的轻量级Web框架,与之经常对比的框架还有Django、Tornado等框架,当然学习这些肯定首先要有一点Python基础,当然由于框架带来的优越性,通常在实际应用过程中配置出现的问题要多于编程的问题,如果是在工作中的业务上使用的话,那么更多的是针对产品需求来做用户交互体验和方式的修改,以及访问性能和数据并发量上的优化,所以刚开始学着学着就会需要再补充很多知识,不过没关系,谁都有个适应的过程,本文会把所有的点都一点一点研究明白,并会不断地更新和修正,欢迎交流和指正。

Welcome to Flask​flask.palletsprojects.com/en/1.1.x/

一.Python基础准备

官方文档中明确指出了前提需要对python所有熟悉,这里提供了一个官方的python tutorial网站,关于这个网站我觉得比较值得快速浏览一遍的原因是官方自带了中文版本,虽然你也可以使用Google,但是还是相信官方自带翻译心里面还是更容易接受一些。

It’s assumed that you’re already familiar with Python.
The official tutorial in the Python docs is a great way to learn or review first.

The Python Tutorial​docs.python.org/3/tutorial/

二.Flask介绍

首先看官方文档的介绍,这里着重提到了另外两个概念,Jinja和Werkzeug。

(1)Jinja

Jinja是日文中神社⛩的意思,这个我们从Jinjia的官方文档中也能够发现有一个大大的神社图标,Jinja是一个模板引擎,说的直白一点就是把Python的内容翻译成前端HTML界面能识别的内容。

这里其实意味着我们要掌握一定的前端知识HTML、CSS、Javascript

Jinja - Jinja Documentation (2.11.x)​jinja.palletsprojects.com/en/2.11.x/

(2)Werkzeug

Werkzeug:werkzeugGerman noun: “tool”. Etymology:werk(“work”),zeug(“stuff”)是一个WSGI工具库,而WSGI是什么?WSGI就是Web服务器网关接口(Python Web Server Gateway Interface,缩写为WSGI)是为Python语言定义的Web服务器和Web应用程序或框架之间的一种简单而通用的接口。

简单说就是主要做服务器通讯和路由解析功能,创建Http的Request和Response对象。

这里需要对计算机网络有一定了解,比如http协议,了解常用的http状态码等知识

HTTP状态码 | 菜鸟教程​www.runoob.com/http/http-status-codes.html

Werkzeug - Werkzeug Documentation (1.0.x)​werkzeug.palletsprojects.com/en/1.0.x/正在上传…重新上传取消

有关Jinja和Werkzeug的知识可以根据官方文档进一步了解,但是实际上它们作为工具来说,能够清楚它们是干什么的,并且能够正确使用它们就好,前期学习其实没必要过深入专研。

三.前言(Foreword)

1.Flask被称为轻量级框架的原因(What does “micro” mean?)

The “micro” in microframework means Flask aims to keep the core simple but extensible.

在介绍Flask时,官方在开头就把Flask的主要特色介绍出来,就是“micro”和“flexible”,核心简单,扩展丰富,对比Django来说,这一点实际上意味着Flask在大佬手中就是“变形金刚”,可以安装各种武器;在菜鸟手中就是“拨浪鼓”,反正能听个响开心一下。

2.官方推荐的配置习惯(Configuration and Conventions)

其中官方文档的建议是默认在项目根目录下创建两个文件夹,分别叫做static和templates,分别放静态文件和模板文件,当然如果是使用Pycharm创建的flask项目的话,不仅会默认生成这两个文件夹,还会生成一个app.py的入口程序。

3.Growing with Flask

这里会给大佬们提供一些高级的编写建议,在官方文档中有两个章节BecomingBigDesign Decisions in Flask 会有一些深入研究,有兴趣可以看看

4.Thread-Locals in Flask

4和5官方强调了说这是给有一定经验的程序员看的,刚开始接触可以忽略。

Flask在内部使用本地线程对象,保证即使是多个线程,自己的值也是互相隔离。 这里使用threading.local对象,用于为每个线程开辟一块空间来保存它独有的值。同样的Flask的上下文管理也参考了上面这种思想。

5.Develop for the Web with Caution

web开发中需要注意安全性问题,有关Flask安全问题的注意事项,可以参考:

Flask 中文文档( 1.1.1 )​dormousehole.readthedocs.io/en/latest/security.html

四.安装(Installation)

1.Python 版本(Python Version)

We recommend using the latest version of Python 3. Flask supports Python 3.5 and newer, Python 2.7, and PyPy.

2.依赖包(Dependencies)

这些依赖包是安装Flask时自动安装的,当用户pip install flask之后,通过pip list查看,会发现自动安装了Jinja和Werkzeug等依赖包

Jinja和Werkzeug之前已经基本介绍过,MarkupSafe和ItsDangerous都是安全性相关的,前者保证了使用Jinja模板不会遭到模板注入攻击,后者保证了session和cookie的安全,最后的Click是一个能使Flask使用命令行进行管理的工具

还有官方推荐的一些可选的依赖包,实际上会在开发过程中安装很多第三方包,这里根据需求自行选择

3.虚拟环境(Virtual environments)

为什么要使用虚拟环境?

随着拥有的Python项目越多,就越有可能需要使用不同版本的Python相关库,甚至Python本身。一个项目的新版本库可能会破坏另一个项目的兼容性。

官方这里给出了Python3和Python2一个安装虚拟环境的例子,Python3使用的是自带的venv库来创建,但是创建简单管理难,因此这里推荐另外两个创建虚拟环境的库,分别是virtualenv和virtualenvwrapper,推荐用virtualenvwrapper,统一管理,省去了切换路径和找不到自己之前环境的麻烦,配置一下环境变量即可,步骤如下:

(1)安装virtualenvwrapper

pip install virtualenvwrapper

(2)配置环境变量

默认虚拟环境统一在 /usr/envs中创建,也可以按下面自己配置指定文件夹

Mac 环境变量vim ~/.bash_profile
注意linux中是~/.bashrc
export WORKON_HOME=$HOME/myenv
export VIRTUALENVWRAPPER_PYTHON=/Library/Frameworks/Python.framework/Versions/3.8/bin/python3
source /Library/Frameworks/Python.framework/Versions/3.8/bin/virtualenvwrapper.sh
修改后需要source ~/.bash_profile编译一下

常用命令

1.退出当前虚拟环境

$deactivate

2.列出虚拟环境列表

$lsvirtualenv -b

3.切换虚拟环境

$workon env2

4.进入当前虚拟环境

$cdvirtualenv

5.删除虚拟环境

$rmvirtualenv env1

6.进入当前环境的site-packages

$cdsitepackages

7.查看环境中安装了哪些包

$lssitepackages

8.复制虚拟环境

$cpvirtualenv env1 env3

五.Quickstart

1.一个最简单的应用程序(A Minimal Application)

from flask import Flask 

app = Flask(__name__)

@app.route('/') 
def hello_world():

    return 'Hello, World!'

1.导入Flask类,然后创建一个Flask实例对象app,这里可能有人会对这个__name__参数产生疑问,当然在Flask类定义中有一大串注释来说这个问题,当然总结来说就是为了其他的扩展程序能够在调试过程中正确的访问到资源文件

.. admonition:: About the First Parameter

The idea of the first parameter is to give Flask an idea of what
belongs to your application. This name is used to find resources
on the filesystem, can be used by extensions to improve debugging
information and a lot more.

So it's important what you provide there. If you are using a single
module, `__name__` is always the correct value. If you however are
using a package, it's usually recommended to hardcode the name of
your package there.

For example if your application is defined in :file:`yourapplication/app.py`
you should create it with one of the two versions below::

app = Flask('yourapplication')
app = Flask(__name__.split('.')[0])

Why is that? The application will work even with `__name__`, thanks
to how resources are looked up. However it will make debugging more
painful. Certain extensions can make assumptions based on the
import name of your application. For example the Flask-SQLAlchemy
extension will look for the code in your application that triggered
an SQL query in debug mode. If the import name is not properly set
up, that debugging information is lost. (For example it would only
pick up SQL queries in `yourapplication.app` and not
`yourapplication.views.frontend`)

[email protected]()这个装饰器表示的是为一个视图函数创建一个URL路由,也就是我们访问URL地址后面跟着的“/”路径,而视图函数return的内容会显示到网页中。

注意一点模块起名不能为falsk.py,否则会产生冲突

根据服务器配置的不同,Flask的启动方式也多种多样,当然目前情况下有以下几种方式启动即可:

(1)IDE中直接运行app.py文件

(2)flask run方式

终端中:

$ export FLASK_APP=hello.py 
$ flask run

windows要用set配置环境变量:

C:\path\to\app>set FLASK_APP=hello.py

PowerShell:

PS C:\path\to\app> $env:FLASK_APP = "hello.py"

或者:

$ export FLASK_APP=hello.py $ python -m flask run

并且可以通过配置host实现全局访问

$ flask run --host=0.0.0.0

2.如果服务器没能启动(What to do if the Server does not Start)

根据报错信息分析

(1)Flask版本太低,低于0.11版本启动方式就不同了,建议升级Flask版本

(2)没有正确导入相关的包,比如没有导入Flask包的话就创建不了app对象

3.调试模式(Debug Mode)

在Flask中有两种模式,一种是调试模式,一种是生产模式,通常程序上线时使用生产模式,不能轻易改动,要根据发布版本重新部署,而调试模式就是在开发过程中使用的模式,可以热部署,支持随时修改,所以要根据场景配置合适的模式。

$ export FLASK_ENV=development 
$ flask run

或者设置FLASK_DEBUG=1也是可以进入调试模式的

4.路由(Routing)

路由函数作为装饰器绑定到视图函数上,从而把视图函数返回值连接到指定的界面上

1.普通路由

@app.route('/')
def index():
    return 'Index Page'

2.动态路由 (Variable Rules)

这里要注意动态路由的尖括号,以及需要在视图函数中传参,还要注意参数的类型

@app.route('/user/<username>') 
def show_user_profile(username):
    return 'User %s' % escape(username)

@app.route('/post/<int:post_id>') 
def show_post(post_id):
    return 'Post %d' % post_id

3.路由结尾带杠和不带杠的区别 (Unique URLs / Redirection Behavior)

(1)带杠的话,访问时加不加都可以访问到,因为带/的如果忘记在URL中写上,那么网络会自动重定向到带/的URL地址中

(2)不带杠的话,如果加杠访问会报404错

@app.route('/projects/') 
def projects():
    return 'The project page'

@app.route('/about') 
def about():
    return 'The about page'

4.url_for() (URL Building)

使用url_for()可以将url绑定到指定的视图函数中,需要注意的绑定的是视图函数名,而不是路由的路径名称。在这里还提到了url reversing和url hard-coding,建议使用类似url_for()这样的url reversing function。其实二者的区别可以理解为url hard-coding使用的是常量,而url reversing使用的是变量,常量在程序写好后无法实时更改,而变量可以根据实际情况修改或重新赋值。

url_for()的优点在官方文档中也被归纳出来:

1. 反转通常比硬编码url更具描述性。
2. 您可以一次性更改url,而不需要记住手动更改硬编码的url。
3.URL构建透明地处理特殊字符和Unicode数据的转义。
4. 生成的路径总是绝对的,避免了浏览器中相对路径的意外行为。
5. 如果您的应用程序被放在URL根目录之外,例如,在/myapplication而不是/中,url_for()可以为您正确地处理这个问题。

如果在没有请求的时候获取上下文,那么可以使用test_request_context()来测试一下url_for()生成的url

from flask import Flask, url_for 
from markupsafe import escape

app = Flask(__name__)

@app.route('/') 
def index():
    return 'index'

@app.route('/login') 
def login():
    return 'login'

@app.route('/user/<username>') 
def profile(username):
    return '{}\'s profile'.format(escape(username))

with app.test_request_context():
    print(url_for('index')) 
    print(url_for('login')) 
    print(url_for('login', next='/')) 
    print(url_for('profile', username='John Doe'))

结果如下:

/ 
/login 
/login?next=/ 
/user/John%20Doe

5.HTTP方法(HTTP Methods)

默认路由只接受GET请求,可以在路由的参数中添加http方法列表,有关http请求和响应的格式可以参考以下资料,解释一下什么是请求行,请求头,请求体,熟悉这些知识有助于后面对网络传输中遇到问题能够正确高效的进行分析

通过 Chrome浏览器 查看http请求报文

HTTP请求报文(请求行、请求头、请求体)

from flask import request

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        return do_the_login() 
    else:
        return show_the_login_form()

5.静态文件(Static Files)

静态文件包括了css、js、images等文件,都放在之前创建的static文件夹中。

6.模板渲染(Rendering Templates)

jinja提供了模板渲染功能,可以通过render_template()方法来实现,可以在参数中提供html文件名和需要渲染到模板中的变量,Flask的模板都默认存在templates文件夹中。

from flask import render_template

@app.route('/hello/') 

@app.route('/hello/<name>') 
def hello(name=None):
    return render_template('hello.html', name=name)

当然也可以使用模板继承,来使一些到大多数页面中共有的导航栏、页面底部、侧边栏信息的代码能够不重复出现在每个界面中。具体使用方法后面会介绍到。

7.访问请求数据(Accessing Request Data)

Flask中,通过全局request对象来实现对用户发送过来的请求作出响应,而在这过程中实现线程安全是通过本地上下文来实现的。

Request对象的方法可以具体参考API文档,这里介绍一些常用方法,

参考Flask request 属性详解:

  1. request.method:获取请求的方法
  2. request.form:拿到form表单中传递过来的值,使用字典格式取值
  3. request.args :#url:http://192.168.1.183:5000/login?a=1&b=2、返回值:{"a": "1", "b": "2"} ,request.args中保存的是url中传递的参数,也是字典格式
  4. request.values:values返回请求中的参数和form
  5. request.cookies:传递浏览器的cookies信息
  6. request.headres :请求headers信息,返回的结果是个list
  7. request.url、path、script_root、base_url、url_root:返回不同的url形式
  8. request.date、files:date是请求的数据,files随请求上传的文件

8.重定向和错误(Redirects and Errors)

redirect()会实现url的重定向跳转,而abort会中断请求返回状态码

from flask import abort, redirect, url_for

@app.route('/') 
def index():
    return redirect(url_for('login'))

@app.route('/login') 
def login():
    abort(401) 
    this_is_never_executed()

当然也可以定制错误界面

from flask import render_template

@app.errorhandler(404) 
def page_not_found(error):
     return render_template('page_not_found.html'), 404

9.Responses对象(About Responses)

视图函数的返回值会自动转换为Responses对象,根据返回值的不同会有以下几种情况:

1.如果返回值是一个字符串,它将被转换为一个响应对象,该字符串作为响应体,200 OK状态代码和一个文本/html mimetype。

2.如果返回值是dict,则调用jsonify()来生成响应。

3.如果返回一个元组,则元组中的项可以提供额外的信息。这些元组必须采用(响应、状态)、(响应、头)或(响应、状态、头)的形式。状态值将覆盖状态代码,标题可以是附加标题值的列表或字典。

10.APIs with JSON

编写一个API,在视图函数中返回一个dict,它将被转换为一个JSON响应。:

@app.route("/me") 
def me_api():
    user = get_current_user() 
    return {
        "username": user.username,
        "theme": user.theme,
        "image": url_for("user_image", filename=user.image), }

如果不是dict格式的话,可以使用jsonify()序列化成json格式

@app.route("/users") 
def users_api():
    users = get_all_users() 
    return jsonify([user.to_json() for user in users])

11.Sessions

session是在cookie之上实现的,并以加密方式对cookie签名。这意味着,用户可以查看cookie的内容,但不能修改它,除非他们知道用于签名的密钥。

为了使用session,需要设置一个secret key:

app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'

如何让系统生成一个随机secret key

$ python -c 'import os; print(os.urandom(16))' 
b'_5#y2L"F4Q8z\n\xec]/'

12.消息闪现(Message Flashing)

必须要设置secret key因为flash是基于session,在视图函数调用flash()函数,设置消息内容,flash()函数把消息存储在session中,在模板中使用全局函数get_flashed_messages()获取消息并将它显示出来。

@app.route('/flash')
def just_flash():
    flash('I am flash, who is looking for me?')
    return redirect(url_for('watchlist'))

因为同一个页面可能包含多条要显示的消息,所以这里使用for循环遍历get_flashed_message()返回的消息列表。

 <main>
     {% for message in get_flashed_messages() %}
         <div class="alert">{{ message }}</div>
     {% endfor %}
     {% block content %}{% endblock %}
 </main>

当get_flashed_message()函数被调用时,session中存储的所有消息都会被移除。如果这时刷新页面,会发现重载后的页面不再出现这条消息。

13.日志(Logging)

这里的logger就是标准的python logger,根据不同的级别记录不同的日志信息,例如:

app.logger.debug('A value for debugging')
app.logger.warning('A warning occurred (%d apples)', 42) 
app.logger.error('An error occurred')

14.Hooking in WSGI Middleware

使用中间件需要包装wsgi_app属性

from werkzeug.middleware.proxy_fix import ProxyFix 
app.wsgi_app = ProxyFix(app.wsgi_app)

15.Using Flask Extensions

例如Flask-SQLAlchemy等

16.Deploying to a Web Server

后面会单独介绍部署


到此为止官方文档有关快速入门部分就到次结束,总结来看的话,主要就是初步了解Flask是如何运行的,以及相关的配置和运作特性,不过每一部分具体的内容很多,后面会分开来逐步介绍,介绍一些概念时参考了网上其他人的总结资料。

发布于 2020-08-03 12:05