Flask 框架高级(下) -- 潘登同学的 flask 学习笔记
钩子函数
在 Flask 中钩子函数是使用特定的装饰器装饰的函数。
钩子函数概念:是因为钩子函数可以在正常执行的代码中,插入一段自己想要执行的代码。
常见的钩子函数
before_first_request
:处理项目的第一次请求之前执行(初始化)before_request
:在每次请求之前执行。通常可以用这个装饰器来给视图函数增加一些变量。请求已经到达了 Flask,但是还没有进入到具体的视图函数之前调用。一般这个就是在视图函数之前,我们可以把一些后面需要用到的数据先处理好,方便视图函数使用。
from flask import Flask
# 创建对象
app = Flask(__name__)
# 路由地址
@app.route("/")
def index():
print('这是一次请求')
return "pandeng"
@app.before_first_request
def before_first():
print('第一次请求时,需要运行的逻辑')
@app.before_request
def before_first():
print('第每次请求时,需要运行的逻辑')
if __name__ == "__main__":
app.run(debug=True)
context_processor
:上下文处理器。使用这个钩子函数,必须返回一个字典。这个字典中的值在所有模版中都可以使用。
使用场景:如果一些在很多模版中都要用到的变量,那么就可以使用这个钩子函数来返回,而不用在每个视图函数中的 render_template
中去写,这样可以让代码更加简洁和好维护
@app.route("/")
def index():
print('这是一次请求')
return render_template("./06Jinja2模板/index1.html")
@app.context_processor
def context_processor():
# 从数据库中查
return {
"uname":"pd",
"age":19,
"height":185,
"hobby":{
"finance":"quantify",
"sports":"riding",
"it":"Python",
},
}
@app.route('/home/')
def home():
return render_template("./06Jinja2模板/index1.html")
index1.html
采用的还是经典的那个
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>用户主页</title>
<style>
a {
float: right;
}
</style>
</head>
<body>
<a href="{{ url_for('index') }}">回到首页</a>
<h1>{{ uname }}: {{ age }}</h1>
<p>最喜欢的金融课目是{{ hobby.finance }}</p>
<p>最喜欢的体育项目是{{ hobby.sports }}</p>
<p>最喜欢的编程语言是{{ hobby.it }}</p>
</body>
</html>
值得注意的是:当访问http://127.0.0.1:5000/home/
时,是优先执行home()
函数,执行到render_template("./06Jinja2模板/index1.html")
就会去找context_processor()
获取对象数据; 值得注意的是,无论模板中是否会用到数据,只要定义了@app.context_processor
都会去执行一遍;
errorhandler
:在发生一些异常的时候,比如 404 错误,比如 500 错误, 那么如果想要优雅的处理这些错误,就可以使用errorhandler
来出来。
注意
- 在 errorhandler 装饰的钩子函数下,记得要返回相应的状态码。
- 在 errorhandler 装饰的钩子函数中,必须要写一个参数,来接收错误的信息,如果没有参数,就会直接报错。
- 使用
flask.abort
可以手动的抛出相应的错误,比如开发者在发现参数不正确的时候可以自己手动的抛出一个 400 错误。
@app.route('/home/')
def home():
20 / 0
return render_template("./06Jinja2模板/index1.html")
@app.errorhandler(500)
def server_error(error):
return render_template("500.html"),500
if __name__ == "__main__":
app.run() # 记得把debug关掉
当在查看处理 404 错误效果的时候,可以去访问一个不存在的页面,也可以在一个存在的页面下写上abort(404)
来模拟 404 错误
@app.errorhandler(404)
def server_error(error):
return render_template("404.html"),404
@app.route('/jjj/')
def jjj():
abort(404)
Flask 信号机制
flask 中的信号使用的是一个第三方插件,叫做 blinker。通过 pip list 看一下,如果没有安装,通过以下命令即可安装 blinker
pip install blinker
- 自定义信号到调用,要分为 3 步,定义信号、监听信号、发送信号
from flask import Flask,render_template
from blinker import Namespace
# 创建对象
app = Flask(__name__)
# 路由地址
@app.route("/")
def index():
# 3.发送信号
fire_signal.send()
return render_template("./06Jinja2模板/index1.html")
# 1.定义信号
space = Namespace()
fire_signal = space.signal('发送一个信号')
# 2.监听信号
def start_fire(sender):
print('开火')
fire_signal.connect(start_fire)
if __name__ == "__main__":
app.run(debug=True)
记录用户信息
- 定义一个登录的信号,以后用户登录进来以后
- 就发送一个登录信号,然后能够监听这个信号
- 在监听到这个信号以后,就记录当前这个用户登录的信息
- 用信号的方式,记录用户的登录信息即登录日志
沿用之前 session 免密登录的操作,利用信号给他加上日志
在应用同级目录下,创建signals.py
,写下日志要记录的内容
from blinker import Namespace
from flask import request,g
space = Namespace()
login_space = space.signal('登录')
def login_singal(sender):
ip = request.remote_addr
info = f'{ip}:{g.uname}'
with open('login.log','a',encoding='utf-8') as f:
f.write(info+'\n')
login_space.connect(login_singal)
from flask import Flask,render_template,views,request,session,redirect,url_for,g
from signals import login_space
# 创建对象
app = Flask(__name__)
app.secret_key = 'vbdibv'
# 路由地址
@app.route("/")
def index():
return 'Hello'
class LoginView(views.MethodView):
def _jump(self,msg=None):
return render_template('./10_类视图的使用/login.html',msg=msg)
def get(self):
msg = request.args.get('msg')
return self._jump(msg=msg)
def post(self):
uname = request.form.get('uname')
pwd = request.form.get('pwd')
if uname or pwd:
g.uname = uname
login_space.send()
if uname == 'pandeng' and pwd == '123':
# 验证用户信息
session['uname'] = uname
return redirect(url_for('user'))
return self._jump(msg="用户名密码错误")
app.add_url_rule('/login/',view_func=LoginView.as_view('login'))
@app.route('/register/')
def register():
return render_template('./10_类视图的使用/register.html')
@app.route("/user/")
def user():
uname = session.get('uname')
if uname:
# 就从数据库中查hobby, 这里写死了
hobby = {
"finance":"quantify",
"sports":"riding",
"it":"Python",
}
return render_template('./06Jinja2模板/index1.html',uname=uname,hobby=hobby)
else:
return '请先' + '<a href="/login/">登录</a>'
if __name__ == "__main__":
app.run(debug=True)
Flask 内置信号
Flask 内置了 10 个常用的信号
template_rendered
:模版渲染完成后的信号。before_render_template
:模版渲染之前的信号。request_started
:请求开始之前,在到达视图函数之前发送信号。request_finished
:请求结束时,在响应发送给客户端之前发送信号。request_tearing_down
:请求对象被销毁时发送的信号,即使在请求过程中发生异常也会发送信号。got_request_exception
:在请求过程中抛出异常时发送信号,异常本身会通过 exception 传递到订阅(监听)的函数中。一般可以监听这个信号,来记录网站异常信息。appcontext_tearing_down
:应用上下文被销毁时发送的信号。appcontext_pushed
:应用上下文被推入到栈上时发送的信号。appcontext_popped
:应用上下文被推出栈时发送的信号。message_flashed
:调用了 Flask 的flash
方法时发送的信号
模板渲染之前,之后的信号到底是怎么运行的呢? 去看源码,点进render_template
@app.route("/")
def index():
return render_template('base.html')
def render_function(sender,template,context):
print(sender)
print(template)
print(context)
template_rendered.connect(render_function)
捕捉异常的信号got_request_exception
from flask import got_request_exception
def exception_log(sender,exception):
print(sender)
print(exception)
got_request_exception.connect(exception_log)
WTFforms
这个插件库主要有两个作用:
- 1.做表单验证,将用户提交上来的数据进行验证是否符合系统要求。
- 2.模版渲染。 (了解即可)
官网: https://wtforms.readthedocs.io/en/3.0.x/
Flask-WTF
是简化了 WTForms 操作的一个第三方库。WTForms 表单的两个主要功能是验证用户提交数据的合法性以及渲染模板。而Flask-WTF
还包括一些其他的功能:CSRF 保护,文件上传等。
WTForms 表单验证的基本使用
- 1.自定义一个表单类,继承自 wtforms.Form 类。
- 2.定义好需要验证的字段,字段的名字必须和模版中那些需要验证的 input 标签的
name属性
值保持一致。 - 3.在需要验证的字段上,需要指定好具体的数据类型。
- 4.在相关的字段上,指定验证器。(正则)
- 5.以后在视图函数中,只需要使用这个表单类的对象,并且把需要验证的数据,也就是 request.form 传给这个表单类,再调用表单类对象.validate()方法进行,如果返回 True,那么代表用户输入的数据都是符合格式要求的,Flase 则代表用户输入的数据是有问题的。如果验证失败了,那么可以通过表单类对象.errors 来获取具体的错误信息。
from flask import Flask,render_template,request
from wtforms import Form
from wtforms.fields.simple import StringField
from wtforms.validators import Length,EqualTo
app = Flask(__name__)
# 路由地址
@app.route("/")
def index():
return 'Hello'
class RegisterFrom(Form):
# 这里的uname与前端的uanme要一致
uname = StringField(validators=[Length(min=2,max=10,message='用户名长度2-10之间')])
pwd = StringField(validators=[Length(min=2,max=10,message='密码长度2-10之间')])
pwd2 = StringField(validators=[EqualTo('pwd',message='两次密码输入不一致')])
@app.route('/register/',methods=["GET","POST"])
def register():
if request.method == "GET":
return render_template('./20flask中WTF的使用/register.html')
else:
form = RegisterFrom(request.form)
if form.validate():
return '验证成功'
return f'验证失败 {form.errors}'
if __name__ == '__main__':
app.run(debug = True)
WTF 的常用验证器
- 1.Length:字符串长度限制,有 min 和 max 两个值进行限制。
- 2.EqualTo:验证数据是否和另外一个字段相等,常用的就是密码和确认密码两个字段是否相等。
- 3.Email: 验证上传的数据格式是否为邮箱数据格式
- 4.InputRequired:验证该项数据为必填项,即要求该项非空。
- 5.NumberRange:数值的区间,有 min 和 max 两个值限制,如果处在这两个数字之间则满足。
- 6.Regexp:定义正则表达式进行验证,如验证手机号码。
- 7.URL:必须是 URL 的形式,如 https://pd3nbplus.github.io/
- 8.UUID:验证数据是 UUID 类型。
新建一个文件formscheck.py
,存放验证的那个表单类
from wtforms import Form
from wtforms.fields.numeric import IntegerField
from wtforms.fields.simple import StringField
from wtforms.validators import Length,Email,InputRequired,NumberRange,Regexp,URL,UUID
class RegisterFrom(Form):
# 这里的uname与前端的uanme要一致
email = StringField(validators=[InputRequired(),Email()])
uname = StringField(validators=[InputRequired(),Length(min=2,max=10,message='用户名长度2-10之间')])
age = IntegerField(validators=[NumberRange(min=18,max=50)])
phone = StringField(validators=[Regexp(r'1[34578]\d{9}')])
homepage = StringField(validators=[URL()])
uuid = StringField(validators=[UUID()])
<body>
<form action="/register/" method="post">
<table>
<tr>
<th>邮箱:</th>
<td><input type="email" name="email" /></td>
</tr>
<tr>
<th>用户名:</th>
<td><input type="text" name="uname" /></td>
</tr>
<tr>
<th>年龄:</th>
<td><input type="number" name="age" /></td>
</tr>
<tr>
<th>手机号码:</th>
<td><input type="text" name="phone" /></td>
</tr>
<tr>
<th>个人主页:</th>
<td><input type="text" name="phomepage" /></td>
</tr>
<tr>
<th>uuid:</th>
<td><input type="text" name="uuid" /></td>
</tr>
<tr>
<th></th>
<td><input type="submit" value="注册" /></td>
</tr>
</table>
</form>
</body>
注意
: 点进 Email 后,能看到这样的提示
先安装
pip install wtforms[email]
uuid 的生成
from uuid import uuid1
print(uuid1()) # 9811341c-741f-11ec-a678-9c8693cca9e5
from flask import Flask,render_template,request
from formscheck import RegisterFrom
app = Flask(__name__)
# 路由地址
@app.route("/")
def index():
return 'Hello'
@app.route('/register/',methods=["GET","POST"])
def register():
if request.method == "GET":
return render_template('./20flask中WTF的使用/register.html')
else:
form = RegisterFrom(request.form)
if form.validate():
return '验证成功'
return f'验证失败 {form.errors}'
if __name__ == '__main__':
app.run(debug = True)
自定义表单验证器
当然,按照惯例,有常用必然有自定义
自定义验证器步骤
- 定义一个方法,方法的名字规则是:
validate_字段名(self,field)
- 在方法中,使用
field.data
可以获取到这个字段的具体的值。 - 验证时,如果数据满足条件,那么可以什么都不做。如果验证失败,那么应该抛出一个
wtforms.validators.ValidationError
的异常,并且把验证失败的信息传到这个异常类中。
需求: 验证码验证功能
<body>
<form action="/register2/" method="post">
<table>
<tr>
<th>用户名:</th>
<td><input type="text" name="uname" /></td>
</tr>
<tr>
<th>密码:</th>
<td><input type="password" name="pwd" /></td>
</tr>
<tr>
<th>验证码:</th>
<td>
<input type="number" name="code" maxlength="4" /><span
style="background-color:red"
>{{code}}</span
>
</td>
</tr>
<tr>
<th></th>
<td><input type="submit" value="注册" /></td>
</tr>
</table>
</form>
</body>
class RegisterFrom2(Form):
uname = StringField(validators=[InputRequired(),Length(min=2,max=10,message='用户名长度2-10之间')])
pwd = StringField(validators=[InputRequired()])
code = StringField(validators=[InputRequired(),Length(4,4,message="验证码输入不完整")])
def validate_code(self,field):
font_code = field.data # 前端数据
server_code = session.get('code') # 后端时间
if str(font_code) != str(server_code):
raise ValueError('验证码错误')
注意,验证码也应该加盐,不然的话,人家直接操作 cookie 就能进行操作,达不到验证码的功能
from random import randint
from flask import Flask,render_template,request,session
from formscheck import RegisterFrom2
from random import randint
app = Flask(__name__)
app.secret_key = 'caiouvbauo'
# 路由地址
@app.route("/")
def index():
return 'Hello'
@app.route('/register2/',methods=["GET","POST"])
def register2():
if request.method == "GET":
code = randint(1000,9999)
session['code'] = code
return render_template('./20flask中WTF的使用/register2.html',code=code)
else:
form = RegisterFrom2(request.form)
if form.validate():
return '验证成功'
return f'验证失败 {form.errors}'
if __name__ == '__main__':
app.run(debug = True)
渲染模版是 WTForms 的第二个作用,不过,我们只需要了解即可,不需要花太多精力和时间去研究它。
文件安全上传
- 1.在模版 html 中,表单需要指定
enctype='multipart/form-data'
才能上传文件。 - 2.在后台如果想要获取上传的文件,那么应该使用 request.files.get('文件名') 来获取。
- 3.保存文件之前,先要使用 werkzeug.utils.secure_filename 来对上传上来的文件名进行一个过滤。能保证不会有安全问题
- 4.获取到上传上来的文件后,使用 文件对象.save(路径) 方法来保存文件。路径=完整路径=路径名+文件名
访问文件
从服务器上读取文件,应该定义一个 url 与视图函数,来获取指定的文件。在这个视图函数中,使用 send_from_directory
(文件的目录,文件名) 来获取。
from flask import Flask, redirect,render_template,request, send_from_directory
from werkzeug.utils import secure_filename
import os
app = Flask(__name__)
UPLOAD_PATH = os.path.join(os.path.dirname(__file__),'img') # 当前文件的上级目录的下级img目录
@app.route('/')
def index():
return "hello"
@app.route('/upload/',methods=['GET','POST'])
def upload():
if request.method == "GET":
return render_template('./21文件上传/index.html')
else:
img_file = request.files.get('pichead')
file_name = img_file.filename
filename = secure_filename(file_name)
# 保存文件
img_file.save(os.path.join(UPLOAD_PATH,filename))
return "上传文件成功"
@app.route('/download/<filename>')
def download_file(filename):
return send_from_directory(UPLOAD_PATH,filename)
if __name__ == '__main__':
app.run(debug=True)
利用 flask-wtf 验证上传的文件
from flask import Flask,render_template,request, send_from_directory
from werkzeug.utils import secure_filename
from werkzeug.datastructures import CombinedMultiDict
from formscheck import UploadForm
import os
app = Flask(__name__)
UPLOAD_PATH = os.path.join(os.path.dirname(__file__),'img') # 当前文件的上级目录的下级img目录
@app.route('/')
def index():
return "hello"
@app.route('/upload/',methods=['GET','POST'])
def upload():
if request.method == "GET":
return render_template('./21文件上传/index.html')
else:
form = UploadForm(CombinedMultiDict([request.form,request.files]))
if form.validate():
img_file = form.pichead.data
file_name = img_file.filename
filename = secure_filename(file_name)
# 保存文件
img_file.save(os.path.join(UPLOAD_PATH,filename))
return "上传文件成功"
else:
return f'上传失败{form.errors}'
# 文件下载
@app.route('/download/<filename>')
def download_file(filename):
return send_from_directory(UPLOAD_PATH,filename)
if __name__ == '__main__':
app.run(debug=True)
from wtforms import Form,FileField
from flask_wtf.file import FileAllowed,FileRequired
class UploadForm(Form):
pichead = FileField(validators=[FileRequired(),FileAllowed(['png','jpg'])])
RESTFul
Restful 接口规范
- 1.REST 指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful。(RESTful 是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件)
- 2.它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次。
- 3.RESTful 接口规范是用于在前端与后台进行通信的一套规范。使用这个规范可以让前后端开发变得更加轻松。
使用场景:一个系统的数据库数据,展现的平台有 PC 端、移动端、app 端、ios 端。
协议:用 http 或者 https 协议
数据传输的格式应该都用 json 格式。
url 链接中,不能有动词,只能有名词。并且对于一些名词,如果出现复数,那么应该在后面加 s。
比如:获取新闻列表,应该使用 /news/
,而不应该使用/get_news/
HTTP 请求方式:
- 1.GET:从服务器上获取资源。
- 2.POST:在服务器上新增或者修改一个资源。
- 3.PUT:在服务器上更新资源。(客户端提供所有改变后的数据)
- 4.PATCH:在服务器上更新资源。(客户端只提供需要改变的属性)
- 5.DELETE:从服务器上删除资源。
状态码:
状态码 | 原因描述 | 描述 |
---|---|---|
200 | OK | 服务器成功响应客户端的请求。 |
400 | INVALID REQUEST | 用户发出的请求有错误,服务器没有进行新建或修改数据的操作 |
401 | Unauthorized | 用户没有权限访问这个请求 |
403 | Forbidden | 因为某些原因禁止访问这个请求 |
404 | NOT FOUND | 用户请求的 url 不存在 |
406 | NOT Acceptable | 用户请求不被服务器接收(比如服务器期望客户端发送某个字段,但是没有发送)。 |
500 | Internal server error | 服务器内部错误,比如遇到 bug |
Restful 的基本使用
- 优点:
Flask-Restful 是一个专门用来写 restful api 的一个插件。使用它可以快速的集成 restful api 接口功能。在系统的纯 api 的后台中,这个插件可以帮助我们节省很多时间。
- 缺点:
如果在普通的网站中,这个插件就没有优势了,因为在普通的网站开发中,是需要去渲染 HTML 代码的,而 Flask-Restful 在每个请求中都是返回 json 格式的数据。
安装flask-restful
pip install flask-restful
基本使用
定义 Restful 的类视图:
- 1.从
flask_restful
中导入Api
,来创建一个api
对象。 - 2.写一个类视图,让他继承自
Resource
类,然后在这个里面,使用你想要的请求方式来定义相应的方法,比如你想要将这个类视图只能采用post
请求,那么就定义一个post
方法。 - 3.使用
api.add_resource
来添加类视图与url
。
from urllib.request import Request
from flask import Flask, url_for
from flask_restful import Resource,Api
app = Flask(__name__)
# 建立Api对象,并绑定应用APP
api = Api(app)
class LoginView(Resource):
def get(self):
return {'flag':True}
def post(slef):
return {'flag':False}
# 建立路由映射, 可以放多个路由地址,
api.add_resource(LoginView,'/login/','/login2/',endpoint='login')
# 反向查找
with app.test_request_context():
# 默认没有写endpoint下,反向url的时候要小写类名
# 如果有多个url,则返回第一个
# print(url_for('loginView'))
print(url_for('login'))
if __name__ == '__main__':
app.run(debug=True)
总结
- 如果你想返回 json 数据,那么就使用 flask_restful,如果你是想渲染模版,那么还是采用之前的方式,就是
app.route
的方式。 - url 还是跟之前的一样,可以传递参数。也跟之前的不一样,可以指定多个 url。
- endpoint 是用来给 url_for 反转 url 的时候指定的。如果不写 endpoint,那么将会使用视图的类名的小写来作为 endpoint。
- add_resource 的第二个参数是访问这个视图函数的 url,这个 url 可以跟之前的 route 一样,可以传递参数,并且还有一点不同的是,这个方法可以传递多个 url 来指定这个视图函数
Restful 参数验证
Flask-Restful 插件提供了类似 WTForms 来验证提交的数据是否合法的包,叫做reqparse
。
基本用法
- 1 通过
flask_restful.reqparse
中RequestParser
建立解析器 - 2 通过
RequestParser
中的add_argument
方法定义字段与解析规则 - 3 通过
RequestParser
中的parse_args
来解析参数- 1 解析正确,返回正确参数
- 2 解析错误,返回错误信息给前端
from flask import Flask, url_for
from flask_restful import Resource,Api
from flask_restful.reqparse import RequestParser
app = Flask(__name__)
# 建立Api对象,并绑定应用APP
api = Api(app)
class RegisterView(Resource):
def post(self):
# 建立解析器
parser = RequestParser()
# 定义数据的解析规则
parser.add_argument('uname', type=str, required=True,help='用户名认证错误',trim=True)
# 解析数据
args = parser.parse_args()
# 正确。直接获取参数
print(args)
# 错误。回馈到前端
# 响应数据
return {'mas':'注册成功'}
# 建立路由映射, 可以放多个路由地址,
api.add_resource(RegisterView,'/register/',endpoint='register')
# 建立映射关系
with app.test_request_context():
# 默认没有写endpoint下,反向url的时候要小写类名
# 如果有多个url,则返回第一个
# print(url_for('registerview'))
print(url_for('register'))
if __name__ == '__main__':
app.run(debug=True)
打开postman
,尝试 POST 失败,在Body
中的form-data
中随便设置一个uname
就行,再次 POST
解析器的参数
主要是add_argument
中的参数
- default:默认值,如果这个参数没有值,那么将使用这个参数指定的默认值。
- required:是否必须。默认为 False,如果设置为 True,那么这个参数就必须提交上来
- type:这个参数的数据类型,如果指定,那么将使用指定的数据类型来强制转换提交上来的值。可以使用 python 自带的一些数据类型(如 str 或者 int),也可以使用 flask_restful.inputs 下的一些特定的数据类型来强制转换。
- url:会判断这个参数的值是否是一个 url,如果不是,那么就会抛出异常。
- regex:正则表达式。
- date:将这个字符串转换为 datetime.date 数据类型。如果转换不成功,则会抛出一个异常.(因为面对(2022-1-13)这样的数据,datetime 可能不好转换)
- choices:固定选项。提交上来的值只有满足这个选项中的值才符合验证通过,否则验证不通过。
- help:错误信息。如果验证失败后,将会使用这个参数指定的值作为错误信息。
- trim:是否要去掉前后的空格。
from flask import Flask, url_for
from flask_restful import Resource,Api,inputs
from flask_restful.reqparse import RequestParser
app = Flask(__name__)
# 建立Api对象,并绑定应用APP
api = Api(app)
class RegisterView(Resource):
def post(self):
# 建立解析器
parser = RequestParser()
# 定义数据的解析规则
parser.add_argument('uname', type=str, required=True,help='用户名认证错误',trim=True)
parser.add_argument('pwd', type=str, help='密码设置错误',default='123456')
parser.add_argument('age', type=int, required=True, help='年龄不对劲',trim=True)
parser.add_argument('phone', type=inputs.regex('^1[3-9]\d{9}$'), required=True,help='手机号输入不合法',trim=True)
parser.add_argument('gender', type=str, required=True,help='你的性别呢?',choices=['男', '女', '保密'])
parser.add_argument('birthday', type=inputs.date,help='生日认证错误',trim=True)
parser.add_argument('homepage', type=inputs.url, required=True,help='个人主页仍在错误',trim=True)
# 解析数据
args = parser.parse_args()
# 正确。直接获取参数
print(args)
# 错误。回馈到前端
# 响应数据
return {'msg':'注册成功'}
# 建立路由映射, 可以放多个路由地址,
api.add_resource(RegisterView,'/register/',endpoint='register')
if __name__ == '__main__':
app.run(debug=True)
Restful 规范返回值
对于一个类视图,可以指定好一些字段做标准化用于返回。以后使用 ORM 模型或者自定义模型的时候,他会自动的获取模型中的相应的字段,生成 json 格式数据,然后再返回给客户端。
from flask import Flask
from flask_restful import Resource,Api,fields,marshal_with
app = Flask(__name__)
api = Api(app)
class RegisterView(Resource):
resource_fields = {
'code':fields.Integer,
'msg':fields.String,
}
@marshal_with(resource_fields)
def get(self):
return {'code':200,'msg':'访问成功!'}
@marshal_with(resource_fields)
def post(self):
return {'msg':'注册成功!'}
api.add_resource(RegisterView,'/register/',endpoint='register')
if __name__ == '__main__':
app.run(debug=True)
那么还能规范什么类型呢?去看源码
除了上面的写法外,还可以这样写
class News:
def __init__(self,code,msg,state,content):
self.code = code
self.msg = msg
self.state =state
self.content = content
class RegisterView(Resource):
@marshal_with(resource_fields)
def put(self):
# 在返回对象时,会自动在对象中获取与约定好的字段,并获取封装成json。
new = News(404,'我是News对象的一个msg属性','PC端','hhhh')
return new
规范返回值的参数设置
设置重命名属性和默认值
- 问题:规范给出的属性名和模型内部的属性名不相同(前端用 uname,后端用 name)
使用 attribute
配置这种映射,比如: fields.String(attribute='username')
- 问题:某些字段,没有值,但想给一个值做为默认值
使用 default
指定默认值,比如: fields.String(default='pandeng')
class News:
def __init__(self,code,msg,state,content,uname):
self.code = code
self.msg = msg
self.state =state
self.content = content
self.uname = uname
class RegisterView(Resource):
resource_fields = {
'code':fields.Integer,
'msg':fields.String,
'state':fields.String(default="微波炉"),
'name':fields.String(attribute='uname'), # 设置完之后,就不能通过{`name`:'xxx'}来操作了
}
@marshal_with(resource_fields)
def get(self):
return {'code':200,'msg':'访问成功!'}
@marshal_with(resource_fields)
def post(self):
return {'msg':'注册成功!'}
@marshal_with(resource_fields)
def put(self):
new = News(404,'我是News对象的一个msg属性','PC端','hhhh','Pandeng')
return new
api.add_resource(RegisterView,'/register/',endpoint='register')
Restful 规范返回值类型
- fields.List 放置一个列表
- fields.Nested 放置一个字典
- fields.List(fields.Nested({xxx:xxx})) 列表里面放字典
Restful 与蓝图结合使用
在蓝图中,如果使用 Flask_RESTful,创建 Api 对象的时候,传入蓝图对象即可,不再是传入 app 对象
找回之前写蓝图的文件,依据功能模块写的蓝图,__init__.py
无需改动,对view.py
修改即可
'''
修改为下面内容
@user_bp.route('/register/')
def register():
return '注册模块'
'''
class RegisterView(Resource):
resource_fields = {
'code':fields.Integer,
'msg':fields.String,
}
@marshal_with(resource_fields)
def get(self):
return {'code':200,'msg':'访问成功!'}
api = Api(user_bp) # 注意这里Api里面的内容是user_bp,而不是app
api.add_resource(RegisterView,'/register/',endpoint='register')
然后main.py
都无需改动;
Restful 渲染模板
渲染模版就是在 Flask_RESTful 的类视图中要返回 html 片段代码,或者是整个 html 文件代码。
如果需要浏览器渲染模板内容应该使用 api.representation
这个装饰器来定义一个函数,在这个函数中,应该对 html
代码进行一个封装,再返回。
注意
api.representation
装饰器修饰的函数必须返回一个 Response
对象
未处理前代码
from flask import Flask,render_template,Response
from flask_restful import Api,Resource
app = Flask(__name__)
api = Api(app)
class HomeView(Resource):
def get(self):
return {"msg":"这个是个人主页"}
class IndexView(Resource):
def get(self):
return render_template('./06Jinja2模板/index.html')
api.add_resource(HomeView,'/home/',endpoint='home')
api.add_resource(IndexView,'/index/',endpoint='index')
点进api.representation
中看源码!
稍作修改
app = Flask(__name__)
# 如果想要前端的中文不再是\u这样的编码,可以加如下参数配置
app.config['RESTFUL_JSON'] = dict(ensure_ascii=False)
api = Api(app)
class HomeView(Resource):
def get(self):
return {"msg":"这个是个人主页"}
class IndexView(Resource):
def get(self):
return render_template('./06Jinja2模板/index.html')
api.add_resource(IndexView,'/index/',endpoint='index')
api.add_resource(HomeView,'/home/',endpoint='home')
# 在前端渲染而不是返回代码原文
@api.representation('text/html')
def out_html(data,code,headers):
# 必须返回一个response对象
if isinstance(data, str): # 防止JSON数据丢失
resp = Response(data)
return resp
else:
return Response(json.dumps(data,ensure_ascii=False).encode('gbk')) # 对JSON转码