Python flask-restful 框架讲解

1、简介

Django 和 Flask 一直都是 Python 开发 Web 的首选,而 Flask 的微内核更适用于现在的云原生微服务框架。但是 Flask 只是一个微型的 Web 引擎,所以我们需要扩展 Flask 使其发挥出更强悍的功能。

python flask框架详解:https://blog.csdn.net/shifengboy/article/details/114274271

Flask-RESTful

Flask-RESTful 就是 Flask 扩展中的佼佼者,它增加了对快速构建 RESTful API 的支持,将 Flask 封装了一层,使其更容易、更快速、更便捷的开发 RESTful API。

GitHub:https://github.com/flask-restful/flask-restful

英文文档:https://flask-restful.readthedocs.io/en/latest/

中文文档:http://www.pythondoc.com/Flask-RESTful/

Flask-RESTPlus

我们知道 Flask-RESTful 是 Flask 的扩展,而 Flask-RESTPlus 则是 Flask-RESTful 的扩展,对 Flask-RESTful 完全兼容且对其进行增强了接口文档的支持。

Flask-RESTPlus 提供了一个连贯的装饰器和工具集合来描述文档 API 所需要的参数和对象,并使用 Swagger 将其解析成正确的接口文档。

GitHub:https://github.com/noirbizarre/flask-restplus

Docs:https://flask-restplus.readthedocs.io/en/latest/

Flask-RESTX

既然已经有了很完美的 Flask-RESTPlus,那为什么还需要 Flask-RESTX 呢?

其实在很长时间中我都一直都在使用 Flask-RESTPlus,但是难受的是作者丢了!没错,就是物理意义上的丢了,Flask-RESTPlus 这个项目团队的成员都找不动他了,团队为了持续维护这个项目只能另开一个分支,将 Flask-RESTPlus 继续延续下去,继续延续后的项目就是 Flask-RESTX。Flask-RESTX 完全兼容 Flask-RESTPlusFlask-RESTPlus 项目里积攒的问题、BUG 都由 Flask-RESTX 完全继承并且社区团队在积极维护汇总,

GitHub:https://github.com/python-restx/flask-restx

Docs:https://flask-restx.readthedocs.io/en/latest/

FastAPI

FastAPI 是独立于 Flask 的新式 Web 框架,虽然能看到很多 Flask 和相关扩展的影子,但是它也成为不可忽视的 Web 框架之一,而且 FastAPI 还号称是最快的Python框架之一。

GitHub:https://github.com/tiangolo/fastapi

Docs:https://fastapi.tiangolo.com

2、快速入门

安装:pip install flask-restful

简单示例

一个最小的 Flask-RESTful API 像这样:

python 复制代码
from flask import Flask
from flask_restful import Resource, Api

app = Flask(__name__)
api = Api(app)


class HelloWorld(Resource):
    def get(self):
        return {'hello': 'world'}


api.add_resource(HelloWorld, '/')

if __name__ == '__main__':
    app.run(debug=True)

把上述代码保存为 api.py 并且在你的 Python 解释器中运行它。需要注意地是我们已经启用了 Flask 调试 模式,这种模式提供了代码的重载以及更好的错误信息。调试模式绝不能在生产环境下使用。

$ python api.py

* Running on http://127.0.0.1:5000/

现在打开一个新的命令行窗口使用 curl 测试你的 API:

$ curl http://127.0.0.1:5000/

{"hello": "world"}

"资源(视图) 和 路由" 绑定

视图中的类 需要继承 flask_restful 中的 Resource

Flask-RESTful 提供的主要构建块是资源。资源构建在 Flask 可插入视图之上,只需在资源上定义方法,就可以轻松访问多个 HTTP 方法。一个 todo 应用程序的基本 CRUD 资源是这样的:

python 复制代码
from flask import Flask, request
from flask_restful import Resource, Api

app = Flask(__name__)
api = Api(app)

todos = {}


