Python的Web框架之Flask(1)

Flask框架

为什么要学习Flask?

  • Flask自由、灵活,可扩展性强,第三方库的选择面广,开发时可以结合自己最喜欢用的轮子,也能结合最流行最强大的Python库
  • 入门简单,即便没有多少web开发经验,也能很快做出网站
  • 非常适用于小型网站
  • 非常适用于开发web服务的API
  • 开发大型网站无压力,但代码架构需要自己设计,开发成本取决于开发者的能力和经验
  • 各方面性能均等于或优于Django
    Django自带的或第三方的好评如潮的功能,Flask上总会找到与之类似第三方库
    Flask灵活开发,Python高手基本都会喜欢Flask,但对Django却可能褒贬不一
  • Flask与关系型数据库的配合使用不弱于Django,而其与NoSQL数据库的配合远远优于Django
  • Flask比Django更加Pythonic,与Python的philosophy更加吻合

简介

Flask诞生于2010年,是Armin ronacher(人名)用Python语言基于Werkzeug工具箱编写的轻量级Web开发框架。它主要面向需求简单的小应用。

Flask本身相当于一个内核,其他几乎所有的功能都要用到扩展(邮件扩展Flask-Mail,用户认证Flask-Login),都需要用第三方的扩展来实现。比如可以用Flask-extension加入ORM、窗体验证工具,文件上传、身份验证等。Flask没有默认使用的数据库,你可以选择MySQL,也可以用NoSQL。其 WSGI 工具箱采用 Werkzeug(路由模块) ,模板引擎则使用 Jinja2 。

可以说Flask框架的核心就是Werkzeug和Jinja2。

Python最出名的框架要数Django,此外还有Flask、Tornado等框架。虽然Flask不是最出名的框架,但是Flask应该算是最灵活的框架之一,这也是Flask受到广大开发者喜爱的原因。
Flask的Github地址:https://github.com/pallets/flask
Flask中文文档地址:http://docs.jinkan.org/docs/flask/

Flask入门程序示例(从Hello World开始)

虚拟环境

使用虚拟环境安装Flask,可以避免包的混乱和版本的冲突,虚拟环境是Python解释器的副本,在虚拟环境中你可以安装扩展包,为每个程序单独创建的虚拟环境,可以保证程序只能访问虚拟环境中的包。而不会影响系统中安装的全局Python解释器,从而保证全局解释器的整洁。

pip install virtualenv
pip install virtualenvwrapper-win

安装完虚拟环境后,如果提示找不到mkvirtualenv命令,须配置环境变量:

mkvirtualenv Flask_py

进入虚拟环境

workon Flask_py

退出虚拟环境

如果所在环境为真实环境,会提示deactivate:未找到命令

deactivate Flask_py

安装Flask

指定Flask版本安装
$ pip install flask==0.10.1

选择Flask框架然后指定工程的位置和python解释器环境,模板引擎选择Jinjia2模板的文件夹选择默认的templates文件夹


创建完之后,会自动生成相应的配置文件,运行app.run()

在浏览器中输入http://127.0.0.1:5000/

Flask目录结构

下图是flask项目的参考目录结构


由上图我们可以看出,pycharm为我们自动创建了static和templates两个文件夹,如有其他需求还要我们自行创建,static文件主要是存放静态文件,templates存放Jinjia2模板文件
接下来我们重点关注app.py文件

# 导入Flask类
from flask import Flask

#Flask类接收一个参数__name__
# __name__表示当前模块的名字
#模块名传入的意义
#flask以这个模块所在的目录为总目录
# 默认这个目录中的static文件夹为静态文件目录
# templates为 模板目录
import demo
app = Flask(__name__)

# 装饰器的作用是将路由映射到视图函数index

@app.route('/')
def index():
    """定义视图视图函数"""
    return 'Hello World!'

# Flask应用程序实例的run方法启动WEB服务器
# 如果当前模块作为启动模块, __name__ 就是 __main__
# 如果是导入这个模块当做包被导入,__name__ 是文件的名字
if __name__ == '__main__':
    app.run()

静态文件访问

在static文件夹下新建index.html,并且添加h1标题内容

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>Welcome to Flask World</h1>
</body>
</html>

