程序员人生 网站导航

Flask Web 开发学习稿(一)

栏目:综合技术时间:2016-06-08 17:38:02

好久没有更新过了,把这些日子弄的东西先放1波出来
这个学习笔记未必会包括全部章节,有时会将两个章节进行合并

第1章 安装

1.1 使用虚拟环境

虚拟环境是 Python 解释器的1个私有副本,可以安装私有包而不影响全局 Python 解释器

Python 3.3 通过 venv 模块原生支持虚拟环境,命令为 pyvenv 。不过在 Python 3.3 中使用 pyvenv 命令创建的虚拟环境不包括 pip ,在 Python 3.4 中改进了这1缺点。

我这边使用 Python 3.5.0 和 Pycharm 来进行学习,所以暂时不使用这个,Pycharm 可以很好的解决各个项目的版本的虚拟环境问题。

1.2 使用 pip 安装 Python 包

使用 (venv) $ pip install flask 可以在虚拟环境中安装 Flask

第2章 程序的基本结构

2.1 初始化

Web 服务器使用1种名为 Web 服务器网关接口的协议,把接收自客户真个所有要求都转交给这个对象处理
Flask 类的构造函数只有1个必须指定的参数,即程序主模块或包的名字。Flask 用这个参数决定程序的根目录,以便找到相对程序根目录的资源文件位置。在大多数程序中,Python 的 __name__ 变量就是所需的值。

2.2 路由和视图函数

程序实例保存了1个 URL 到 Python 函数的映照关系,处理 URL 和函数之间关系的程序成为路由。
Flask 中定义路由最简单的方式是使用程序提供的 app.route 修饰器,把修饰的函数注册为路由。

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

修饰器是 Python 语言的标准特性,可使用不同的方式修改函数的行动

把 index 函数注册为程序跟地址的处理程序,不过在 Pthon 代码中嵌入响应字符串会致使代码难以保护,以后将会介绍生成响应的正确方法。像 index 这样的函数称为视图函数。
地址中也能够包括可变部份,可以在路由中定义可变的这部份

@app.route('/user/<name>') def user(name): return '<h1>Hello, %s!</h1>' % name

尖括号中的内容是动态部份,任何能匹配静态部份的 URL 都会映照到这个路由上。

2.3 - 2.4 启动服务器

用 run 方法来启动 Flask 集成的开发 Web 服务器

from flask import Flask app = Flask(__name__) @app.route('/') def index(): return '<h1>Hello World!</h1>' if __name__ == '__main__': app.run(debug=True)

使用__name__ == '__main__'是 Python 的惯用法,确保直接履行此脚本时才启动服务器,若其他程序调用该脚本可能父级程序会启动不同的服务器,就不履行 app.run 了
在 Pycharm 中启动后,可以看到 Flask 为我们启动了1个调试实例
1.png
通过阅读器访问,可以发现已成功的访问了,而且调试器中也记录下了这次访问
这里写图片描述
我们可以和前面的动态部份结合起来,变成1个根据不同地址显示不同页面的服务,可以看到服务器已记录下了两次访问,其中1次使用路径进来,响应中也带有相应信息,表示已成功
这里写图片描述
这里写图片描述

2.5 要求-响应循环

2.5.1 程序和要求上下文

为了不在需要使用访问对象时,都将其作为参数传入。Flask 使用上下文临时把某些对象变成全局可访问

from flask import Flask from flask import request app = Flask(__name__) @app.route('/') def index(): user_agent = request.headers.get('User-Agent') return '<p>Your Browser is %s!</p>' % user_agent if __name__ == '__main__': app.run(debug=True)

这里写图片描述
在这个视图函数中,我们把 request 当作全局变量使用,事实上 request 不多是全局变量,而 Flask 使用上下文让特定的变量在1个线程中全局可访问,与此同时还不会干扰其他线程。

线程是可单独管理的最小指令集,多线程 Web 服务器会创建1个线程池
再从线程池当选择1个线程用于处理接收到的要求

在 Flask 中有两种上下文:程序上下文和要求上下文,这两种上下文提供的变量以下:

变量名 上下文 说明
current_app 程序上下文 当前激活程序的程序实例
g 程序上下文 处理要求时用作临时存储的对象,每次要求都会重设这个变量
request 要求上下文 要求对象,封装了客户端发出的 HTTP 要求中的内容
session 要求上下文 用户绘画,用户存储要求之间需要“记住”的值的字典

Flask 会在分发要求之前激活程序和要求上下文,要求处理完成后再将其删除