class TodoSimple(Resource):
    def get(self, todo_id):
        return {todo_id: todos[todo_id]}

    def put(self, todo_id):
        todos[todo_id] = request.form['data']
        return {todo_id: todos[todo_id]}


api.add_resource(TodoSimple, '/<string:todo_id>')

if __name__ == '__main__':
    app.run(debug=True)

你可以尝试这样:

$ curl http://localhost:5000/todo1 -d "data=Remember the milk" -X PUT

{"todo1": "Remember the milk"}

$ curl http://localhost:5000/todo1

{"todo1": "Remember the milk"}

$ curl http://localhost:5000/todo2 -d "data=Change my brakepads" -X PUT

{"todo2": "Change my brakepads"}

$ curl http://localhost:5000/todo2

{"todo2": "Change my brakepads"}

或者如果你安装了 requests 库的话,可以从 python shell 中运行:

>>> from requests import put, get

>>> put('http://localhost:5000/todo1', data={'data': 'Remember the milk'}).json()

{u'todo1': u'Remember the milk'}

>>> get('http://localhost:5000/todo1').json()

{u'todo1': u'Remember the milk'}

>>> put('http://localhost:5000/todo2', data={'data': 'Change my brakepads'}).json()

{u'todo2': u'Change my brakepads'}

>>> get('http://localhost:5000/todo2').json()

{u'todo2': u'Change my brakepads'}

Flask-RESTful 支持视图方法多种类型的返回值。同 Flask 一样,你可以返回任一迭代器,它将会被转换成一个包含原始 Flask 响应对象的响应。Flask-RESTful 也支持使用多个返回值来设置响应代码和响应头,如下所示:

python 复制代码
from flask import Flask, request
from flask_restful import Resource, Api

app = Flask(__name__)
api = Api(app)

todos = {}


class TodoSimple(Resource):
    def get(self, todo_id):
        return {todo_id: todos[todo_id]}

    def put(self, todo_id):
        todos[todo_id] = request.form['data']
        return {todo_id: todos[todo_id]}


class Todo1(Resource):
    def get(self):
        # Default to 200 OK
        return {'task': 'Hello world'}


class Todo2(Resource):
    def get(self):
        # Set the response code to 201
        return {'task': 'Hello world'}, 201


class Todo3(Resource):
    def get(self):
        # Set the response code to 201 and return custom headers
        return {'task': 'Hello world'}, 201, {'Etag': 'some-opaque-string'}


api.add_resource(TodoSimple, '/<string:todo_id>')

if __name__ == '__main__':
    app.run(debug=True)

测试

curl -i http://127.0.0.1:5000/todo1

curl -i http://127.0.0.1:5000/todo2

curl -i http://127.0.0.1:5000/todo3

端点 ( Endpoints )

很多时候,在一个 API 中,你的资源会有多个 url。可以将多个 url 传递给 Api 对象上的 add _ resource ()方法。每一个都将被路由到Resource

api.add_resource(HelloWorld,

'/',

'/hello')

你也可以为你的资源方法指定 endpoint 参数。

api.add_resource(Todo,

'/todo/<int:todo_id>', endpoint='todo_ep')

示例

python 复制代码
from flask import Flask
from flask_restful import Resource, Api

app = Flask(__name__)
api = Api(app)


class HelloWorld(Resource):
    def get(self):
        return {'hello': 'world'}


class Todo(Resource):
    def get(self, todo_id):
        # Default to 200 OK
        return {'task': 'Hello world'}


api.add_resource(HelloWorld, '/', '/hello')
api.add_resource(Todo, '/todo/<int:todo_id>', endpoint='todo_ep')

if __name__ == '__main__':
    app.run(debug=True)

测试

curl http://127.0.0.1:5000/

curl http://127.0.0.1:5000/hello

curl http://127.0.0.1:5000/todo/1

curl http://127.0.0.1:5000/todo/2

参数解析

尽管 Flask 能够简单地访问请求数据(比如查询字符串或者 POST 表单编码的数据),验证表单数据仍然很痛苦。Flask-RESTful 内置了支持验证请求数据,它使用了一个类似 argparse 的库。