然后我们访问http://127.0.0.1:5000/static/index.html就可以看到相应的效果

所有Flask程序必须有一个程序实例。

Flask调用视图函数后,会将视图函数的返回值作为响应的内容,返回给客户端。一般情况下,响应内容主要是字符串和状态码。

当客户端想要获取资源时,一般会通过浏览器发起HTTP请求。此时,Web服务器使用WSGI(Web Server Gateway Interface)协议,把来自客户端的所有请求都交给Flask程序实例。WSGI是为 Python 语言定义的Web服务器和Web应用程序之间的一种简单而通用的接口,它封装了接受HTTP请求、解析HTTP请求、发送HTTP,响应等等的这些底层的代码和操作,使开发者可以高效的编写Web应用。

程序实例使用Werkzeug来做路由分发(URL请求和视图函数之间的对应关系)。根据每个URL请求,找到具体的视图函数。 在Flask程序中,路由的实现一般是通过程序实例的route装饰器实现。route装饰器内部会调用add_url_route()方法实现路由注册。

调用视图函数,获取响应数据后,把数据传入HTML模板文件中,模板引擎负责渲染响应数据,然后由Flask返回响应数据给浏览器,最后浏览器处理返回的结果显示给客户端。

给路由传参示例:

有时我们需要将同一类URL映射到同一个视图函数处理,比如:使用同一个视图函数 来显示不同用户的个人信息。
在app.py中增加一个路由

# 路由传递的参数默认当做string处理,这里指定int,尖括号中冒号后面的内容是动态的
@app.route('/user/<int:id>')
def hellot(id):
    return 'hello 少年{}'.format(id)

返回状态码示例:

return后面可以自主定义状态码(即使这个状态码不存在)。当客户端的请求已经处理完成,由视图函数决定返回给客户端一个状态码,告知客户端这次请求的处理结果。


abort函数:

如果在视图函数执行过程中,出现了异常错误,我们可以使用abort函数立即终止视图函数的执行。通过abort函数,可以向前端返回一个http标准中存在的错误状态码,表示出现的错误信息。

使用abort抛出一个http标准中不存在的自定义的状态码,没有实际意义。如果abort函数被触发,其后面的语句将不会执行。其类似于python中raise。

from flask import Flask, abort

app = Flask(__name__)

@app.route('/')
def hello_world():
    abort(404)
    return 'Hello World!',999

捕获异常:

在Flask中通过装饰器来实现捕获异常,errorhandler()接收的参数为异常状态码。视图函数的参数,返回的是错误信息。

@app.errorhandler(404)
def error(e):
    return '您请求的页面不存在了,请确认后再次访问!%s'%e

重定向redirect示例

from flask import redirect
@app.route('/')
def hello2():
    return redirect('http://www.baidu.com')

正则URL示例:
正则URL是为了匹配指定的URL,而匹配指定的URL则可以达到限制访问,以及优化访问路径的目的。

from werkzeug.routing import BaseConverter

class Regex_url(BaseConverter):
    def __init__(self,url_map,*args):
        super(Regex_url,self).__init__(url_map)
        self.regex = args[0]
app.url_map.converters['re'] = Regex_url
@app.route('/user/<re("[a-z]{3}"):id>')
def hello_shaonian(id):
    return 'hello 少年{}'.format(id)

设置cookie和获取cookie

from flask import Flask,make_response
@app.route('/cookie')
def set_cookie():
    resp = make_response('this is to set cookie')
    resp.set_cookie('username', 'admin')
    return resp
from flask import Flask,request
#获取cookie
@app.route('/request')
def resp_cookie():
    resp = request.cookies.get('username')
    return resp

扩展

上下文:相当于一个容器,保存了Flask程序运行过程中的一些信息。

Flask中有两种上下文,请求上下文和应用上下文。

请求上下文(request context)
request和session都属于请求上下文对象。

request:封装了HTTP请求的内容,针对的是http请求。举例:user = request.args.get('user'),获取的是get请求的参数。

session:用来记录请求会话中的信息,针对的是用户信息。举例:session['name'] = user.id,可以记录用户信息。还可以通过session.get('name')获取用户信息。

应用上下文(application context)

current_app和g都属于应用上下文对象。