2.5.2 要求调度

Flask 使用 app.route 修饰器或非修饰器情势的 app.add_url_rule() 生成映照
想要查看 Flask 程序中的 URL 映照是甚么模样,我们可以在 Python Shell 中检查为 hello》py 生成的映照

from hello import app app.url_map

2.5.3 要求钩子

有时在处理要求之前或以后履行代码会很有用,Flask 提供了注册通用函数的功能,注册的函数可在要求被分发到视图函数之前或以后调用。要求钩子使用修饰器实现。Flask 支持以下4种钩子:

钩子名 上下文
before_first_request 注册1个函数,在处理第1个要求之前运行
before_request 注册1个函数,在每次要求之前运行
after_request 注册1个函数,如果没有未处理的异常抛出,在每次要求后运行
teardown_request 注册1个函数,即便有未处理的异常抛出,也在每次要求后运行

Flask 会在分发要求之前激活程序和要求上下文,要求处理完成后再将其删除
在要求钩子函数和视图函数之间同享数据1般使用上下文全局变量g

2.5.4 响应

Flask 调用视图函数后,会将其返回值作为响应的内容。如果视图函数返回的响应需要使用不同的状态码,那末可以把数字代码作为第2个返回值,添加到响应文本以后。

from flask import Flask from flask import request app = Flask(__name__) @app.route('/') def index(): return '<p>Bad Request !</p>', 400 if __name__ == '__main__': app.run(debug=True)

可以看到阅读器中直接显示了 HTPP 400 毛病。视图函数返回的响应还可以接受第3个参数,这是1个由首部组成的字典,可以添加到 HTTP 响应中。
如果不想返回由多个值组成的元组,Flask 视图函数还可以返回 Response 对象。make_response() 函数可接受1/2/3个参数,并返回1个 Response 对象。

from flask import Flask from flask import make_response from flask import redirect app = Flask(__name__) @app.route('/') def index(): response = make_response('<h1>This document carries a cookie!</h1>') response.set_cookie('answer', '42') return response @app.route('/<name>') def user(name): return redirect('http://www.baidu.com') if __name__ == '__main__': app.run(debug=True)

可以看到,已被设置好了 Cookie
这里写图片描述
如果在地址中访问/txb则会被重定向到百度去
还有1种特殊的响应由 abort 函数生成,用于毛病处理

from flask import Flask from flask import make_response from flask import abort app = Flask(__name__) @app.route('/') def index(): response = make_response('<h1>This document carries a cookie!</h1>') response.set_cookie('answer', '42') return response @app.route('/get/<id>') def get_id(id): if id != '9': abort(404) return '<h1>Hello, %s!</h1>' % id if __name__ == '__main__': app.run(debug=True)

通过访问可以发现,只有在访问/get/9时才能正确得到 Hello ,若是输入其他返回为 404 页面
abort 不会把控制权交还给调用它的函数,而是抛出异常把控制权交给 Web 服务器

2.6 Flask 扩大

Flask 的开发 Web 服务器支持很多启动设置选项,但只能在脚本中作为参数传给 app.run() 函数,这类方式其实不10分方便,传递设置选项的理想方式是使用命令行参数。
Flask-Script 是1个 Flask 扩大,为 Flask 程序添加了1个命令行解析器。

from flask import Flask from flask import make_response from flask import redirect from flask import abort from flask.ext.script import Manager app = Flask(__name__) manager = Manager(app) @app.route('/') def index(): response = make_response('<h1>This document carries a cookie!</h1>') response.set_cookie('answer', '42') return response @app.route('/user/<name>') def user(name): return redirect('http://www.baidu.com') @app.route('/get/<id>') def get_id(id): if id != '9': abort(404) return '<h1>Hello, %s!</h1>' % id if __name__ == '__main__': manager.run()

这个扩大的初始化方法为:把程序实例作为参数传给构造函数,初始化主类的实例。创建的对象可以在各个扩大中使用。在这里,服务器由 manager.run() 启动,启动后就可以解析命令行了。
在命令行中启动试试看:
这里写图片描述
Shell 命令用于在程序的上下文中启动 Python Shell 会话,可以用来运行保护任务或测试,还可以调试异常
runserver 命令用来启动 Web 服务器

第3章 模版

视图函数的作用很明确,即生成要求的响应
模版是1个包括响应文本的文件,其中包括用占位符变量表示的动态部份

3.1 Jinja 2 模版引擎

3.1.1 渲染模版