示例

python 复制代码
from flask import Flask
from flask_restful import reqparse, Api, Resource

app = Flask(__name__)
api = Api(app)

parser = reqparse.RequestParser()
parser.add_argument('rate', type=int, help='Rate to charge for this resource')


class Todo(Resource):
    def post(self):
        args = parser.parse_args()
        print(args)
        # Default to 200 OK
        return {'task': 'Hello world'}


api.add_resource(Todo, '/todos')

if __name__ == '__main__':
    app.run(debug=True)

测试

curl -d 'rate=100' http://127.0.0.1:5000/todos

curl -d 'rate=foo' http://127.0.0.1:5000/todos

与 argparse 模块不同,reqparse. RequestParser.parse _ args ()返回 Python 字典,而不是自定义数据结构。

输入模块提供了许多常用的转换函数,例如 inputs.date ()和 inputs.url ()。

使用 strict = True 调用 parse _ args 可以确保在请求包含您的解析器没有定义的参数时抛出错误。

args = parser.parse_args(strict=True)

curl -d 'rate2=foo' http://127.0.0.1:5000/todos

数据格式化

默认情况下,在你的返回迭代中所有字段将会原样呈现。尽管当你刚刚处理 Python 数据结构的时候,觉得这是一个伟大的工作,但是当实际处理它们的时候,会觉得十分沮丧和枯燥。为了解决这个问题,Flask-RESTful 提供了 fields 模块和 marshal_with() 装饰器。类似 Django ORM 和 WTForm,你可以使用 fields 模块来在你的响应中格式化结构。

python 复制代码
from flask import Flask
from flask_restful import fields, marshal_with, Resource, Api

app = Flask(__name__)
api = Api(app)

resource_fields = {
    'task': fields.String,
    'uri': fields.Url('todo')
}


class TodoDao(object):
    def __init__(self, todo_id, task):
        self.todo_id = todo_id
        self.task = task

        # This field will not be sent in the response
        self.status = 'active'


class Todo(Resource):
    @marshal_with(resource_fields)
    def get(self, **kwargs):
        return TodoDao(todo_id='my_todo', task='Remember the milk')


api.add_resource(Todo, '/todo')

if __name__ == '__main__':
    app.run(debug=True)

上面的例子接受一个 python 对象并准备将其序列化。marshal_with() 装饰器将会应用到由 resource_fields 描述的转换。从对象中提取的唯一字段是 task。fields.Url 域是一个特殊的域,它接受端点(endpoint)名称作为参数并且在响应中为该端点生成一个 URL。许多你需要的字段类型都已经包含在内。请参阅 fields 指南获取一个完整的列表。

$ curl http://127.0.0.1:5000/todo

{

"task": "Remember the milk",

"uri": "/todo"

}

完整 示例

python 复制代码
from flask import Flask
from flask_restful import reqparse, abort, Api, Resource

app = Flask(__name__)
api = Api(app)

TODOS = {
    'todo1': {'task': 'build an API'},
    'todo2': {'task': '?????'},
    'todo3': {'task': 'profit!'},
}


def abort_if_todo_doesnt_exist(todo_id):
    if todo_id not in TODOS:
        abort(404, message="Todo {} doesn't exist".format(todo_id))


parser = reqparse.RequestParser()
parser.add_argument('task')


# Todo
# shows a single todo item and lets you delete a todo item
class Todo(Resource):
    def get(self, todo_id):
        abort_if_todo_doesnt_exist(todo_id)
        return TODOS[todo_id]

    def delete(self, todo_id):
        abort_if_todo_doesnt_exist(todo_id)
        del TODOS[todo_id]
        return '', 204

    def put(self, todo_id):
        args = parser.parse_args()
        task = {'task': args['task']}
        TODOS[todo_id] = task
        return task, 201