current_app:表示当前运行程序文件的程序实例。我们可以通过current_app.name打印出当前应用程序实例的名字。

如果说我们更改 app = Flask("dsadsadas") 这样的任意的字符串,按理来说Flask会去找dsadsadas这个模块所在目录为总目录,但是实际上并没有这个模块,所以flask找不到,找不到flask会默认当前这个启动文件所在的目录为总目录,总目录下的static文件夹为静态文件目录,所以访问http://127.0.0.1:5000/static/index.html仍然可以成功(注意如果测试字符串是abc,则找不到,因为标准库里有abc模块)

路由参数设置

我们使用app.url_map查看所有路由,下面演示一些路由设置,此外还有动态路由和定义请求方式路由

from flask import Flask
app = Flask(__name__)


# 路由
@app.route('/hello')
def index():
    """定义视图视图函数"""
    return 'hello'
# 同一视图函数对应多个装饰器都可以访问该视图函数
@app.route('/hi1')
@app.route('/hi2')
def hi():
    return 'hi i am here!'
# 查看所有路由
print(app.url_map)
if __name__ == '__main__':
    app.run()

Jinja2模板引擎

使用Flask的render_template渲染模板
在app.py中修改如下代码

from flask import Flask, render_template
app = Flask(__name__)

@app.route('/index')
def index():
    # 第一个参数数模板的名字,第二个参数是返回值,平铺的方式即可
     return render_template("index.html", name='鲁班七号', grade=15)
    # 2. Django 一般都是传入的字典,如果用类似方式可以这样
    # **data 代表解包的意思
    data = {
        "name": "安琪拉",
        "grade": 13
    }
    return render_template("index.html", **data)

if __name__ == '__main__':
    app.run()

在templates目录下新建index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>英雄:</h1>
<p>{{ name }}</p>
<h1>等级:</h1>
<p>{{ grade }}</p>
</body>
</html>

访问http://127.0.0.1:5000/index

Flask过滤器

safe:禁用转义;
<p>{{ '<em>hello</em>' | safe }}</p>
capitalize:把变量值的首字母转成大写,其余字母转小写;
<p>{{ 'hello' | capitalize }}</p>
lower:把值转成小写;
<p>{{ 'HELLO' | lower }}</p>
upper:把值转成大写;
<p>{{ 'hello' | upper }}</p>
title:把值中的每个单词的首字母都转成大写;
<p>{{ 'hello' | title }}</p>
trim:把值的首尾空格去掉;
<p>{{ ' hello world ' | trim }}</p>
reverse:字符串反转;
<p>{{ 'olleh' | reverse }}</p>
format:格式化输出;
<p>{{ '%s is %d' | format('name',17) }}</p>
striptags:渲染之前把值中所有的HTML标签都删掉;
<p>{{ '<em>hello</em>' | striptags }}</p>

支持链式使用过滤器

<p>{{ “ hello world “ | trim | upper }}</p>

在模板目录index.html下更新如下代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{#支持链式操作#}
welcome to {{ '     neuedu    '| trim | upper}}
</body>
</html>

Flask内置过滤器

Flask提供了一个内置过滤器”tojson”,它的作用是将变量输出为JSON字符串。这个在配合Javascript使用时非常有用

更新app.py文件 增加json数据

from flask import Flask, render_template
app = Flask(__name__)

@app.route('/index')
def index():
    data = '''
       {
       "name": "Python书籍",
       "origin_price": 66,
       "pub_date": "2018-4-14 17:00:00",
       "store": ["京东", "淘宝"],
       "author": ["张三", "李四", "Jhone"],
       "is_valid": true,
       "is_sale": false,
       "meta": {
           "isbn": "abc-123",
           "pages": 300
       },
       "desc": null
       }
       '''
    return render_template("index.html", data=data)

if __name__ == '__main__':
    app.run()

更新index.html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{#支持链式操作#}
welcome to {{ '     neuedu    '| trim | upper}}
<script>
    var data = {{ data | tojson |safe }}
    console.log(data)
</script>
</body>
</html>

打开浏览器控制台


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,542评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,822评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,912评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,449评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,500评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,370评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,193评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,074评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,505评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,722评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,841评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,569评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,168评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,783评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,918评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,962评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,781评论 2 354