中文文档(http://docs.jinkan.org/docs/flask/)
英文文档(http://flask.pocoo.org/docs/0.11/)
postman
回顾HTTP通信
历史
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受到广大开发者喜爱的原因。
01-flask安装配置
虚拟环境是一个互相隔离的目录
mkvirtualenv flask_py2
pip install flask==0.10.1
pip freeze>requirements.txt
pip install -r requirements.txt
flask第一个 Hello World
# coding:utf-8
# 导入 Flask 类
from flask import Flask
# 创建一个flask的应用对象
# __name__ 表示当前的模块名字
# 模块名,flask以这个模块所在的目录为总目录,默认这个目录中的static为静态目录,templates为模版目录
app = Flask(__name__)
# app = Flask("__main_") # 直接当成启动文件的模式
@app.route("/") #装饰器
def index():
"""定义视图函数"""
return "Hello Flask"
if __name__ == '__main__':
app.run()
02-app对象的初始化和配置
目录
|-flask_project
|—static
|—templates
|—app.py
|—demo.py
flask应用对象初始化参数说明
"""
import name; #导入路径(寻找静态目录与模版目录位置的参数)
static_url_path # 访问静态资源的url前缀,默认static
static_folder # 静态文件的目录,默认 static
template_folder # 模版文件目录,默认是templates
"""
Flask(__name__, # __name__ 就是当前模块的名字
static_url_path="/static", # 默认值是static
static_folder="static",
template_folder="templates"
)
静态文件目录与路由说明
/static/ index.html
将 static 目录摘取出来,默认访问静态资源了就;
约定成熟的!
url.py
flask 设置 - 配置参数
3 直接操作 config
app.config['DEBUG'] = TRUE
flask中的配置参数不是在一个单独的文件中设置的,而是在app.config中保存的
app.config的操作与对字典的操作相同,可以通过[]运算符直接设置其中的内容,还可以通过get取出其中的内容之前在启动程序的时候,如果设置debug=True的参数,就可以打开调试模式,如果不在启动的时候给出debug参数,也可以在app.config中设置DEBUG,同样可以打开调试模式。本次就以这个参数为例,演示一下flask的配置参数的方法
from flask import Flask app = Flask(__name__) # 通过直接操作app.config字典,来完成配置参数 # 这并不是一个好的配置参数的方法,仅供参考 app.config["DEBUG"] = True @app.route("/") def index(): return "<h1>hello world</h1>" if __name__ == "__main__": app.run()1 使用配置文件读取配置文件
app.config.from_pyfile()
通过app.config.from_pyfile(),指定配置文件,可以完成参数配置
首先,创建一个settings.py文件,settings.py中的代码如下
DEBUG = Truefrom flask import Flask app = Flask(__name__) app.config.from_pyfile("./settings.py")2 通过对象配置参数(项目中的方式)
app.config.from_object()
定义一个配置参数的类对象,然后从中读取配置信息
从对象中读取文件的方法:app.config.from_object()
具体的使用代码如下from flask import Flask app = Flask(__name__) class FlaskConfig: DEBUG = True app.config.from_object(FlaskConfig)4 使用环境变量读取配置文件
同上一个方法一样,仍然是通过配置文件完成配置信息,但是不同的地方在于配置文件的名字没有被直接写在代码中,而是通过系统的环境变量读取的
环境变量名设置为:任意,例如FLASK_SETTING
读取环境变量使用:app.config.from_envvar(环境变量名的字符串)
例如,在linux的系统中,通过如下的方式设置环境变量名# 环境变量的设置,用于linux系统,windows系统可通过相似的方法完成设置 export FLASK_SETTING="./settings.py"然后在代码中,按照如下方式使用
from flask import Flask app = Flask(__name__) app.config.from_envvar("FLASK_SETTING")补充:从环境变量中读取配置文件的分析
在python中,通过内置的os模块,可以读取到环境变量,因此从环境变量中读取配置文件,和直接读取配置文件其实是一样的,以上代码相当于from flask import Flask import os app = Flask(__name__) # 通过os模块,从os.environ中读取到FLASK_SETTING的配置文件路径 flask_setting = os.environ.get("FLASK_SETTING") # 通过刚刚读取到的配置文件的路径,通过from_pyfile配置文件 app.config.from_pyfile(flask_setting)
读取 - 配置参数
视图中使用配置参数?
- 1.直接从全局对象app的config字典中取值
app.config.get() - 2.current_app
from flask import Flask,current_app
current_app.config.get()
app的run使用说明
Flask app.run的执行流程
在使用app.run执行的时候我们不是再本地跑或者说我们不想使用127.0.0.1的方式来执行。那我们应该怎么执行呢?
‘’’
1.host:要监听的主机名。 默认为127.0.0.1(localhost)。设置为“0.0.0.0”以使服务器在外部可用
2.port:默认值为5000
3.debug:默认为false。 如果设置为true,则提供调试信息
4.options:要转发到底层的Werkzeug服务器
‘’’
app.run(host=‘0.0.0.0’,port=5000,debug=True)
03-视图函数的路由
使用route() 装饰器来告诉 Flask 触发函数的 URL
路由转换器
视图函数的路由规则设置说明
查看全部路由信息
app.url_map 查看所有路由
给路由指定访问方式
通过 methods 限定访问方式, 接受参数形式为列表
# 通过method限定访问方式 @app.route('/post_only', methods=['POST', 'GET']) def post_only(): return 'post'反解析 url_for()和redirect()
使用 url_for() 的函数,可以通过 视图函数的名字 找到视图对应的 url 路径
而 redirect() 函数则是 url 重定向,会再次发送一个新的请求。
从 flask 库中导入这两个函数,一般这两个函数会一起使用。
切记: url_for() 接收的是 视图函数的名称,返回的是对应的 url 路径,切勿把 url 路径传进去。
案例:
from flask import Flask,current_app,redirect,url_for
# 创建flask应用对象
# __name__表示当前的模块名字
# 模块名,flask以整个模块所在的目录为总目录,默认整个目录中的static为静态目录
# templates为模板目录
app = Flask(__name__)
@app.route("/")
def index():
"""
定义的视图函数
:return:
"""
print(app.url_map)
return "hello flask"
@app.route("/post_only",methods=["POST"])
def post_only():
return "post only page"
@app.route("/hello",methods=["POST"])
def hello():
return "hello 1"
@app.route("/hello",methods=["GET"])
def hello2():
return "hello 2"
# 多个路由绑定同一视图函数
@app.route("/hi1")
@app.route("/hi2")
def hi():
return "hi page"
@app.route("/login")
def login():
# 使用url_for函数,通过视图函数的名字,找到视图对应的url路径
url = url_for("index")
return redirect(url)
@app.route("/register")
def register():
url = "/"
return redirect(url)
if __name__ == '__main__':
# 通过url_map可以查看整个flask中路由信息
print(app.url_map)
app.run(debug=True)
路由提取参数与自定义路由转换器
路由转换器: 有时我们需要将同一类URL映射到同一个视图函数处理
比如:使用同一个视图函数 来显示不同用户的个人信息。
注意 转换器的使用格式为:<转换器类型:参数名称>
Flask内置转换器
| 转换器 | 描述 |
|---|---|
| int | 接受整数 |
| float | 同int,但是接受浮点数 |
| path | 和默认的相似,但也接受斜线 |
- [ X ] 字符串转换器
注意:视图函数里接受的参数必须和route捕获尖括号<>里的参数一致
# str转换器 不加转换器类型, 默认是普通字符串规则(除了/的字符)
@app.route('/user/<username>')
def user_str(username):
response = 'hello {}'.format(username)
return response
- [ X ] 整型转换器
# int转换器
@app.route('/user/<int:user_id>')
def user_int(user_id):
response = 'hello {}'.format(user_id)
return response
- [ X ] 路径转换器
# path转换器
@app.route('/user/<path:user>')
def user_path(user):
response = 'hello {}'.format(user)
return response
自定义转换器
自定义一个匹配正则 url 的转换器
```py
# -- coding: utf-8 --
from flask import Flask, redirect, url_for
# 1. 定义自己的转换器
# 首先导入 werkzeug.routing 中 BaseConverter
from werkzeug.routing import BaseConverter
app = Flask(__name__)
# 定义自己的转换器 MoblieConverter
class MoblieConverter(BaseConverter):
def __init__(self, url_map):
# 调用父类的构造方法
super() # python3 中直接调用
super(MoblieConverter,self).__init__(url_map) # python2 中的调用
# 将正则表达式参数保存到对象属性中,flask会使用这个属性来进行路由的正则匹配
self.regex = r'1[34578]\d{9}'
# 然后自定义类继承 BaseConverter
class RegexConverter(BaseConverter):
""""""
def __init__(self, url_map, regex):
# 调用父类的构造方法
super() # python3 中直接调用
super(RegexConverter,self).__init__(url_map) # python2 中的调用
# 将正则表达式参数保存到对象属性中,flask会使用这个属性来进行路由的正则匹配
self.regex = regex
# 2. 将自定义的转换器添加到flask的应用当中
app.url_map.converters["re"] = RegexConverter
app.url_map.converters["mobile"] = MoblieConverter
# 3. 使用
# 127.0.0.1:5000/send/15912456789
@app.route("/send/<re(r'1[34578]\d{9}'):mobile_num>")
@app.route("/send/<mobile:mobile_num>")
def send_sms(mobile_num):
return "send sms to %s" % mobile_num
if __name__ == '__main__':
# 启动flask程序
app.run(debug=True)
```
路由转换器进阶使用 to_python()、to_url()
重写父类
BaseConverterto_python()、to_url()方法
to_python() 用于处理转换器提取出来的参数,可以进行修改。(转换器的核心)
to_url() 使用 url_for() 时调用,其结果将作为 url_for的返回值;每次进行url_for提交的参数都会先经过to_url,经过处理后才会返回回去。这里我就不过多演示。
```py
from flask import Flask, redirect, url_for
# 然后自定义类继承 BaseConverter
class RegexConverter(BaseConverter):
""""""
def __init__(self, url_map, regex):
# 调用父类的构造方法
super() # python3 中直接调用
super(RegexConverter,self).__init__(url_map) # python2 中的调用
# 将正则表达式参数保存到对象属性中,flask会使用这个属性来进行路由的正则匹配
self.regex = regex
def to_python(self, value):
return value+'RegexConverter'
def to_url(self, value):
return value+'RegexConverter'
@app.route('/')
def index():
url = url_for("send_sms", mobile_num="15912354697")
return redirect(url)
```
04-request对象
request中包含了前端发送过来的所有请求数据,在使用前要进行导入request库
from flask import request
request 常用的属性如下表
| 属性 | 说明 | 类型 |
|---|---|---|
| data | 记录请求的数据,并转换为字符串 | * |
| form | 记录请求中的表单数据 | MultiDict |
| args | 记录请求中的查询 参数 | MultiDict |
| cookies | 记录请求中的 cookie 信息 | Dict |
| headers | 记录请求中的报文头 | EnvironHeaders |
| methd | 记录请求使用的Http方法 | GET/POST |
| url | 记录请求的url地址 | string |
| files | 记录请求上传的文件 | * |
request的form_data_args
表单格式数据: k=v&kk=vv&kkk=vvv
- form和data是用来提取请求体数据,通过request.form可以直接提取请求体中的
表单格式的数据,是一个类字典的对象,例如:
from flask import Flask,request
app = Flask(__name__)
@app.route("/index",methods=["GET","POST"])
def index():
# request中包含了前端发送过来的所有请求数据
# form和data是用来提取请求体数据
# 通过request.form可以直接提取请求体中的表单格式的数据,是一个类字典的对象
name = request.form.get("name")
age = request.form.get("age")
return "hello world name=%s ,age=%s"%(name, age)
if __name__ == '__main__':
app.run(host="127.0.0.1", port=8000,debug=True)
注意:通过get方法只能拿到多个同名参数的第一个,getlist方法可以拿到多个同名参数的列表,例如:
from flask import Flask,request
app = Flask(__name__)
@app.route("/index",methods=["GET","POST"])
def index():
# request中包含了前端发送过来的所有请求数据
# form和data是用来提取请求体数据
# 通过request.form可以直接提取请求体中的表单格式的数据,是一个类字典的对象
# 通过get方法只能拿到多个同名参数的第一个
name = request.form.get("name")
age = request.form.get("age")
print(request.data)
# getlist方法可以拿到多个同名参数的列表
name_li = request.form.getlist("name")
return "hello world name=%s ,age=%s, name_li=%s"%(name, age, name_li)
if __name__ == '__main__':
app.run(host="127.0.0.1", port=8000,debug=True)
- 若数据不是表单格式的数据,可用requset.data来进行提取
from flask import Flask,request
app = Flask(__name__)
@app.route("/index",methods=["GET","POST"])
def index():
result= request.data
return result
if __name__ == '__main__':
app.run(host="127.0.0.1", port=8000,debug=True)
- args是用来提取url中的参数,例如http://127.0.0.1:8000/index?city=BeiJing 查询字符串,可以用args方式进行提取city参数,范例如下:
from flask import Flask,request
app = Flask(__name__)
# http://127.0.0.1:8000/index?city=huizhou 查询字符串 可以用args方式进行提取
@app.route("/index",methods=["GET","POST"])
def index():
# args是用来提取url中的参数
city = request.args.get("city")
day = request.args.get("day")
return "hello world city=%s, day=%s"%( city, day)
if __name__ == '__main__':
app.run(host="127.0.0.1", port=8000,debug=True)
上传文件
只能是POST方式上传文件!!!
上传文件主要用了request当中最后一个属性files
填写headers:
Key:Content-Type
Value:multipart/form-data
# -*- coding: utf-8 -*-
from flask import Flask,request
app = Flask(__name__)
@app.route("/upload", methods=["GET","POST"])
def upload():
f = request.files.get("pic")
if f is None:
# 没有发送文件
return "未上传文件"
# 将文件保存在本地
# 1.创建一个文件
f1 = open("./demo.png", "wb")
# 2.向文件写内容
data = f.read()
f1.write(data)
f1.close()
return "上传成功"
if __name__ == '__main__':
app.run(debug=True)
更简单的写法
# -*- coding: utf-8 -*-
from flask import Flask,request
app = Flask(__name__)
@app.route("/upload", methods=["GET","POST"])
def upload():
f = request.files.get("pic")
if f is None:
# 没有发送文件
return "未上传文件"
# 直接使用上传的文件对象保存
f.save("./demo1.jpg")
return "上传成功"
if __name__ == '__main__':
app.run(debug=True)
with使用
有一些任务,可能事先需要设置,事后做清理工作。对于这种场景,Python的with语句提供了一种非常方便的处理方式。其中一个很好的例子是文件处理,你需要获取一个文件句柄,从文件中读取数据,然后关闭文件句柄。
基本思想是:with所求值的对象必须有一个enter()方法,一个exit()方法。
enter 进入with调用
exit 离开with的时候调用
# coding:utf-8
f = open('./test.txt','wb')
try:
f.write('hello')
except Exception:
pass
finally:
f.close()
## 等价于
with open('./test.txt','wb') as f:
f.write('hello')
### with 完成finally完成的操作!!!
05 - abort函数、自定义错误、 视图函数的返回值
abort函数 from flask import abort
立即终止视图函数的运行,并且返回给前端特定的信息
1. 传递状态码信息 `abort(400)`;[必须是标准的状态码]
2. 传递响应体信息 `abort(Response("faild"))`
```py
# coding:utf-8
from flask import Flask, abort, Response
app = Flask(__name__)
@app.route("/login", methods=["GET"])
def login():
name = ""
pwd = ""
if name != "zhangsan" or pwd != "admin":
# abort函数可以立即终止视图函数的执行
# 并返回给前端特定的信息
# 1 传递状态码信息, 必须是标准的http状态码
abort(404)
# # 2. 传递响应体信息
# resp = Response("login failed")
# abort(resp) # 返回Response响应对象
return "login success"
if __name__ == '__main__':
app.run(host="0.0.0.0", port=5000, debug=True)
```
自定义错误
```py
@app.errorhandler(404)
def error(err):
return '您请求的页面不存在了,请确认后再次访问! %s' % err
```
设置响应信息的方法
- [x] 字符串响应形式
字符串:直接使用return关键字返回一个字符串: return 'hello flask'
- [x] 模版响应形式
模版的返回,建立 templates 文件夹 --> 在templates中建立要返回的HTML页面 --> 在视图中使用`render_template(HTML文件名)` --> 返回模版
- [x] 重定向响应形式
重定向:重定向是跳转至一个新的路由,使用redirect
重定向的形式:
重定向一个外链
重定向一个内部链接
- [x] 响应json形式与元组
json: 返回json数据需要借助flask提供的jsonify来实现
元组形式: 可以返回一个元组。这样的元组必须是(response, status, headers)的形式,且至少包含2个元素。status 值会覆盖状态代码, headers可以是一个列表或者字典,作为额外的消息表头值
> import json
> json.dumps('字典')
> json.loads('字符串')
```python
# 实例如下:
from flask import Flask
from flask import jsonify
app = Flask(__name__)
# --- json ---
@app.route('/index', endpoint='idx')
def index():
# 字典形式数据
data = {
'name': 'jeremy',
'age': 18
}
# 返回json数据;并设置header头
return jsonify(data)
# 看一下源码
# --- 元组 ---
@app.route('/index', endpoint='idx')
def index():
# return ('要返回的字符串', 6969是状态码, {'author': 'jeremy'}字典是设置在响应头中的键值对信息)
return ('要返回的字符串', 6969, {'author': 'jeremy'})
if __name__ == '__main__':
app.run(debug=True)
```
- [x] make_response()自定义响应
make_response()形式:flask可以返回make_response自定义的响应,make_response()内部需要传递返回的字符串,起创建的对象还有headers属性用于设置响应头,status属性用于设置状态码
```python
# 示例如下:
from flask import Flask
from flask import make_response
app = Flask(__name__)
@app.route('/index', endpoint='idx')
def index():
# 响应字符串
resp = make_response('自定义的响应字符串')
# 响应头键值对
resp.headers['author'] = 'jeremy'
# 状态码与状态码提示信息
resp.status = '6969 a post'
# 注意: 响应头键值对与状态码提示信息均为英文, 否则抛异常
return resp
if __name__ == '__main__':
app.run(debug=True)
```
06-cookie和session
cookie 的使用
注意:默认有效期是临时有效
res = make_response(‘success’)
res.set_cookie(‘key’,‘value’,max_age=3600)
request.cookies.get(‘key’)
res = make_response(‘success’)
res.del_cookie(‘key’)
session 机制说明
注意:flask的session需要用到的密钥字段;防串改的处理
app.config[‘SECRET_KEY’] = “dddfdfdfdfdfddfddfdfd”
session[“name”] = “python”
cookie set_cookie 中保存 session_id
服务器生成[session_id] --> 客户端 --> 客户端保存session_id到cookie中
客户端携带cookie[session_id] --> 服务器
特殊:flask默认把session保存在cookie中
session 保存位置:数据库、redis、mongodb、文件、
程序运行内存中?问题:1.占用服务器内存!
2.session 跨服务器:多台机器的话,nginx负载均衡将用户分配到不同的机器?
问:如果禁用cookie可以保存session_id吗?
答:当然可以,将session_id放到请求的路径当中!!! http://www.url.cn?session_id=***
问:url中和cookie中,session的有效期?
cookie中可以设置cookie中的有效期,url中只能是不关闭网站的情况下session有效,再次重新登陆需要重新获取和设置!!!
07-flask上下文和请求钩子
上下文
上下文
什么是上下文?上下文相当于一个容器,它保存了程序运行过程中的一些信息,它是当前环境的一个快照(snapshot)。
Flask 项目中有两个上下文,一个是应用上下文(app),另外一个是请求上下(request)。请求上下文 request 和 应用上下文 current_app 都是一个全局变量,所有请求都共享的。
程序上下文中包含了程序运行所必须的信息;请求上下文里包含了请求的各种信息,比如请求的URL、HTTP方法等
from flask import Flask,request # 引入的是全局变量
隔离:flask分装好的线程局部变量的概念!!!
全局变量,在不同线程内看到的是不一样的,使用起来就像使用线程变量一样
线程编号作为键 --> {form:“xxx”,args:[]}
-[x]请求上下文 request
-[x]应用上下文 app
current_app
g变量:在本次请求之内,一次请求之内的多个函数之间传递参数!!!
请求钩子
| 钩子 | 说明 |
|---|---|
| before_first_request | 注册一个函数,在处理请求前运行 |
| before_request | 注册一个函数,在处理每个请求前运行 |
| after_request | 注册一个函数,如果有未处理的一场抛出。会在每个请求结束后运行 |
| teardown_request | 工作在非调试模式的时候,注册一个函数,即使有未处理的异常抛出,会在每个请求介绍后执行。如果发生异常,会传入异常对象作为参数注册到函数中;debug=False |
| after_this_request | 在视图函数内注册一个函数,在这个请求结束后运行 |
| 假如我们创建了三个视图函数A、B、C,其中视图C使用了after_this_request钩子,那么当请求A进入后,整个请求处理周期的请求处理函数调用流程如图: |
flask_script脚本扩展的使用
flask_script
-[x]第一步:安装扩展脚本库
pip install flask-Script
-[x]第二步:创建Manager管理对象类,将app托管给他
# coding:utf-8
# 文件名:01_flask_script.py
from flask import Flask
from flask_script import Manager # 启动命令的管理类
app = Flask(__name__)
# 创建Manger管理类的对象
manager = Manager(app)
@app.route('/index')
def index():
print("index 被执行")
return "index page"
if __name__ == "__main__":
# 通过管理对象来启动flask应用
manager.run()
-[x]第三步:通过–help查看该管理文件当前支持的命令
-[x]第四步:启动flask应用runserver
python 01_flask_script.py runserver
-[x]第五步:shell,通过shell可操作当前问价内的所有对象
python 01_flask_script.py shell
app.url_map
-[x]第六步:查看runserver支持的命令
python 01_flask_script.py runserver --help
08-模板
模板与自定义过滤器
支持链式使用过滤器
<p>{{"jello world" |trim |upper }}</p>
first:获取第一个元素{{[1,2,3,4,5,6]|first}}
last:最后一个元素{{[1,2,3,4,5,6]|last}}
length:获取列长度{{[1,2,3,4,5,6]|length}}
字符串过滤器:
safe:禁止转义
capitalize:把变量值的首字母转成大写,其余字母转小写
lower:把值转成小写
upper:把值转成大写
title:把值中的每个单词的首字母都转成大写
trim:把值的首尾空格去掉
reverse:字符串反转
format:格式化输出
striptags:渲染之前把值中所有的HTML标签都删掉自定义过滤器
第一种方法,编写相关函数添加进过滤器模块中:
"""第一种方法"""
# 1.编写过滤器函数
def list_step_2(li):
return li[::2]
# 注册过滤器
"""app.template_filter第一个参数为过滤器函数,过滤器函数名字"""
app.add_template_filter(list_step_2,"li2")
第二种方法,使用装饰器的方法给过滤器模块添加新功能:
"""第二种方法"""
@app.template_filter("li3")
def list_step_3(li):
return li[::3]
引入表单扩展
pip install Flask-WTF
Flask web开发实战之基础篇 Flask-表单
Flask-WTF 默认为每个表单启用CSRF保护,他会为我们自动生成和验证CSRF令牌。默认情况下,Flask-WTF 使用程序密钥来对CSRF令牌进行签名,所以我们需要为程序设置密钥:
app.sercret_key='sercret string'
创建表单模型类与模版使用
```python
import os
from pickle import TRUE
from flask import Flask, redirect, render_template, request, session, url_for
from flask_wtf import CSRFProtect, FlaskForm
from wtforms import PasswordField, StringField, SubmitField
from wtforms.validators import DataRequired, EqualTo, Length
SECRET_KEY = os.urandom(24)
app = Flask(__name__)
app.debug = True
app.config['SECRET_KEY'] = SECRET_KEY
```
```html
<form action="" methods="POST">
{{ form.csrf_token() }}
<p>{{ form.user_name }}</p>
{% for msg in form.user_name.errors %} {{msg}} {% endfor %}
</form>
```
WTForms 支持的HTML标准字段
| 字段类型 | 说 明 |
|---|---|
| StringField | 文本字段 |
| TextAreaField | 多行文本字段 |
| PasswordField | 密码文本字段 |
| HiddenField | 隐藏文本字段 |
| DateField | 文本字段,值为 datetime.date 格式 |
| DateTimeField | 文本字段,值为 datetime.datetime 格式 |
| IntegerField | 文本字段,值为整数 |
| DecimalField | 文本字段,值为 decimal.Decimal |
| FloatField | 文本字段,值为浮点数 |
| BooleanField | 复选框,值为 True 和 False |
| RadioField | 一组单选框 |
| SelectField | 下拉列表 |
| SelectMultipleField | 下拉列表,可选择多个值 |
| FileField | 文件上传字段 |
| SubmitField | 表单提交按钮 |
| FormField | 把表单作为字段嵌入另一个表单 |
| FieldList | 一组指定类型的字段 |
常见的验证函数
| 验证函数 | 说 明 |
|---|---|
| 验证电子邮件地址 | |
| EqualTo | 比较两个字段的值;常用于要求输入两次密码进行确认的情况 |
| IPAddress | 验证 IPv4 网络地址 |
| Length | 验证输入字符串的长度 |
| NumberRange | 验证输入的值在数字范围内 |
| Optional | 无输入值时跳过其他验证函数 |
| Required | 确保字段中有数据 |
| Regexp | 使用正则表达式验证输入值 |
| URL | 验证 URL |
| AnyOf | 确保输入值在可选值列表中 |
| NoneOf | 确保输入值不在可选值列表中 |
模版宏的使用
```html
<!-- 模版中 -->
定义:
{% macro input() %}
<input type="text" value="" size="30"/>
{% endmacro %}
使用:
{{ input() }}
带参数的定义
{% macro input(value,typr,size) %}
<input type={{}} value={{}} size={{}}/>
{% endmacro %}
使用:
{{ input() }}
```
宏定义在外部使用
提取出来创建单独的一个文件
macro_input.html
{% macro input4() %}
{% endmacro %}
导入宏文件
{% import “macro_input.html” as m_input %}
{{ m_input.input4() }}
模版的闪现使用
- 模版的继承
# 父模版
{% block top %}
顶部菜单
{% endblock top %}
{% block content %}
{% endblock content %}
{% block bottom %}
底部
{% endblock bottom %}
----
# 子模版
{% extends 'base.html'%}
{% block content %}
需要填充的内容
{% endblock content %}
- extends 扩展
- include 包含
flask 特殊的变量和方法
在模版中可以直接使用
config 对象
request 对象
url_for 方法
get_flashed_message方法,返回通过 flash()传入的信息,只能看一次[闪现]
09-数据库扩展包flask-sqlalchemy
使用sqlalchemy的配置
sqlalchemy
sqlalchemy 是一个关系型数据库框架,它提供了高层的ORM和底层的原生数据库操作。flask-sqlalchemy 是一个简化了SQLAlchemy操作的flash扩展。
pip install flask-sqlalchemy
仅仅转换为模型类 --> 转换换成模型类的对象
数据库驱动:要连接mysql数据库,仍需要安装flask-mysqldb、pymysql(python3)
pymysql.install_as_mysqldb()
pip install MySQL-Python :python2默认安装官方的
pip install flask-mysqldb
方法1:
pip install PyMySQL
#然后在需要的项目中,把 init.py中添加两行:
import pymysql
pymysql.install_as_MySQLdb()
方法2:pip3 install mysqlclient
config
# coding:utf8
import pymysql
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_wtf import FlaskForm
pymysql.install_as_MySQLdb()
app = Flask(__name__)
class Config():
"""配置参数"""
# sqlalchemy
SQLALCHEMY_DATABASE_URI = 'mysql://root:123456@127.0.0.1:3306/flask'
# 设置 sqlalchemy 自动关跟踪数据库
SQLALCHEMY_TRACK_MODIFICATIONS = True
app.config.from_object(Config)
# 方案二
# app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:mysql@127.0.0.1:3306/test3'
# #每次请求结束后会自动提交数据库中的改动;官方不建议了已经
# app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN']=True
# app.config['SQLALCHEMY_TRACK_MODIFICATIONS']=True
# # 查询时会显示原始的sql语句
# app.config['SQLALCHEMY_ECHO']=True
# 使用sqlalchemy的配置
db = SQLAlchemy(app)
########## ------------------------------ ########
"""
创建模型类
表名常见命名规范:
- 数据库名缩写_表名 db_table
- tbl_表名
"""
class Role(db.Model):
"""角色表"""
__tablename__ = "tbl_roles" # 指名数据库的表名
id = db.Column(db.Integer,primary_key=True,unique=True) #整型的主键默认设置为自增
name = db.Column(db.Srting(32),unique=True)
users = db.relationship("User",backref="role") # 模型类角度考虑;反推,给User模型添加一个role属性
def __repr__(self):
"""定义以后让对象显示更加直观"""
return "Role object:name%s" % self.name
class User(db.Model):
"""用户表"""
__tablename__ = "tbl_users" # 指名数据库的表名
id = db.Column(db.Integer,primary_key=True,unique=True) #整型的主键默认设置为自增
name = db.Column(db.Srting(64),unique=True)
password = db.Column(db.Srting(128),unique=True)
role_id = db.Column(db.Integer,db.ForeignKey("tbl_roles.id")) #底层考虑
@app.route('/')
def index():
pass
if __name__ == '__main__':
# 删除所有表
db.drop_all()
# 创建所有表
db.create_all()
role1 = Role(name="admin")
# session 记录对象任务
db.session.add(role1)
# 提交到任务数据库中
db.session.commit()
us1=User(name="zhang",password="12334",role_id=role1)
us2=User(name="zhang2",password="12334",role_id=role1)
# 一次保存多条
db.session.add_all(us1,us2)
db.session.commit()
# 查询操作
Role.query.all() # 多条,查询的列表对象
Role.query.first() # 单条,返回第一条记录
Role.query.get(id) # 根据主键 ID获取对象
db.session.query(Role).all()
db.session.query(Role).get()
app.run(debug=TRUE)
SQLALCHEMY 配置说明
| 名称 | 说明 |
|---|---|
| SQLALCHEMY_BINDS | 一个映射绑定 (bind) 键到 SQLAlchemy 连接 URIs 的字典。 更多的信息请参阅 绑定多个数据库。 |
| SQLALCHEMY_ECHO | 如果设置成 True,SQLAlchemy 将会记录所有发到标准输出(stderr)的语句,这对调试很有帮助。 |
| SQLALCHEMY_RECORD_QUERIES | 可以用于显示地禁用或者启用查询记录。查询记录在调试或者测试模式下自动启用。更多信息请参阅 get_debug_queries()。 |
| SQLALCHEMY_NATIVE_UNICODE | 可以用于显示地禁用支持原生的 unicode。这是 某些数据库适配器必须的(像在 Ubuntu 某些版本上的 |
| SQLALCHEMY_POOL_SIZE | 数据库连接池的大小。默认是数据库引擎的默认值 (通常是 5)。 |
| SQLALCHEMY_POOL_TIMEOUT | 指定数据库连接池的超时时间。默认是 10。 |
| SQLALCHEMY_POOL_RECYCLE | 自动回收连接的秒数。这对 MySQL 是必须的,默认 情况下 MySQL 会自动移除闲置 8 小时或者以上的连接。 需要注意地是如果使用 MySQL 的话, Flask-SQLAlchemy 会自动地设置这个值为 2 小时。 |
| SQLALCHEMY_MAX_OVERFLOW | 控制在连接池达到最大值后可以创建的连接数。当这些额外的 连接回收到连接池后将会被断开和抛弃。 |
| SQLALCHEMY_TRACK_MODIFICATIONS | 如果设置成 True (默认情况),Flask-SQLAlchemy 将会追踪对象的修改并且发送信号。这需要额外的内存, |
列选项
| 选项名 | 说明 |
|---|---|
| primary_key | 如果为True,代表主键 |
| unique | 如果为True,代表这列不允许出现重复值 |
| index | 创建索引 |
| nullable | true允许空值,false不允许空 |
| default | 为这列定义默认值 |
常用的 SQLAlchemy 查询过滤器
| 过滤器 | 说明 |
|---|---|
| filter( == ) | 把过滤器添加到原查询上,返回一个新查询;需要指定类名 |
| filter_by() | 把等值过滤器添加到原查询上,返回一个新查询;只能是等于 |
| limit | 使用指定的值限定原查询返回的结果 |
| offset() | 偏移查询返回的结果,返回一个新查询 |
| order_by() | 根据指定条件对原查询结果进行排序,返回一个新的查询 |
| group_by() | 根据指定条件对原查询结果进行分组,返回一个新的查询 |
查询失败 Django 返回异常,flask 返回 null
常用的 SQLAlchemy 查询执行器
关联查询与自定义显示信息
user = User.query.get(1)
user.role.name
def repr():
“”“定义以后让对象显示更加直观”“”
pass
数据的修改与删除
- 先查询再更新
user = User.query.get(1)
user.name
user.name=“python”
db.session.add(user)
db.session.commit()
查询时候同时更新
User.query.fiter_by(name=“zhou”).update(“password”:“111111”)
db.session.commit()删除
user = User.query.get(3)
db.session.delete(user)
db.session.commit()
11-数据库迁移扩展包Flask-Migrate、 邮件扩展包Flask-Mail
Flask-Migrate
pip install flask-migrate
pip install flask-script
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate,MigrateCommand
from flask_script import shell,Manager
app = Flask(__name__)
manager = Manager(app)
db=SQLAlchemy(app)
migrate = Migrate(app,db)
manager.add_command('db',MigrateCommand)
class Role(db.Model):
pass
class User(db.Model):
pass
if __name__ =='__main__':
manager.run()
- 初始化操作,生成文件夹
python author_book.py db init - 生成迁移文件;等价 django->migrations (-m 操作的备注信息)
python author_book.py db migrate -m ‘add mobile’ - 更新数据库,升级
python author_book.py db upgrade - 查看历史
python author_book.py db history - 回退降级操作
python author_book.py db downgrade 版本号
Flask-Mail
流程:链接到邮箱服务,开启 SMTP 服务!!!
第三方生成授权码,将该码添加到 Flask 的配置变量
from flask import flask
from flask_mail import Mail,Message
app = Flask(__name__)
# 一次性添加多个键值对
app.config.update([
DEBUD = True,
MAIL_SERVER = 'smtp.qq.com',
MAIL_PORT=465,
MAIL_USE_TLS = True,
MAIL_USERNAME = 'xxx',
MAIL_PASSWORD='xxxxxx'
])
mail = Mail(app)
msg = Message('This is a test',sender='xxxx@qq.com',recipients=['',''])
msg.body = "Flask test mail"
mail.send(msg)
12-不使用蓝图解决模块划分问题
app:相当于project工程
蓝图:对应的小模块的划分
循环引用
推迟一方的导入,让另一方先完成。
使用装饰器解决模块分割问题
app.route(“/get_goods”)(被装饰的函数)
注意:
@ 只是一个语法行
装饰器的实质是一个闭包函数!!!
带参数的装饰器:三层闭包
13-使用蓝图划分模块
蓝图的概念:在Flask中, 蓝图(blueprint)是一种用来扩展已有Flask应用结构的方式, 蓝图提供了一种把功能类似的视图函数组合在一起的方式, 通过这种方式, 开发者能够把他们的应用拆分成不同的组件. 在我们的架构中, 蓝图的作用类似于控制器(controller).
蓝图flask自带的核心的内容!!!
蓝图的使用步骤分为三步:
- 创建一个蓝图对象
- 在这个蓝图对象上进行操作,注册路由,指定静态文件,注册模版过滤器
- 在应用对象上注册蓝图对象
蓝图的基本定义与使用
# 蓝图使用示例:
# 1创建蓝图 & 2添加蓝图路由
from flask import Blueprint
# 创建蓝图对象
users_bp = Blueprint('users', __name__)
# 添加蓝图路由
@users_bp.route('/index')
def index():
return 'users bp response'
---
# 3.注册蓝图
from flask import Flask
# 导入蓝图
from apps.users_bp import users_bp
# 创建Flask实例
app = Flask(__name__)
# 注册蓝图
app.register_blueprint(users_bp)
# 带前缀的蓝图
app.register_blueprint(users_bp,url_prefix="/xxx")
if __name__ == '__main__':
# 查看路由映射
print(app.url_map)
app.run()
以目录形式定义蓝图
在包的初始化文件中定义
目录
|-main.py
|—cart
|------static
|------templates
|------init.py
|------view.py
init.py
from flask import Blueprint
# 创建一个蓝图
app_cart = Blueprint('app_cart', __name__,template_folder="templates")
# 在 __init__.py文件被执行的时候把视图加载进来,让蓝图与应用程序知道蓝图的存在
from .views import get_cart
view.py
from flask import render_template
from . import app_cart
@app_cart.route("/get_cart")
def get_cart():
return "get cart"
main.py
from flask import Flask
from cart import app_cart
app = Flask(__name__)
app.register_blueprint(app_cart,url_prefix="/cart")
蓝图里模板目录的处理
模块里面找不到模版可以向工程里面去找模版~~~
14-单元测试
Dev - test - demo - product
pip install pytest-flask
单元测试的引入
单元测试(unit testing)是开发者自己编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确。通常而言,一个单元测试是用于判断某个特定条件(或者场景)下某个特定函数的行为。
assert断言使用
assert 断言后面是一个表达式,如果表达式返回成功,则断言成功,程序能够继续往下执行,
如果表达式返回假,断言失败,assert会抛出异常AssertionError,终止程序继续往下执行!!!
实现简单的单元测试
单元测试:setUp与flask的测试模式
*** setUp:在进行测试之前先被执行
*** tearDown: 所有测试执行后执行[通常用来清理操作]
*** 测试模式: app.config[‘TESTING’] = True / app.testing = True
login.py
# coding:utf-8
from flask import Flask, jsonify, request
app = Flask(__name__)
@app.route("/login")
def login():
user_name = request.form.get('user_name')
password = request.form.get('password')
if not all([user_name, password]):
resp = {
"code": 1,
"message": "invalid params"
}
return jsonify(resp)
if user_name == 'admin' and password == '123456':
resp = {
"code": 2,
"message": "Login Success"
}
return jsonify(resp)
else:
resp = {
"code": 3,
"message": "wrong user_name or passwd !!"
}
return jsonify(resp)
if __name__ == "__main__":
app.run(debug=True)
test.py
# coding:utf-8
import json
import unittest
from login import app
class LoginTest(unittest.TestCase):
def setUp(self):
"""在进行测试之前先被执行"""
# app.config['TESTING'] = True
app.testing = True
# 创建进行web请求的客户端,使用flask提供的
self.client = app.test_client()
def test_empty_user_name_pasword(self):
# 利用client客户端模拟发送web请求
ret = self.client.post("/login", data={})
# ret 是视图返回的响应对象,data属性是响应体的数据
resp = ret.data
resp = json.loads(resp)
# 拿到返回值后进行断言测试
self.assertIn("code", resp)
self.assertEqual(resp["code"], 1)
# 测试只是传递用户名或者密码
ret = self.client.post("/login", data={"user_name": "admin"})
resp = ret.data
resp = json.loads(resp)
self.assertIn("code", resp)
self.assertEqual(resp["code"], 1)
# 测试用户名或者密码错误
def test_empty_user_name_pasword(self):
pass
if __name__ == "__main__":
unittest.main()
15-flask部署
Gunicorn(绿色独角兽) + Flask
业务服务器:统一入口 nginx nginx负载均衡,提供静态文件
# 安装
pip install gunicron
# 帮助
gunicorn -h
# 4个进程运行 / -D 后台守护进程运行
gunicorn -w 4 -b 127.0.0.1:5000 -D --access-logfile ./logs/log main:app

nginx 配置
nginx 轮转发根据 upstream 配置
# nginx.conf
http{
upstream flask{
server IP:port;
server IP:port;
server IP:port;
}
server{
listen 80;
server_name loclahost;
# 转发
location /{
# proxy_pass http://10.211.55.2.5000; # 单台服务器
proxy_pass http://flask # 组服务器 和 upstream 后面的值一样
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}
sqlalchemy操作mysql提示警告的问题
mysql5.7以上的事物隔离级别出错问题
总结
常用的扩展包
Flask-SQLalchemy:操作数据库;
Flask-script:插入脚本;
Flask-migrate:管理迁移数据库;
Flask-Session:Session存储方式指定;
Flask-WTF:表单;
Flask-Mail:邮件;
Flask-Bable:提供国际化和本地化支持,翻译;
Flask-Login:认证用户状态;
Flask-OpenID:认证;
Flask-RESTful:开发REST API的工具;
Flask-Bootstrap:集成前端Twitter Bootstrap框架;
Flask-Moment:本地化日期和时间;
Flask-Admin:简单而可扩展的管理接口的框架
虚拟环境
- 创建虚拟环境,virtualenv虚拟开发环境
pip install virtualenv pip install virtualenvwrapper-win - 利用安装好的模块创建一个虚拟环境
mkvirtualenv first_env
创建的路径C:\*\Local\pypa\virtualenv - 虚拟环境其他相关命令:
切换到指定的虚拟环境:workonworkon first_env
退出虚拟环境deactivate
删除指定的虚拟环境rmvirtaulenv first_env
列出所有虚拟环境lsvirtualenv
进入到虚拟环境所在的目录cdvirtualenv
在虚拟环境中安装Flask模块pip install flask - 打开pycharm选择已经存在的虚拟环境
C:\*\Envs\first_env\Scripts\python.exe
** 或直接新建flask项目,选择Virtualenv,配置python解释器后新建,在teminal终端上安装pip install flask。**
项目目录详解
static文件夹 用于存放各种静态文件 css、js、图片等
templates文件夹 用于存放html模板文件
app.py 为主文件 启动项目需要启动该文件
app.py 文件的名字可以自由命名
初始app.py
The flask object implements a WSGI application and acts as the central object.
from flask import Flask
app = Flask(__name__) # __name__参数:Python会根据所处的模块来赋予__name__变量相应的值,此时值为app
@app.route('/') # 用来匹配url,以装饰器实现,装饰器引用的是上面实例化核心类出来的对象。
def hello_world(): # 路由下面跟的函数是视图函数,如果匹配到了路由就会触发视图函数执行,并且return回具体的数据。
return 'Hello World!'
if __name__ == '__main__':
app.run(debug=True) # app.run()实现了flask程序在开发环境下运行起来,并且默认ip和端口是127.0.0.1:5000 host,port参数可以设定
导入Flask的核心类实例化对象app,然后app作为装饰器使用匹配url分发给下面的视图函数,然后执行该页面会触发app调用run()方法运行起来整个项目。此时为b/s型。
DEBUG模式
app参数设置 debug=True开发阶段对代码进行修改时使用,flask默认部署后再修改代码,刷新页面不起作用。flask代码中如果出现了异常,浏览器中不会提示具体的错误信息,开启debug模式后会把具体的错误信息发送到浏览器上。flask代码如果被修改了,必须要重启项目修改的代码才会有效,开启debug模式后修改代码后只要ctrl+s项目就会自动重新加载,不需要手动加载整个网站。
对这些配置操作手动解耦,创建settings文件
ENV = 'development'
DEBUG = True
此时app文件改为
from flask import Flask, Response
import settings
app = Flask(__name__)
app.config.from_object(settings) # 配置文件解耦
# app.config.from_pyfile('settings') # 或这个
@app.route('/')
def hello_world():
return 'Hello World!' # 底层将其送入Response进行封装
@app.route('/han/')
def hello_world():
return Response('你好啊')
if __name__ == '__main__':
app.run()
运行python app.py
用http://localhost:5000/或http://127.0.0.1:5000/访问,url是统一资源定位符
默认只能是本机访问,如果需要通过本机ip地址访问,则将host改为‘0.0.0.0’
查看本机ip:cmd–ipconfig 查找ipv4地址
针对sever端写东西:(扩展)
b/s:浏览器/服务器 c/s:客户机/服务器
CS = Client - Server = 客户端- 服务器。
例子: QQ,迅雷,快播,暴风影音,各种网络游戏等等。
BS = Browser - Server = 浏览器 - 服务器。
例子:所有的网站都是BS结构。( 知乎 / 果壳 / 微博 / 等等等等等等 )
区别:
1、开发维护成本
cs开发维护成本高于bs。因为采用cs结构时,对于不同的客户端要开发不同的程序,而且软件安装调试和升级都需要在所有客户机上进行。
bs只需要将服务器上的软件版本升级,然后重新登录就可以了。
2、客户端负载
cs客户端负载大。cs客户端不仅负责和用户的交互,收集用户信息,而且还需要通过网络向服务器发出请求。
bs把事务处理逻辑部分交给了服务器,客户端只是负责显示。
3、安全性
cs安全性高。cs适用于专人使用的系统,可以通过严格的管理派发软件。
bs使用人数多,不固定,安全性低。
4、作用范围
Client/Server是建立在局域网的基础上的。Browser/Server是建立在广域网的基础上的。
Werkzeug工具包简介
WSGI:Web服务器网关接口(Python Web Server Gateway Interface,缩写为WSGI)是为Python语言定义的Web服务器和Web应用程序或框架之间的一种简单而通用的接口。Werkzeug是一个WSGI工具包,可以作为一个Web框架的底层库,封装了很多 Web 框架的东西,例如 Request,Response 等 。但它不是一个web服务器,也不是一个web框架。
注意
在 python2中有一个编码问题: 在接收的数据转换一下
str “utf-8” “gbk”
a=“中国” # str
a=u"中国" #unicode
表名常见命名规范:
- 数据库名缩写_表名 db_table
- tbl_表名
