Flask 框架高级(下)

作者: pdnbplus | 发布时间: 2024/06/17 | 阅读量: 167

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)

before_first_request钩子函数

  • 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关掉

errorhandler钩子函数

当在查看处理 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

Flask内置信号源码1

@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)

Flask内置信号结果

捕捉异常的信号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 后,能看到这样的提示

WTF验证源码

先安装

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

Restful源码1

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.reqparseRequestParser 建立解析器
  • 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

Restful参数验证结果1

解析器的参数

主要是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)

Restful规范返回值GET

Restful规范返回值POST

那么还能规范什么类型呢?去看源码

Restful源码2

除了上面的写法外,还可以这样写

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

Restful规范返回值PUT

规范返回值的参数设置

设置重命名属性和默认值

  • 问题:规范给出的属性名和模型内部的属性名不相同(前端用 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规范返回值PUT1

Restful 规范返回值类型

  • fields.List 放置一个列表
  • fields.Nested 放置一个字典
  • fields.List(fields.Nested({xxx:xxx})) 列表里面放字典

Restful规范返回值GET1

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与蓝图结合使用

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')

Restful渲染模板失败

点进api.representation中看源码!

Restful源码

稍作修改

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转码

Restful渲染模板1

Restful渲染模板2