赞
踩
Flask是一个基于Python的Web开发框架,它以灵活、微框架著称,Flask本身不具备太多功能,但是通过丰富的第三方插件,可以轻松地应对现实开发中复杂的需求。
学习Flask框架,我们可以开发一个服务器,与前端进行交互
Flask有3个主要依赖:路由、调试和Web服务器网关接口(WSGI,Web server gateway interface)子系统由Werkzeug提供;模板系统由Jinja2提供;命令行集成由Click提供。这些依赖全都是Flask的开发者Armin Ronacher开发的。
读者可以先查看下计算机中现有的Python版本,按照系统来分,可以用以下方式查看:
从图可以看到Windows系统安装的Python版本是3.10.2,读者可以查看下自己的计算机上安装的Python版本,建议最好安装python3.6以上的版本。
可以到官网:https://www.python.org/下载最新版本的Python,下载后直接安装即可。
安装Flask 非常简单,只要在系统的终端软件中输入以下命令,然后按Enter键即可安装。
$ pip install flask
许多软件都可以用来开发Flask项目,如Sublime Text、Visual Studio Code等,但是最专业的软件还是PyCharm。PyCharm是一个集成开发环境(integrated development environment,简称IDE),它提供了许多方便快捷的功能,如断点调试、版本控制等
PyCharm是JetBrains公司出品的一款专门针对Python编程的软件,它有两大版本:一个是PyCharm Professional,即专业版;另一个是PyCharm Community,即社区版,这两大版本的主要区别如下。
我们需要开发Flask项目,所以选择PyCharm Professional版本。关于它的收费问题,如果读者是学生,可以用学校提供的教育邮箱账号(一般以edu.cn结尾)去申请免费授权(申请网址: https://www.jetbrains.com/community/education/#students)。
如果读者是企业开发者,可以跟公司申请购买正版授权。如果您既不是学生又不想购买正版PyCharm Professional版本,则可以退而求其次选择PyCharm Community版本,也完全可以学习本书的内容,只是一些代码提示没有那么智能(PyCharm Professional有30天试用期)。
所有Flask应用都必须创建一个应用实例。Web服务器使用一种名为Web服务器网关接口(WSGI,Web server gateway interface,读作“wiz-ghee”)的协议,把接收自客户端的所有请求都转交给这个对象处理。应用实例是Flask类的对象
from flask import Flask
app = Flask(__name__)
Flask类的构造函数只有一个必须指定的参数,即应用主模块或包的名称。在大多数应用中,Python的__name__变量就是所需的值。
Flask用_name_参数确定应用的位置,进而找到应用中其他文件的位置,例如图像和模板
客户端(例如Web浏览器)把请求发送给Web服务器,Web服务器再把请求发送给Flask应用实例。应用实例需要知道对每个URL的请求要运行哪些代码,所以保存了一个URL到Python函数的映射关系。处理URL和函数之间关系的程序称为路由。
在Flask应用中定义路由的最简便方式,是使用应用实例提供的app.route装饰器。下面的例子说明了如何使用这个装饰器声明路由:
@app.route('/')
def index():
return 'Hello World!'
装饰器是Python语言的标准特性。惯常用法是把函数注册为事件处理程序,在特定事件发生时调用。
index()这样处理入站请求的函数称为视图函数。如果应用部署在域名为www.example.com的服务器上,在浏览器中访问http://www.example.com后,会触发服务器执行index()函数。这个函数的返回值称为响应,是客户端接收到的内容。如果客户端是Web浏览器,响应就是显示给用户查看的文档。视图函数返回的响应可以是包含HTML的简单字符串。
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return '<h1>Hello World!</h1>'
绝大部分网站都不可能只有首页一个页面,以一个最简单的博客网站为例,博客页面相关的有博客列表、博客详情等,用户页面相关的有注册、登录、个人中心等。
所以在制作网站时,需要定义许多不同的URL来满足不同页面的访问需求,而URL总体上来讲又分为两种,第一种是无参数的URL,第二种是有参数的URL,下面分别进行讲解这两种URL的定义。
无参数URL是指在URL定义的过程中,不需要定义参数。这里以个人中心为例,如定义个人中心的URL为/profile,可以使用以下代码实现。
@app.route('/profile')
def profile():
return '这是个人中心'
有些时候,在访问某个URL时需要携带一些参数。如获取博客详情时,需要把博客的id传过去,那么博客详情的URL可能为/blog/13,其中13为博客的id。假如获取第10页的博客列表,那么博客列表的URL可能为/blog/list/10,其中10为页码。
在Flask中,如果URL中携带了参数,那么视图函数也必须定义相应的形参来接收URL中的参数。这里以博客详情的URL为例,示例代码如下:
@app.route("/blog/<blog_id>")
def blog_detail(blog_id):
return "您查找的博客id为:"%blog_id
通过以上代码可以看到,URL中多了一对尖括号,并且尖括号中多了一个blog_id,这个blog_id就是参数。然后在视图函数blog_detail中,也相应定义了一个blog_id的形参,当浏览器访问这个URL时,Flask接收到请求后,会自动解析URL中的参数blog_id,把它传给视图函数blog_detail,在视图函数中,开发者就可以根据blog_id从数据库中查找到具体的博客数据返回给浏览器。
URL中的参数类型
这里着重讲解参数类型any的使用。例如,现在要实现一个获取某个分类的博客列表,但是博客分类只能是python、flask、django之一,用any就可以轻松实现。
@app.route("/blog/list/<any(python,flask,django):category>")
def blog_list_with_category(category):
return "您获取的博客分类为:%s"%category
在浏览器中访问/blog/list/python,因为博客分类python被包含在了备选值中,所以可以正常显示内容
开发了一个简单的Flask应用之后,你或许希望进一步了解Flask的工作方式。下面将介绍这个框架的一些设计理念。
Flask从客户端收到请求时,要让视图函数能访问一些对象,这样才能处理请求。请求对象就是一个很好的例子,它封装了客户端发送的HTTP请求。
要想让视图函数能够访问请求对象,一种直截了当的方式是将其作为参数传入视图函数,不过这会导致应用中的每个视图函数都多出一个参数。除了访问请求对象,如果视图函数在处理请求时还要访问其他对象,情况会变得更糟。
为了避免大量可有可无的参数把视图函数弄得一团糟,Flask使用上下文临时把某些对象变为全局可访问。有了上下文,便可以像下面这样编写视图函数:
from flask import request
@app.route('/')
def index():
user_agent = request.headers.get('User-Agent')
return '<p>Your browser is {}</p>'.format(user_agent)
注意,在这个视图函数中我们把request当作全局变量使用。事实上,request不可能是全局变量。试想,在多线程服务器中,多个线程同时处理不同客户端发送的不同请求时,每个线程看到的request对象必然不同。Flask使用上下文让特定的变量在一个线程中全局可访问,与此同时却不会干扰其他线程。
线程是可单独管理的最小指令集。进程经常使用多个活动线程,有时还会共享内存或文件句柄等资源。多线程Web服务器会创建一个线程池,再从线程池中选择一个线程处理接收到的请求。
在Flask中有两种上下文:应用上下文和请求上下文。
Flask上下文全局变量
Flask在分派请求之前激活(或推送)应用和请求上下文,请求处理完成后再将其删除。应用上下文被推送后,就可以在当前线程中使用current_app和g变量。类似地,请求上下文被推送后,就可以使用request和session变量。如果使用这些变量时没有激活应用上下文或请求上下文,就会导致错误
下述Python shell会话演示了应用上下文的使用方法:
>>> from hello import app
>>> from flask import current_app
>>> current_app.name
Traceback (most recent call last):
...
RuntimeError: working outside of application context
>>> app_ctx = app.app_context()
>>> app_ctx.push()
>>> current_app.name
'hello'
>>> app_ctx.pop()
在这个例子中,没激活应用上下文之前就调用current_app.name会导致错误,但推送完上下文之后就可以调用了。注意,获取应用上下文的方法是在应用实例上调用app.app_context()。
Flask通过上下文变量request对外开放请求对象。这个对象非常有用,包含客户端发送的HTTP请求的全部信息。Flask请求对象中最常用的属性和方法见表
Flask请求对象
有时在处理请求之前或之后执行代码会很有用。例如,在请求开始时,我们可能需要创建数据库连接或者验证发起请求的用户身份。为了避免在每个视图函数中都重复编写代码,Flask提供了注册通用函数的功能,注册的函数可在请求被分派到视图函数之前或之后调用。
请求钩子通过装饰器实现。Flask支持以下4种钩子。
- before_request
注册一个函数,在每次请求之前运行,实例代码如下:
@app.before_first_request
def first_request():
print ('first time request')
- before_first_request
注册一个函数,只在处理第一个请求之前运行。可以通过这个钩子添加服务器初始化任务。实例代码如下:
@app.before_request
def before_request():
if not hasattr(g,'user'):
setattr(g,'user','xxxx')
- after_request
注册一个函数,如果没有未处理的异常抛出,在每次请求之后运行。
- teardown_request
注册一个函数,即使有未处理的异常抛出,也在每次请求之后运行。实例代码如下:
@app.teardown_appcontext
def teardown(exc=None):
if exc is None:
db.session.commit()
else:
db.session.rollback()
在请求钩子函数和视图函数之间共享数据一般使用上下文全局变量g。例如,before_request处理程序可以从数据库中加载已登录用户,并将其保存到g.user中。随后调用视图函数时,便可以通过g.user获取用户。
在Flask项目中,如果要获取客户端提交上来的数据,可以通过全局线程安全对象flask.request实现。flask.request对象封装了许多属性和方法,常用的属性和方法如表
常用的属性和方法
flask.request对象完整的属性和方法,请参考Flask官方文档:https://flask.palletsprojects.com/en/2.0.x/api/#incoming-request-data。
页面重定向,下文简称重定向。重定向在页面中体现的操作是,浏览器会从一个页面自动跳转到另外一个页面。例如,用户访问了一个需要权限的页面,但是该用户当前并没有登录,因此重定向到登录页面。重定向分为永久性重定向和暂时性重定向。
在Flask中,重定向是通过flask.redirect(location,code=302)函数来实现的,其中location表示需要重定向到哪个URL,code代表状态码,默认是302,即暂时性重定向。
下面用一个简单的案例来说明这个函数的用法。
from flask import Flask,url_for,redirect
app = Flask(__name__)
@app.route('/login')
def login():
return 'login page'
@app.route('/profile')
def profile():
name = request.args.get('name')
if not name:
# 如果没有name,说明没有登录,重定向到登录页面
return redirect("/login")
else:
return name
从以上代码可看出,在访问/profile时,如果没有通过查询字符串的方式传递name参数,那么就会被重定向到/login。如访问/profile?name=admin可以看到,浏览器中显示admin,但是如果直接访问/profile,就会被重定向到/login。读者可自行尝试。
前面介绍的视图函数返回的都是一个字符串,而在实际网站开发中,为了让网页更加美观,需要渲染一个有富文本标签的页面,通常包含大量的HTML代码,如果把这些HTML代码用字符串的形式写在视图函数中,后期的代码维护将变成一场噩梦。
因此,在Flask中,渲染HTML通常会交给模板引擎来实现,而Flask中默认配套的模板引擎是Jinja2,Jinja2是一个高效、可扩展的模板引擎。Jinja2可以独立于Flask使用,如被Django使用。
首先在templates文件夹下创建index.html文件,然后输入以下代码。
在使用PyCharm Professional版创建完一个Flask项目后,默认会生成一个templates文件夹,如果没有修改模板查找路径,默认会在这个文件夹下寻找模板文件。模板文件可以是任意纯文本格式的文件,如TXT、HTML、XML等,但是为了让项目更规范,也为了与前端开发者更无缝地协作,一般都是用HTML文件来写模板代码。
注意:如果读者用的是非PyCharm Professional版创建的Flask项目,则可以手动创建templates文件夹。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1>这是首页</h1>
</body>
</html>
如果学过HTML的话,想必读者对这段代码就不陌生了,没错,这就是HTML的基本语法。如果读者不会HTML语法也不要慌张,这里主要只是介绍模板的基本用法,读者可以直接复制粘贴代码先用起来。后期作者也会更新相关的教程。
接下来在视图函数中使用render_template函数渲染index.html模板。在app.py中,将原来的hello_world视图函数修改为以下代码。
from flask import Flask,render_template
...
@app.route('/')
def index():
return render_template("index.html")
render_template默认会从当前项目的templates文件夹下寻找index.html文件,读取后进行解析,再渲染成HTML代码返回给浏览器。
在浏览器中访问http://127.0.0.1:5000,可以看到如图所示的效果。
从图中可以看到,“这是首页”4个字已经是一级标题了,原因是模板中给“这是首页”4个字外面套了一个h1标签,至此我们就完成了一个最简单的模板渲染。
HTML文件中的有些数据是需要动态地从数据库中加载的,不能直接在HTML中写死。一般的做法是,在视图函数中把数据先提取好,然后使用render_template渲染模板时传给模板,模板再读取并渲染出来。
在模板中使用的{{ name }}结构表示一个变量,这是一种特殊的占位符,告诉模板引擎这个位置的值从渲染模板时使用的数据中获取。
示例代码如下:
@app.route("/variable")
def variable():
hobby = "游戏"
return render_template("variable.html",hobby=hobby)
以上代码中渲染了一个variable.html模板,这个模板文件的创建接下来会具体讲解。除模板名称外,还给render_template传递了一个hobby关键字参数,后续在模板中就可以使用这个变量了。
现在再在templates文件夹下创建一个variable.html模板文件(注意:要记得先删掉template_folder参数),然后输入以下代码。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>变量使用</title>
</head>
<body>
<h1>我的兴趣爱好是:{{ hobby }}</h1>
</body>
</html>
从以上代码中可以看到,把变量放到两对花括号中即可使用变量。“游戏”从视图函数中通过render_template传过去的,并不是在HTML中写死的,所以变量的使用可以让同一个HTML模板渲染无数个不同的页面。
在Python中,如果需要对某个变量进行处理,可以通过函数来实现,而在模板中,则是通过过滤器来实现的。过滤器本质上也是函数,但是在模板中使用的方式是通过管道符号(|)来调用的。
例如,有一个字符串类型的变量name,要获取它的长度,可以通过{{ name|length }}来获取,Jinja2会把name当作第1个参数传给length过滤器底层对应的函数。
length是Jinja2内置好的过滤器,Jinja2中内置了许多好用的过滤器,如果内置的过滤器不能满足需求,还可以自定义过滤器。
Jinja2中内置过滤器如下。
<div>default过滤器:{{ user|default('admin') }}</div>
如果user没有定义,那么将会使用admin作为默认的值。再看以下示例。
<div>default过滤器:{{ ""|default('admin',boolean=True) }}</div>
因为" "(空字符串)在使用if判断时,返回的是False,这时如果要使用默认值admin,就必须加上boolean=True参数。
escape(value):将一些特殊字符,如&、<、>、"、'进行转义。因为Jinja2默认开启了全局转义,所以在大部分情况下无须手动使用这个过滤器去转义,只有在关闭了转义的情况下,会需要使用到它。
filesizeformat(value,binary=False):将值格式化成方便阅读的单位。如13KB、4.1MB、102Bytes等。默认是Mega、Giga,也就是每个相邻单位换算是1000倍。如果第2个参数设置为True,那么相邻单位换算是1024倍。
first(value):返回value序列的第1个元素。
float(value,default=0.0):将value转换为浮点类型,如果转换失败会返回0.0。(7)format(value,*args,**kwargs):格式化字符串,示例代码如下。
{{ "%s,%s"|format(greeting,name) }}
内置过滤器还有很多,读者可自行去官方文档上查找,上文有链接
Jinja2提供了多种控制结构,可用来改变模板的渲染流程。本节通过简单的例子介绍其中最有用的一些控制结构。
下面这个例子展示如何在模板中使用条件判断语句:
{% if user %}
Hello, {{ user }}!
{% else %}
Hello, Stranger!
{% endif %}
另一种常见需求是在模板中渲染一组元素。下例展示了如何使用for循环实现这一需求:
<ul>
{% for comment in comments %}
<li>{{ comment }}</li>
{% endfor %}
</ul>
Jinja2还支持宏。宏类似于Python代码中的函数。例如:
{% macro render_comment(comment) %}
<li>{{ comment }}</li>
{% endmacro %}
<ul>
{% for comment in comments %}
{{ render_comment(comment) }}
{% endfor %}
</ul>
为了重复使用宏,可以把宏保存在单独的文件中,然后在需要使用的模板中导入:
{% import 'macros.html' as macros %}
<ul>
{% for comment in comments %}
{{ macros.render_comment(comment) }}
{% endfor %}
</ul>
需要在多处重复使用的模板代码片段可以写入单独的文件,再引入所有模板中,以避免重复:
{% include 'common.html' %}
另一种重复使用代码的强大方式是模板继承,这类似于Python代码中的类继承。首先,创建一个名为base.html的基模板:
<html>
<head>
{% block head %}
<title>{% block title %}{% endblock %} - My Application</title>
{% endblock %}
</head>
<body>
{% block body %}
{% endblock %}
</body>
</html>
基模板中定义的区块可在衍生模板中覆盖。Jinja2使用block和endblock指令在基模板中定义内容区块。在本例中,我们定义了名为head、title和body的区块。注意,title包含在head中。下面这个示例是基模板的衍生模板:
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
{{ super() }}
<style>
</style>
{% endblock %}
{% block body %}
<h1>Hello, World!</h1>
{% endblock %}
extends指令声明这个模板衍生自base.html。在extends指令之后,基模板中的3个区块被重新定义,模板引擎会将其插入适当的位置。如果基模板和衍生模板中的同名区块中都有内容,衍生模板中的内容将显示出来。在衍生模板的区块里可以调用super(),引用基模板中同名区块里的内容。上例中的head区块就是这么做的。
到此,如果你认真读完了本篇文章,并进行了简单的实现,那么恭喜你,你已经是一名合格的Flask框架的使用者了。
如果你还想继续深入研究的话,那么请持续关注博主,博主后期会持续更新Flask框架与表单的结合,Flask框架与数据库的结合。以及通过实践项目来巩固掌握的知识点。
如果你对前端感兴趣的话,博主后期也会更新HTML,CSS,JavaScript,以及Jquery,bootstrap等前端框架相关内容。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。