默许情况下,Flask 在程叙文件夹中的 templates 子文件夹中寻觅模版

from flask import Flask from flask import render_template app = Flask(__name__) @app.route('/') def index(): return render_template('index.html') @app.route('/user/<name>') def user(name): return render_template('user.html', name=name) if __name__ == '__main__': app.run()

这段代码可以和上面实现相同的作用,render_template()函数的第1个参数是模版的文件名,随后的参数都是键值对,表示模版中变量对应的真实值

3.1.2 变量

模版中使用的{{ name }}结构表示1个变量,它是1种特殊的占位符,告知模版引擎这个位置的值从渲染模版时使用的数据中获得
Jinja2 能辨认所有类型的变量,乃至是1些复杂的类型,例如列表、字典和对象,例如

<p>A value from a dictionary: {{ mydict['key'] }}.</p> <p>A value froma list: {{ mylist[3] }}.</p> <p>A value froma list, with a variable index: {{ mylist[myintvar] }}.</p> <p>A value froman object's method: {{ myobj.somemethod() }}.</p>

下面这个例子就是读取这些复杂类型的

from flask import Flask from flask import render_template app = Flask(__name__) class Human(): def somemethod(self): return 'Flask say Hello world!' @app.route('/<name>') def user(name): mydict = {"key": "This is a Flask Program"} mylist = ['it', 'Hello', 'the', 'world'] myintvar = 0 myobj = Human() return render_template('user.html', name=name, mydict=mydict, mylist=mylist, myintvar=myintvar, myobj=myobj) if __name__ == '__main__': app.run()

也能够使用过滤器来修改变量,过滤器名添加在变量名以后,中间使用竖线分隔

过滤器名 说明
safe 渲染值时不转义
capitalize 把值的首字母转换成大写,其他字母转换成小写
lower 把值转换成小写情势
upper 把值转换成大写情势
title 把值中的每一个单词的首字母都变成大写
trim 把值的首尾空格去掉
striptags 渲染前把值中的 HTML 标签都删掉

默许情况出于安全斟酌, Jinja2 会转义所有变量,完全的过滤器可去 Jinja2 的文档中查看

3.1.3 控制结构

Jinja2 提供了多种控制结构,可用来改变模版的渲染流程

{{ % if user %}} hello, {{ user }} {{% else %}} hello, world {{% endif % }}

是条件控制结构

{% for comment in comments %} <li>{{ comment }}</li> {% endfor %}

是循环控制结构, Jinja2 还支持宏,宏类似于 Python 代码中的函数

from flask import Flask from flask import render_template app = Flask(__name__) @app.route('/macro') def macro(): comments = ['it', 'Hello', 'the', 'world'] return render_template('macro.html', comments=comments) if __name__ == '__main__': app.run()

在响应模版macro.html

{% macro render_comment(comment) %} <li>{{comment}}</li> {% endmacro %} <ul> {% for comment in comments%} {{ macro.render_comment(comment) }} {% endfor %} </ul>

奇怪的是这个例子我1直返回的是500毛病,没有成功
为了提高重用性,可以把重复使用的部份保存在单独的文件中,在需要使用的模版中导入便可

from flask import Flask from flask import render_template app = Flask(__name__) @app.route('/comments') def comments(): comments = ["it", "Hello", "the", "world"] return render_template('comments.html', comments=comments) if __name__ == '__main__': app.run()

macro.html

{% macro render_comment(comment) %} <li>{{comment}}</li> {% endmacro %}

comments.html

{% import 'macro.html' as macro %} <ul> {% for comment in comments%} {{ macro.render_comment(comment) }} {% endfor %} </ul>

提高重用性的更好方式是模版继承,就像面向对象中的继承类似,首先创建1个base.html的基模版

<html> <head> {% block head%} <title> {% block title%}{% endblock%}- My Application </title> {% endblock %} </head> <body> {% block body%} {% endblock%} </body> </html>

衍生模版为child.html

{% extends 'base.html'%} {% block title%} Index {% endblock %} {% block head%} {{ super() }} <style> </style> {% endblock%} {% block body%} <h1>Helll, World!</h1> {% endblock%}

在 Python 文件中

from flask import Flask from flask import render_template app = Flask(__name__) @app.route('/extends') def extends(): return render_template('child.html') if __name__ == '__main__': app.run()

extends指令声明这个模版衍生自base.html,在此指令后基模版的3个块被重新定义,模版引擎会将其插入适当的位置,使用super()来获得原来的内容