# TodoList
# shows a list of all todos, and lets you POST to add new tasks
class TodoList(Resource):
    def get(self):
        return TODOS

    def post(self):
        args = parser.parse_args()
        todo_id = int(max(TODOS.keys()).lstrip('todo')) + 1
        todo_id = 'todo%i' % todo_id
        TODOS[todo_id] = {'task': args['task']}
        return TODOS[todo_id], 201


##
## Actually setup the Api resource routing here
##
api.add_resource(TodoList, '/todos')
api.add_resource(Todo, '/todos/<todo_id>')

if __name__ == '__main__':
    app.run(debug=True)

测试

curl http://localhost:5000/todos 获取列表

curl http://localhost:5000/todos/todo3 获取一个单独任务

删除一个任务

$ curl http://localhost:5000/todos/todo2 -X DELETE -v

* Trying ::1...

* TCP_NODELAY set

* Connection failed

* connect to ::1 port 5000 failed: Connection refused

* Trying 127.0.0.1...

* TCP_NODELAY set

* Connected to localhost (127.0.0.1) port 5000 (#0)

> DELETE /todos/todo2 HTTP/1.1

> Host: localhost:5000

> User-Agent: curl/7.64.1

> Accept: */*

>

* HTTP 1.0, assume close after body

< HTTP/1.0 204 NO CONTENT

< Content-Type: application/json

< Server: Werkzeug/1.0.1 Python/3.9.2

< Date: Sat, 06 Mar 2021 03:29:33 GMT

<

* Closing connection 0

增加一个新的任务

$ curl http://localhost:5000/todos -d "task=something new" -X POST -v

Note: Unnecessary use of -X or --request, POST is already inferred.

* Trying ::1...

* TCP_NODELAY set

* Connection failed

* connect to ::1 port 5000 failed: Connection refused

* Trying 127.0.0.1...

* TCP_NODELAY set

* Connected to localhost (127.0.0.1) port 5000 (#0)

> POST /todos HTTP/1.1

> Host: localhost:5000

> User-Agent: curl/7.64.1

> Accept: */*

> Content-Length: 18

> Content-Type: application/x-www-form-urlencoded

>

* upload completely sent off: 18 out of 18 bytes

* HTTP 1.0, assume close after body

< HTTP/1.0 201 CREATED

< Content-Type: application/json

< Content-Length: 32

< Server: Werkzeug/1.0.1 Python/3.9.2

< Date: Sat, 06 Mar 2021 03:31:02 GMT

<

{

"task": "something new"

}

* Closing connection 0

更新一个任务

$ curl http://localhost:5000/todos/todo3 -d "task=something different" -X PUT -v

* Trying ::1...

* TCP_NODELAY set

* Connection failed

* connect to ::1 port 5000 failed: Connection refused

* Trying 127.0.0.1...

* TCP_NODELAY set

* Connected to localhost (127.0.0.1) port 5000 (#0)

> PUT /todos/todo3 HTTP/1.1

> Host: localhost:5000

> User-Agent: curl/7.64.1

> Accept: */*

> Content-Length: 24

> Content-Type: application/x-www-form-urlencoded

>

* upload completely sent off: 24 out of 24 bytes

* HTTP 1.0, assume close after body

< HTTP/1.0 201 CREATED

< Content-Type: application/json

< Content-Length: 38

< Server: Werkzeug/1.0.1 Python/3.9.2

< Date: Sat, 06 Mar 2021 03:32:44 GMT

<

{

"task": "something different"

}

* Closing connection 0

获取最新列表

$ curl http://localhost:5000/todos

{

"todo1": {

"task": "build an API"

},

"todo3": {

"task": "something different"

},

"todo4": {

"task": "something new"

}

}

使用 gunicorn、异步

安装:pip install gunicorn

gunicorn 部署 flask 项目简单示例:https://blog.csdn.net/feng_1_ying/article/details/107469379

python 复制代码
from flask import *
from flask_restful import  Api,Resource,reqparse
from gevent import monkey
from gevent.pywsgi import WSGIServer
 
 
monkey.patch_all()
 
app=Flask(__name__)
api=Api(app)
 
class infoView(Resource):
    def post(self):
        parser = reqparse.RequestParser()
        parser.add_argument('username', type=str)
        args = parser.parse_args()
        return args
api.add_resource(infoView,'/info/')
if __name__ == '__main__':
    http_server = WSGIServer(('10.45.7.11', int(5001)), app)
    http_server.serve_forever()
 
# 部署方案
# gunicorn -k gevent -b 10.45.7.11:5001 flask_restful_test:app

flask_restful.marshal 过滤 数据

示例:

python 复制代码
from flask import Flask
from flask_restful import Api, Resource, fields, marshal

app = Flask(__name__)
api = Api(app)

# 定义一个示例数据
data = {
    'name_1': 'John',
    'age_1': 30,
    'email_address': 'john@example.com',
}

# 定义字段格式和过滤器
resource_fields = {
    'name_1': fields.String,
    'name_2': fields.String,
    'age_2': fields.Integer,
    # 可以使用 attribute 指定源数据的键
    'email': fields.String(attribute='email_address')
}


class HelloWorld(Resource):
    def get(self):
        # 序列化数据
        serialized_data = marshal(data, resource_fields)
        return serialized_data


api.add_resource(HelloWorld, '/hello', '/', '/world')

if __name__ == '__main__':
    app.run(debug=True)

flask_sqlalchemy

https://flask-sqlalchemy.palletsprojects.com/en/3.0.x/

Flask-SQLAlchemy 是一个与 Flask 配合使用的 SQLAlchemy 扩展,它提供了在 Flask 应用程序中使用 SQLAlchemy 进行数据库操作的便利方法和工具。

安装 flask_sqlalchemy 扩展:pip install flask_sqlalchemy

python 复制代码
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///mydatabase.db'
db = SQLAlchemy(app)


class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)

    def __repr__(self):
        return '<User %r>' % self.username


# 创建数据表
db.create_all()

# 插入数据
user = User(username='john', email='john@example.com')
db.session.add(user)
db.session.commit()

# 查询数据
users = User.query.all()
for user in users:
    print(user.username)

# 更新数据
user = User.query.filter_by(username='john').first()
user.email = 'newemail@example.com'
db.session.commit()

# 删除数据
user = User.query.filter_by(username='john').first()
db.session.delete(user)
db.session.commit()


if __name__ == '__main__':
    app.run(debug=True)

Flask_migrate

在开发时,以删除表再重建的方式更新数据库简单直接,但明显的缺陷是会丢掉数据库中的所有数据。在生产环境下,没有人想把数据都删除掉,这时需要使用数据库迁移工具来完成这个工作。SQLAlchemy的开发者Michael Bayer写了一个数据库迁移工作---Alembic来帮助我们实现数据库的迁移,数据库迁移工具可以在不破坏数据的情况下更新数据库表的结构。蒸馏器(Alembic)是炼金术士最重要的工具,要学习SQL炼金术(SQLAlchemy),当然要掌握蒸馏器的使用。

扩展 Flask-Migrate 继承了 Alembic,提供了一些 flask 命令来简化迁移工作,可以使用它来迁移数据库。

python 复制代码
from flask import Flask
from flask import render_template
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate

app = Flask(__name__)
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://user:passwd@host/my_db'
app.config['SQLALCHEMY_DATABASE_URI'] = SQLALCHEMY_DATABASE_URI

db = SQLAlchemy(app)
Migrate(app, db)


class User(db.Model):
    __tablename__ = 'users'

    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(250), unique=True, nullable=False)
    username = db.Column(db.String(250), unique=True, nullable=False)
    password = db.Column(db.String(250), nullable=False)
    login_time = db.Column(db.Integer)

    def __init__(self, username, password, email):
        self.username = username
        self.password = password
        self.email = email

    def __str__(self):
        return "Users(id='%s')" % self.id


@app.route('/')
def index():
    return 'Hello World'


if __name__ == '__main__':
    app.run(debug=True)

3、总结:整体流程

Restful 运用于前后端分离

前端:app,小程序,pc页面

后端:没有页面,mvt:模型模板视图 去掉了t模板

mv:模型 视图

模型的使用:更原来的用法相同

视图:api构建视图

创建 api 对象在exts扩展包中

在创建app的函数中绑定API 等价于 api.init_app(app=app)

api = Api(app=app)

在创建app的函数中绑定db 等价于 db.init_app(app=app)

db = SQLAlchemy(api=蓝图对象)

定义 视图

视图中的类需要基础 flask_restful 中的 Resource

python 复制代码
from flask_restful import Resource


class xxxApi(Resource):
    def get(self):
        pass

    def post(self):
        pass

给 app 绑定 api 视图(view中完成)

api.add_resource(xxxApi,'/user')

格式化输出 ( 都在view中完成 )

python 复制代码
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_restful import fields, marshal_with, Resource, Api

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///my_database.db'
api = Api(app)
db = SQLAlchemy(app)


class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)

    def __repr__(self):
        return f'<User {self.username}>'


# 格式化输出数据,相当于json的格式
user = {
    'id': fields.Integer,
    'username': fields.String,
    'password': fields.String
}


# 定义类视图
class UserResource(Resource):
    # get请求处理
    # 在对应的api接口上添加配置:@marshal_with(user)
    @marshal_with(user)  # user的json格式化输出
    def get(self):
        users = User.query.all()
        return users


api.add_resource(UserResource, '/user')

路由

flask 中路由的写法

python 复制代码
@app.route('/user')
def user():         ----------->视图函数
	.....
	return response对象

增加 修改 删除 查询 按钮动作 都是自己在模板中写的

回顾 flask 路由路径上的参数解析使用:

路由的变量规则(解析路径上的参数)

string (缺省值)接收任何不包含斜杠的文本值

int 接收正整数

float 接收正浮点数

path 类似string,但是可以接收斜杠

UUID  接收UUID字符

route规则中如果传入了变量,那么被绑定的函数中也必须需要传入相应的参数

解析路径上的参数案例:

python 复制代码
@app.route('/<int:key>') # key是一个变量,默认就是字符串类型
def city(key):
    return data.get(key)

restful 中路由写法

restful:------->api-------->接口-------->资源------>uri

概述基本的 ResuFul 的建立:

class xxxApi(Response): -------视图类

def get(self):

pass

....

http://127.0.0.1:5000/user 这个路径可以做的事:

get

post

put

delete

...

增加 修改 删除 查询 是通过请求方式完成的

路径产生:

api.add_resource(xxxApi,'/user')

api.add_resource(xxxApi,'/goods')

api.add_resource(xxxApi,'/order')

api 解析路径参数:http://127.0.0.1:5000/user/1

api 中的路径参数解析(与模板中的路径参数解析类型):

class UserGetIdResource(Resource):

@marshal_with(user)

def get(self, uid):

users = User.query.get(uid)

return users

api.add_resource(UserGetIdResource, '/user/<int:uid>')

endpoint 的使用,便捷的反向解析出api

定义类视图

class UserResource(Resource):

def put(self):

print('endpoint的使用,反向解析出api:', url_for('all_user'))

return {'msg': '-------->ok'}

api.add_resource(UserResource, '/user', endpoint='all_user')

数据传入(进)( request )

参数解析:

1.使用 reqparse 对象进行传入的参数进行解析

parser = reqparse.RequestParser() # 创建解析对象

2.使用

parser.add_argument('username', type=str, required=True, help="必须输入账号", location=['form'])

args = parser.parse_args()

username = args.get('username')

案例:用于api中的数据被接收,我们需要验证或者过滤

参数解析

parser = reqparse.RequestParser(bundle_errors=True) # 解析对象

parser.add_argument('username', type=str, required=True, help="必须输入账号", location=['form'])

parser.add_argument('password', type=inputs.regex(r'^\d{6,12}$'), required=True, help="必须输入密码", location=['form'])

parser.add_argument('phone ', type=inputs.regex(r'^1[356789]\d{9}$'), location=['form'])

parser.add_argument('icon', required=True, type=FileStorage, location=['files'])

From the request headers

parser.add_argument('Host', type=str, required=True, location=['headers'])

#在对应请求中的使用

args = parser.parse_args()

username = args.get('username')

password = args.get('password')

phone = args.get('phone')

icon = args.get('icon')

host = args.get('Host')

数据返回(出)( response )

概述:return data

主要:data 必须是 json 格式,默认的返回类型不是 json 格式,并且强行转换还会报错。解决方法如下。数据需要返回的格式类型

{

'aa':'haha',

'bb':[

{

'id':1,

'xxx':[

{},{},{}

]

}

]

}

如果直接返回不能有自定义的对象:User,Friend......

如果有,需要 marshal(),marshal_with() 帮助 JSON 的序列化进行转换.

    1. marshal(对象,对象的fields格式) #对象的fields格式是指字典的输出格式 marshal([对象,对象],对象的fields格式)
    1. marshal_with() 作为装饰器,修饰你的请求方法

@marshal_with(user_friend_fields)

def get(self,id):

.....

data={

xxx:xxx

xxx:xxx

'friends': friend_list # 直接是list,因为使用了@marshal_with(user_friend_fields)

}

return data

函数需要参数,参数就是最终数据输出的格式

参数:user_friend_fields,类型:dict类型

如:

user_fields = {

'id': fields.Integer,

'username': fields.String(default='匿名'),

'pwd': fields.String(attribute='password'),

'isDelete': fields.Boolean(attribute='isdelete')

}

user_friend_fields={

'username':fields.String,

'nums':fields.Integer,

'friends':fields.List(fields.Nested(user_fields))

}

3.fields.Nested 的作用

高级类型的数据转换

fields.Nested(fields.String) ---> ['aaa','bbb','bbbbv']

fields.Nested(user_fields) ---> user_fields是一个字典结构,将里边的每一个对象转 换成user_fields---->[user,user,user]

示例代码:

python 复制代码
import os
from flask import Flask
from flask import Blueprint, url_for
from flask_restful import marshal_with, marshal
from flask_restful import Resource, fields, reqparse, inputs
from flask_restful import Api
from flask_script import Manager
from werkzeug.datastructures import FileStorage
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate

##############################################################
setting_conf = {}
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://user:passwd@host/my_db'
# 蓝图的别名为user,看到/api 就是我们写的蓝图
user_bp = Blueprint('user', __name__, url_prefix='/api')
##############################################################

flask_app = Flask(__name__, template_folder='../templates', static_folder='../static')
flask_app.config.from_object(setting_conf)
flask_app.config['SQLALCHEMY_DATABASE_URI'] = SQLALCHEMY_DATABASE_URI
flask_app.register_blueprint(user_bp)

# 将 SALAlchemy插件与app关联。等价于 db.init_app(app=flask_app)
db = SQLAlchemy(flask_app)
# 将api插件与app关联。等价于 api.init_app(app=flask_app)
api = Api(app=flask_app)
print(flask_app.url_map)


class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(20))
    password = db.Column(db.String(15))
    icon = db.Column(db.String(15))
    phone = db.Column(db.String(11))


# 格式化输出数据,输出的json格式如下
user = {
    'id': fields.Integer,
    'username': fields.String(20),
    'password': fields.String(15)
}

user_fields = {
    'id': fields.Integer,
    'username': fields.String(default='匿名'),
    'pwd': fields.String(attribute='password'),
    'isDelete': fields.Boolean(attribute='isdelete')
}

user_friend_fields = {
    'username': fields.String,
    'nums': fields.Integer,
    'friends': fields.List(fields.Nested(user_fields))
}

# 参数解析
parser = reqparse.RequestParser(bundle_errors=True)  # 解析对象
parser.add_argument('username', type=str, required=True, help="必须输入账号", location=['form'])
parser.add_argument('password', type=inputs.regex(r'^\d{6,12}$'), required=True, help="必须输入密码", location=['form'])
parser.add_argument('phone ', type=inputs.regex(r'^1[356789]\d{9}$'), location=['form'])
parser.add_argument('icon', required=True, type=FileStorage, location=['files'])
# From the request headers
parser.add_argument('Host', type=str, required=True, location=['headers'])


# 定义类视图
class UserResource(Resource):
    # get请求处理
    @marshal_with(user)  # user的json格式化输出
    def get(self):
        users = User.query.all()
        print(users)
        return users

    @marshal_with(user)
    def post(self):
        args = parser.parse_args()
        username = args.get('username')
        password = args.get('password')
        phone = args.get('phone')
        icon = args.get('icon')
        host = args.get('Host')
        print('host:', host)
        print('icon:', icon)

        # 创建user对象
        user_db_model = User()
        user_db_model.icon = icon
        user_db_model.username = username
        user_db_model.password = password
        user_db_model.phone = phone
        db.session.add(user_db_model)
        db.session.commit()
        return user_db_model

    def put(self):
        print('endpoint的使用,反向解析出api:', url_for('all_user'))
        return {'msg': '-------->ok'}

    def delete(self):
        return {'msg': '-------->delete'}


class UserGetIdResource(Resource):
    @marshal_with(user)
    def get(self, uid):
        users = User.query.get(uid)
        return users

    def put(self, uid):
        pass

    def post(self, uid):
        pass

    def delete(self):
        pass


class UserFriendResoruce(Resource):
    @marshal_with(user_friend_fields)
    def get(self, id):
        friends = Friend.query.filter(Friend.uid == id).all()
        user = User.query.get(id)
        friend_list = []
        for friend in friends:
            u = User.query.get(friend.fid)
            friend_list.append(u)

        # data = {
        #     'username': user.username,
        #     'nums': len(friends),
        #     'friends': marshal(friend_list, user_fields)  # marshal(数据名, 结构名)
        # }
        data = {
            'username': user.username,
            'nums': len(friends),
            'friends': friend_list  # 直接是list,因为使用了@marshal_with(user_friend_fields)
        }

        return data

    def post(self):
        pass


if __name__ == '__main__':
    api.add_resource(UserResource, '/user', endpoint='all_user')
    api.add_resource(UserGetIdResource, '/user/<int:uid>')
    api.add_resource(UserFriendResoruce, '/friend/<int:id>')
    # 搭建数据库
    # migrate = Migrate(app=flask_app, db=db)
    pass
相关推荐
chao_78915 分钟前
二分查找篇——寻找旋转排序数组中的最小值【LeetCode】
python·线性代数·算法·leetcode·矩阵
金玉满堂@bj32 分钟前
PyCharm 中 Python 解释器的添加选项及作用
ide·python·pycharm
程序员三藏36 分钟前
如何使用Pytest进行测试?
自动化测试·软件测试·python·测试工具·职场和发展·测试用例·pytest
随心点儿1 小时前
使用python 将多个docx文件合并为一个word
开发语言·python·多个word合并为一个
不学无术の码农1 小时前
《Effective Python》第十三章 测试与调试——使用 Mock 测试具有复杂依赖的代码
开发语言·python
sleepybear11131 小时前
在Ubuntu上从零开始编译并运行Home Assistant源码并集成HACS与小米开源的Ha Xiaomi Home
python·智能家居·小米·home assistant·米家·ha xiaomi home
纪伊路上盛名在1 小时前
(鱼书)深度学习入门1:python入门
人工智能·python·深度学习
夏末蝉未鸣012 小时前
python transformers笔记(TrainingArguments类)
python·自然语言处理·transformer
德育处主任Pro2 小时前
「py数据分析」04如何将 Python 爬取的数据保存为 CSV 文件
数据库·python·数据分析
咸鱼鲸2 小时前
【PyTorch】PyTorch中数据准备工作(AI生成)
人工智能·pytorch·python