1、Flask框架简介
- Django:1个重武器,包含了web开发中常用的功能、组件的框架;(ORM、Session、Form、Admin、分页、中间件、信号、缓存、ContenType....);
- Tornado(知乎):2大特性就是异步非阻塞、原生支持WebSocket协议;
- Flask:功能和性能虽然不及Django和Tornado,但是Flask的第三方开源组件比丰富:http://flask.pocoo.org/extensions/
- Bottle:比较简单;
- web.py、web2py、Quixote(豆瓣)……
总结:小型web应用设计的功能点不多使用Flask; 大型web应用设计的功能点比较多使用的组件也会比较多,使用Django(自带功能多不用去找插件); 如果追求性能可以考虑Tornado;
2、安装Python
官网下载地址:https://www.python.org/downloads/
特别要注意选上pip
和Add python.exe to Path
,然后一路点“Next”即可完成安装
3、安装Flask
Flask的socket是基于Werkzeug 实现的,模板语言依赖jinja2模板,在使用Flask之前需要安装一下:
pip install flask //建议用 PyCharm 编写代码`
如果要安装python3可用的flask,通过pip3 install flask
4、程序的基本结构
一个完整的flask程序(index.py)
<!-- 引入Flask类,Flask类实现了一个WSGI应用--> from flask import Flask <!--app是Flask的实例,它接收程序主模块或包的名字作为参数,但一般都是传递__name__。--> app = Flask(__name__) <!--路由:使用app.route装饰器处理URL和视图函数--> @app.route('/') def index(): return '<h1>Hello World!</h1>' <!--确保直接执行这个脚本时才启动开发Web服务器,其他文件引用该文件时(如"from hello import app"”")不会执行执行app.run函数--> if __name__ == '__main__': <!--执行app.run就可以启动服务了。默认监听127.0.0.1:5000--> app.run(debug=True) <!--可配置端口, 0.0.0.0表示监听所有地址,可在外部访问--> # app.run(debug=True, port=5001, host='0.0.0.0')
- Flask中有两种上下文:程序上下文和请求上下文
变量名 | 上下文 | 说明 |
---|---|---|
current_app | 程序上下文 | 当前激活程序的程序实例 |
g | 程序上下文 | 处理请求时用作临时存储对象,每次请求都会重设这个变量 |
request | 请求上下文 | 请求对象,封装了客户端发出的HTTP请求中的内容 |
session | 请求上下文 | 用户会话,用于存储请求之间需要“记住”的值的词典 |
# 一个request例子
from flask import request,Flask
app = Flask(__name__)
@app.route('/')
def index():
user_agent = request.headers.get('User-Agent')
return '<p>Your browser is %s</p>' % user_agent
<!--Your browser is Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36-->
<!--request.headers:-->
<!--Your browser is Host: 127.0.0.1:5000 -->
<!--Connection: keep-alive -->
<!--Pragma: no-cache -->
<!--Cache-Control: no-cache -->
<!--Upgrade-Insecure-Requests: 1 -->
<!--User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 -->
<!--Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 -->
<!--Accept-Encoding: gzip, deflate, br -->
<!--Accept-Language: zh-CN,zh;q=0.9-->
if __name__ == '__main__':
app.run(debug=True)
5、模板
5.1 render_template
- 为了渲染模板,Flask 使用了一个名为Jinja2 的强大模板引擎。
- 默认情况下,Flask 在程序文件夹中的templates 子文件夹中寻找模板。
Flask 提供的render_template 函数把Jinja2 模板引擎集成到了程序中。render_template 函数的第一个参数是模板的文件名。随后的参数都是键值对,表示模板中变量对应的真实值.
from flask import Flask, render_template @app.route('/') def index(): return render_template('index.html') @app.route('/user/<name>') def user(name): return render_template('user.html', name=name) @app.route('/compare') def show_compare(): uid_list = ['1', '2', '3', '4'] return render_template('compare.html', uid_list=uid_list) if __name__ == '__main__': app.run(debug=True)
5.2 引用模板
需要在多处重复使用的模板代码片段可以写入单独的文件,再包含在所有模板中,以避免重复:
{% include 'common.html' %}
5.3 定义宏
宏是Jinja2特有的,像Django则没有这个。
定义宏:
{% macro 名称() %} 代码块 {% endmacro %}
调用:
{{ 宏名称() }}
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>宏</title> {% macro input(type='text', name='', placeholder='', value='') %} <input type="{{ type }}", name="{{ name }}", placeholder="{{ placeholder }}", value="{{ value }}"> {% endmacro %} </head> <body> <!--若给了默认值,传参时必须用关键字参数进行传值--> <div>账号{{ input(placeholder="请输入账号") }}</div> <div>密码{{ input(placeholder="请输入密码") }}</div> <div>{{ input(type="submit", value="提交") }}</div> </body> </html>
也可以将宏封装成一个包的形式,在需要使用的时候通过导入进行调用:
{% import 'macros/forms.html' as forms %} ………… <div>账号{{ forms.input(placeholder="请输入账号")}</div> <div>密码{{ forms.input(placeholder="请输入密码") }}</div> <div>{{ forms.input(type="submit", value="提交") }}</div>
若forms.html文件有多个宏定义时:
{% from 'macros/forms.html' import input %} <!--或者--> {% from 'macros/forms.html' import input as input_field%}
5.4 模版的继承
定义基模板:如base.html,
block
标签定义的元素可在衍生模板中修改。在本例中,我们定义了名为head、title 和body 的块。注意,title 包含在head 中。<html> <head> {% block head %} <title>{% block title %}{% endblock %} - My Application</title> {% endblock %} {% block style %}{% endblock %} </head> <body> {% block body %}{% endblock %} {% block content %}{% endblock %} </body> </html>
定义衍生模板:
extends
指令声明这个模板衍生自base.html。在extends 指令之后,基模板中的3个块被重新定义,模板引擎会将其插入适当的位置。注意新定义的head块,在基模板中其内容不是空的,所以使用super() 获取原来的内容。{% extends "base.html" %} {% block title %}Index{% endblock %} {% block head %} {{ super() }} {% block style %} <style></style> {% endblock %} {% endblock %} {% block body %} <h1>Hello, World!</h1> {% endblock %}
5.5 过滤器
Jinja2 提供的部分常用过滤器,例: Hello, {{ name|capitalize }}
过滤器名 | 说明 |
---|---|
safe | 渲染值时不转义 |
capitalize | 把值的首字母转换为大写,其他字母转换为小写 |
lower | 把值转为小写形式 |
upper | 把值转换成大写形式 |
title | 把值中每个单词的首字母都转换成大写 |
trim | 把值的首尾空格去掉 |
striptags | 渲染之前把值中所以的HTML标签删掉 |
6、Flask语法
6.1 条件控制语句
{% set a = true %}
{% set b = false %}
{% if user or a %}
Hello, {{ user }}!
{% elif not a and not b%}
Hello, Stranger!
{% else %}
Hello, Stranger2!
{% endif %}
<li {% if caption == active_page %} class="active" {% endif %}>
<a href={{ href }}>{{ icon|safe }} {{ caption }}</a>
</li>
6.2 for循环语句
页面上,每个元素之间会有空格,如果你不希望有空格,就要在for
语句的最后,和endfor
语句的最前面各加上一个-
号。
<!--example1-->
{% set digits=[1,2,3,4,5] %}
{% for digit in digits -%}
{{ digit }}
{%- endfor %}
<!--example2-->
{% set compare_list = [
('112121;23232323', {'112121':'施耐德1', '32432432':'施耐德2', '1wewew':'施耐德3'}, '2012'),
('22232323;232323232', {'112121':'施耐德1', '1wewew':'施耐德2'}, '2013'),
('32323223;2323232', {'112121':'施耐德1', '32432432':'施耐德2', '1wewew':'施耐德3'}, '2014'),
('423232323;232323323', {'112121':'施耐德1', '1wewew':'施耐德2'}, '2015')
] -%}
{% for id, hids, submission_date in compare_list %}
{% set outer_loop = loop %}
<tbody>
{% for uid in hids %}
<tr>
{% if loop.first %}
<td style="vertical-align:middle" rowspan="{{ hids | length + 1 }}">{{ outer_loop.index }}</td>
<td style="vertical-align:middle" rowspan="{{ hids | length + 1 }}">{{ submission_date }}</td>
{% endif %}
<td>NOE77102</td>
<td class="{% if loop.first %}active{% endif %}">PLC</td>
<td>{{ hids[uid] }}</td>
<td>kernel</td>
<td>1.1.0</td>
<td><font face="courier" style="font-size:12px">{{uid}}</font></td>
{% if loop.first %}
<td style="vertical-align:middle" rowspan="{{ hids | length + 1 }}">
<a href="/compare/{{ id }}" class="glyphicon glyphicon-search text-info">查看</a>
</td>
{% endif %}
{% endfor %}
</tr>
{% else %}
<h2>数据库中没有比较结果!</h2>
</tbody>
{% endfor %}
Jinja2的循环内置变量主要有以下几个:
变量 | 内容 |
---|---|
loop.index | 循环迭代计数(从1开始) |
loop.index0 | 循环迭代计数(从0开始) |
loop.revindex | 循环迭代倒序计数(从len开始,到1结束) |
loop.revindex0 | 循环迭代倒序计数(从len-1开始,到0结束) |
loop.first | 是否为循环的第一个元素 |
loop.last | 是否为循环的最后一个元素 |
loop.length | 循环序列中元素的个数 |
loop.cycle | 在给定的序列中轮循,如上例在”odd”和”even”两个值间轮循 |
loop.depth | 当前循环在递归中的层级(从1开始) |
loop.depth0 | 当前循环在递归中的层级(从0开始) |
- 如果你启用了"jinja2.ext.loopcontrols"扩展的话,你还可以在循环中使用
{% break %}
和{% continue %}
来控制循环执行。
6.3 路由规则
from flask import Flask,redirect,url_for
app = Flask(__name__)
# 变量规则
@app.route('/user/<username>')
def show_user_profile(username):
return 'User %s' % username
@app.route('/post/<float:post_id>')
def show_post(post_id):
return 'Post %d' % post_id
# 构造 URL
@app.route('/')
def index(): pass
@app.route('/login')
def login(): pass
@app.route('/user/<username>')
def profile(username): pass
with app.test_request_context():
# /
print(url_for('index'))
# / login
print(url_for('login'))
# / login?next=%2
print(url_for('login', next='/'))
# /user/John%20Doe
print(url_for('profile', username='John Doe'))
if __name__ == '__main__':
app.run(debug=True)
6.4 重定向redirect和反向解析url_for
redirect()的参数location,表示具体的url路径;
url_for反向解析,可以让重定向直接定位到具体的视图函数,让代码不冗余,扩展性更强。
from flask import Flask,redirect,url_for
app = Flask(__name__)
@app.route('/')
def index():
return redirect('http://www.double12.com')
# 反向解析:建议使用url_for实现页面重定向
@app.route('/demo')
def demo_url_for():
return redirect(url_for('index'))
if __name__ == '__main__':
app.run(debug=True)
7、其他
7.1 定义错误页面
与视图函数类似,只不过使用不同的修饰器@app.errorhandler(error_code)
@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
7.2 引用静态文件
默认设置下,Flask 会在程序根目录中名为 static的子目录中寻找静态文件。
<link rel="shortcut icon" href="{{ url_for('static', filename='logo_ico.png') }}"> <link rel="stylesheet" type="text/css" href="{{ url_for('static',filename='bootstrap/css/bootstrap.css') }}"> <script type="text/javascript" src="{{ url_for('static',filename='bootstrap/js/bootstrap.min.js') }}"></script> <img id="map_img" src="{{url_for('.static', filename='Pacman.gif')}}""/>
7.3 忽略模板语法
有时候,我们在页面上就是要显示”{{ }}”这样的符号怎么办?Jinja2提供了”raw”语句来忽略所有模板语法。
{% raw %}
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
{% endraw %}
7.4 赋值
使用set
关键字给变量赋值:{% set items = [[1,2],[3,4,5]] %}
7.5 with语句
类似于Python中的with
关键字,它可以限制with语句块内对象的作用域:
<!--使用with关键字前要启用`jinja2.ext.with_`扩展,在Flask框架中,这个扩展默认已启用-->
{% with foo = 1 %}
{% set bar = 2 %}
{{ foo + bar }}
{% endwith %}
{# foo and bar are not visible here #}
{% with arr = ['Sunny'] %}
{{ arr.append('Rainy') }} <!--页面会输出”None”,换成”{% %}”来执行,程序会报错,因为这是个表达式,不是语句-->
{{ arr }}
{% endwith %}
{% with arr = ['Sunny'] %}
{% do arr.append('Rainy') %} <!--启用”jinja2.ext.do”扩展。然后在模板中执行”do”语句即可-->
{{ arr }}
{% endwith %}
7.6 pass语句
对函数不做任何处理,可通过pass
关键字来进行占位,让代码不要报错先能正常运行
def userLogin(): pass
if(True): pass
for x in range(101): pass
print("程序正常执行");
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。