3.2 Flask-Bootstrap

from flask.ext.bootstrap import Bootstrap from flask import Flask from flask import render_template app = Flask(__name__) bootstrap = Bootstrap(app) @app.route('/bootstrap/<name>') def bootstrap(name): return render_template('bootstrap.html', name=name) if __name__ == '__main__': app.run()

bootstrap.html

{% extends 'bootstrap/base.html'%} {% block title%} Flasky {% endblock %} {% block navbar%} <div class="navbar navbar-inverse" role="navigation"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="/">Flasky</a> </div> <div class="navbar-collapse collapse"> <ul class="nav navbar-nav"> <li><a href="/">Home</a></li> </ul> </div> </div> </div> {% endblock %} {% block content%} <div class="container"> <div class="page-header"> <h1>Hello, {{ name }}!</h1> </div> </div> {% endblock %}

可以看到,访问已带有其他样式了,这样就构建了1个“好看”的网页。Jinja2 中的extends指令从 Flask-Bootstrap 中导入bootstrap/base.html,从而实现模版继承。Flask-Bootstrap 中的基模版提供了1个网页框架,引入了 Bootstrap 中的所有 CSS 和 JavaScript 文件
Flask-Bootstrap 的基模版中定义了可在衍生模版中重定义的块,block 和 endblock 指令定义的块中的内容可添加到基模版中

块名 说明
doc 全部 HTML 文档
html_attribs 标签中的属性
html 标签中的内容
head 标签中的内容
title 标签中的内容
metas 1组标签
styles 层叠样式表定义
body_attribs 标签的属性
body 标签中的内容
navbar 用户定义的导航条
content 用户定义的页面内容
scripts 文档底部的 JavaScript 声明

上表中的很多块都是 Flask-Bootstrap 自用的,最好不要重新定义,Bootstrap 所需的文件在 styles 和 scripts 块中声明,如果程序需要向已有内容的块中添加新内容,必须使用 Jinja2 提供的super()函数,例如添加1个新的 JavaScript 文件

{% block scripts %} {{ super() }} <script type="text/javascript" src="my-script.js"></script> {% endblock %}

3.3 自定义毛病页面

from flask.ext.bootstrap import Bootstrap from flask import Flask from flask import render_template app = Flask(__name__) bootstrap = Bootstrap(app) @app.route('/user/<name>') def bootstrap(name): return render_template('bootstrap.html', name=name) @app.errorhandler(404) def page_not_found(e): return render_template('404.html'), 404 @app.errorhandler(500) def internal_server_error(e): return render_template('500.html'), 500 if __name__ == '__main__': app.run()

对已有页面进行复制粘贴的修改肯定可行
这里写图片描述
不过我们想通过更好的方法(模版继承)来实现,我们定义1个基模版base.html

{% extends "bootstrap/base.html" %} {% block title %}Flasky{% endblock %} {% block navbar %} <div class="navbar navbar-inverse" role="navigation"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="/">Flasky</a> </div> <div class="navbar-collapse collapse"> <ul class="nav navbar-nav"> <li><a href="/">Home</a></li> </ul> </div> </div> </div> {% endblock %} {% block content %} <div class="container"> {% block page_content %}{% endblock %} </div> {% endblock %}

这个模版的content块中只有1个

容器,其中包括了1个名为page_content的新的空块,块中内容由衍生模版定义,这样 404.html 就没必要基于 Flask-Bootstrap 的基模版了,只需要继承自 base.html 便可。之前的 user.html 也能够通过这个继承页面来进行简化

{% extends 'base.html'%} {% block title%} 欢迎页面 {% endblock%} {% block content %} <div class="page-header"> <h1> 欢迎,{{ name }}! </h1> </div> {% endblock%}

3.4 链接

Flask 提供了url_for()辅助函数,它可使程序 URL 映照中保存的信息生成 URL
url_for()函数最简单的用法是以视图函数名作为参数,返回对应的 URL,

3.5 静态文件

对静态文件的援用被当做1个特殊的路由,即/static/<filename>
index.html中加入以下

{% block head %} {{ super() }} <link rel="shortcut icon" href="{{ url_for('static', filename = 'favicon.ico') }}" type="image/x-icon"> <link rel="icon" href="{{ url_for('static', filename = 'favicon.ico') }}" type="image/x-icon"> {% endblock %}

在网页查看源码可以发现,如果存在/static/<filename>即会被加载

------分隔线----------------------------
------分隔线----------------------------

最新技术推荐