当前位置: 首页 > news >正文

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 像这样:

from flask import Flask
from flask_restful import Resource, Apiapp = 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 资源是这样的:

from flask import Flask, request
from flask_restful import Resource, Apiapp = 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 也支持使用多个返回值来设置响应代码和响应头,如下所示:

from flask import Flask, request
from flask_restful import Resource, Apiapp = 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 OKreturn {'task': 'Hello world'}class Todo2(Resource):def get(self):# Set the response code to 201return {'task': 'Hello world'}, 201class Todo3(Resource):def get(self):# Set the response code to 201 and return custom headersreturn {'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')

示例

from flask import Flask
from flask_restful import Resource, Apiapp = 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 OKreturn {'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 的库。

示例

from flask import Flask
from flask_restful import reqparse, Api, Resourceapp = 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 OKreturn {'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 模块来在你的响应中格式化结构。

from flask import Flask
from flask_restful import fields, marshal_with, Resource, Apiapp = 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_idself.task = task# This field will not be sent in the responseself.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"
}

完整 示例

from flask import Flask
from flask_restful import reqparse, abort, Api, Resourceapp = 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 '', 204def put(self, todo_id):args = parser.parse_args()task = {'task': args['task']}TODOS[todo_id] = taskreturn task, 201# TodoList
# shows a list of all todos, and lets you POST to add new tasks
class TodoList(Resource):def get(self):return TODOSdef post(self):args = parser.parse_args()todo_id = int(max(TODOS.keys()).lstrip('todo')) + 1todo_id = 'todo%i' % todo_idTODOS[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

from flask import *
from flask_restful import  Api,Resource,reqparse
from gevent import monkey
from gevent.pywsgi import WSGIServermonkey.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 过滤 数据

示例:

from flask import Flask
from flask_restful import Api, Resource, fields, marshalapp = 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_dataapi.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

from flask import Flask
from flask_sqlalchemy import SQLAlchemyapp = 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 命令来简化迁移工作,可以使用它来迁移数据库。

from flask import Flask
from flask import render_template
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrateapp = Flask(__name__)
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://user:passwd@host/my_db'
app.config['SQLALCHEMY_DATABASE_URI'] = SQLALCHEMY_DATABASE_URIdb = 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 = usernameself.password = passwordself.email = emaildef __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

from flask_restful import Resourceclass xxxApi(Resource):def get(self):passdef post(self):pass

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

api.add_resource(xxxApi,'/user')

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

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_restful import fields, marshal_with, Resource, Apiapp = 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 usersapi.add_resource(UserResource, '/user')

路由

flask 中路由的写法

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

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

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

路由的变量规则(解析路径上的参数)
string (缺省值)接收任何不包含斜杠的文本值
int 接收正整数
float 接收正浮点数
path 类似string,但是可以接收斜杠
UUID  接收UUID字符
route规则中如果传入了变量,那么被绑定的函数中也必须需要传入相应的参数

解析路径上的参数案例:

@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格式)
  • 2. 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]

示例代码:

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 = iconuser_db_model.username = usernameuser_db_model.password = passworduser_db_model.phone = phonedb.session.add(user_db_model)db.session.commit()return user_db_modeldef 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 usersdef put(self, uid):passdef post(self, uid):passdef delete(self):passclass 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 datadef post(self):passif __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

相关文章:

Python flask-restful 框架讲解

1、简介 Django 和 Flask 一直都是 Python 开发 Web 的首选&#xff0c;而 Flask 的微内核更适用于现在的云原生微服务框架。但是 Flask 只是一个微型的 Web 引擎&#xff0c;所以我们需要扩展 Flask 使其发挥出更强悍的功能。 python flask框架详解&#xff1a;https://blog.…...

MySQL_约束、多表关系

约束 概念&#xff1a;就是用来作用表中字段的规则&#xff0c;用于限制存储在表中的数据。 目的&#xff1a;保证数据库中数据的正确性&#xff0c;有效性和完整性。 约束演示 #定义一个学生表&#xff0c;表中要求如下&#xff1a; #sn 表示学生学号&#xff0c;要求使用 …...

在Qt中使用LoadLibrary无法加载DLL

Qt系列文章目录 文章目录 Qt系列文章目录前言一、问题分析 前言 最近因项目需要使用qt做开发&#xff0c;之前使用LoadLibrary加载dll成功&#xff0c;很庆幸&#xff0c;当一切都那么顺风顺水的时候&#xff0c;测试同事却发现&#xff0c;在windows平台上个别电脑上加载dll会…...

如何将区块链新闻稿发布到海外媒体?

随着区块链技术的不断发展&#xff0c;越来越多的区块链项目涌现出来&#xff0c;各大媒体也开始关注和报道区块链新闻。然而&#xff0c;如何将区块链新闻稿发布到海外媒体成为了许多区块链项目所面临的难题。本文将介绍一些有效的方法&#xff0c;帮助区块链项目将新闻稿发布…...

基于 CentOS 7 构建 LVS-DR 群集。

1.准备实验环境 本次实验我准备了4台虚拟机 DS:DIP--192.168.163.138 VIP--192.168.163.200 RIP1(web1)--192.168.163.140 RIP2(web2)--192.168.163.141 Client&#xff1a;user--192.168.163.142 2.配置服务器环境 1)搭建简易的web服务 RIP1 [rootlocalhost ~]# yum …...

防火墙组建双击热备后,点击管理对端设备,老是打不开,怎么办?

环境&#xff1a; 防火墙 8.0.75 AF-2000-FH2130B-SC 问题描述&#xff1a; 防火墙组建双击热备后&#xff0c;点击管理对端设备&#xff0c;老是打不开&#xff0c;怎么办&#xff1f; 浏览器老是加载 解决方案&#xff1a; 1.打开设置查看双机连接的心跳接口是哪个端口 …...

【Kubernetes】Kubernetes之Pod详解

Pod 一、 Pod1. Pod 基础概念2. 在 Kubrenetes 集群中 Pod 使用方式2.1 pasue 容器2.2 kubernetes 中的 pause 容器提供的功能 3. Pod 的概念和结构组成4. Pod 的分类5. Pod 容器的分类5.1 基础容器&#xff08;infrastructure container&#xff09;5.2 初始化容器&#xff08…...

电商与客服系统完美对接指南源码-无缝对接唯一客服系统-提升电商客户体验...

提升电商客户体验&#xff0c;无缝对接唯一客服系统&#xff0c;助您商城腾飞&#xff01; 在如今竞争激烈的电商领域&#xff0c;除了优质的商品和吸引人的价格&#xff0c;出色的客户服务同样不可或缺。一个高效的客服系统能够为您的电商商城带来更多的机会&#xff0c;建立顾…...

新知识:Monkey 改进版之 App Crawler

原生Monkey 大家知道Monkey是Android平台上进行压力稳定性测试的工具&#xff0c;通过Monkey可以模拟用户触摸屏幕、滑动、按键等伪随机用户事件来对设备上的程序进行压力测试。而原生的Android Monkey存在一些缺陷&#xff1a; 事件太过于随机&#xff0c;测试有效性大打折扣…...

黑马头条项目学习--Day3: 自媒体文章发布

Day3: 自媒体文章发布 Day3: 自媒体文章发布1) 素材管理-图片上传a) 前期微服务搭建b) 具体实现 2) 素材管理-图片列表a) 接口定义b) 具体实现 3) 素材管理-照片删除/收藏a) 图片删除a1) 接口定义a2) 代码实现 b) 收藏与取消b1) 接口定义b2) 代码实现 4) 文章管理-频道列表查询…...

使用frp实现内网穿透

1、介绍 ​ 当我们想把内网的一些资源暴露在公网上时&#xff0c;可以使用内网穿透功能。比如公司的内网服务器&#xff0c;部署了平时需要开发的项目&#xff0c;但是回到家中无法访问&#xff0c;就可以使用内网穿透&#xff0c;将公司内网的接口映射到一台公网的服务器上&a…...

安装 opendr 踩坑记

复现早期的优化算法需要用到opendr&#xff0c;踩坑一天记录&#xff01;&#xff01;&#xff01; 测试是否成功安装的命令 >>> import opendr >>> opendr.demo("texture")失败案例 python 3.8.15 numpy1.23.0 mayavi4.8.1 chumpy0.70 cpython…...

各地区-各行业法人单位、区划数63个指标(2010-2022年)

一、数据介绍 数据名称&#xff1a;各地区-各行业法人单位、区划数63个指标 数据年份&#xff1a;2010-2022年&#xff08;法人单位至2021年&#xff09; 数据样本&#xff1a;404条 数据来源&#xff1a;自我整理 二、指标说明 行政区划代码 地区 长江经济带 经度 纬…...

W5500-EVB-PICO作为TCP Client 进行数据回环测试(五)

前言 上一章我们用W5500-EVB-PICO开发板通过DNS解析www.baidu.com&#xff08;百度域名&#xff09;成功得到其IP地址&#xff0c;那么本章我们将用我们的开发板作为客户端去连接服务器&#xff0c;并做数据回环测试&#xff1a;收到服务器发送的数据&#xff0c;并回传给服务器…...

web前端面试--递归(斐波那契数列)

web面试题 本人是一个web前端开发工程师&#xff0c;主要是vue框架&#xff0c;整理了一些面试题&#xff0c;今后也会一直更新&#xff0c;有好题目的同学欢迎评论区分享 ;-&#xff09; web面试题专栏&#xff1a;点击此处 文章目录 web面试题定义源码测试示例 之前去笔试&…...

Vue3 Props组件简单应用(父组件获取子组件数据)

去官网学习→Props | Vue.js 运行示例&#xff1a; 代码&#xff1a;App.vue <template><img alt"Vue logo" src"./assets/logo.png"><h2>Vue Props数据传递</h2><h4>子组件中的数据&#xff1a;{{ content }}</h4>…...

Mybatis查询

返回实体类&#xff0c;必须指定返回类型&#xff0c; resultType不能省略&#xff0c;并且数据库字段名与实体类不一致会填充NULL&#xff0c;实体类我们一般都是驼峰&#xff0c;数据库字段一般都是下划线&#xff0c;所以在查询的时候可以起别名解决,属性填充本质上调用的是…...

如何让ES低成本、高性能?滴滴落地ZSTD压缩算法的实践分享

前文分别介绍了滴滴自研的ES强一致性多活是如何实现的、以及如何提升ES的性能潜力。由于滴滴ES日志场景每天写入量在5PB-10PB量级&#xff0c;写入压力和业务成本压力大&#xff0c;为了提升ES的写入性能&#xff0c;我们让ES支持ZSTD压缩算法&#xff0c;本篇文章详细展开滴滴…...

[数据集][目标检测]PCB板缺陷目标检测数据集VOC格式693张6类别

数据集格式&#xff1a;Pascal VOC格式(不包含分割路径的txt文件和yolo格式的txt文件&#xff0c;仅仅包含jpg图片和对应的xml) 图片数量(jpg文件个数)&#xff1a;693 标注数量(xml文件个数)&#xff1a;693 标注类别数&#xff1a;6 标注类别名称:["missing_hole",…...

Linux 安装中文输入法

在linux安装ibus输入法 在linux安装ibus输入法 加入开机自启动 运行以下命令以编辑IBus配置文件&#xff1a;如果没有该文件&#xff0c;则可以创建一个新文件。 nano ~/.xprofile将以下内容添加到文件中&#xff1a;这些命令将设置相应的环境变量并启动IBus守护进程。 ex…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

网络六边形受到攻击

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 抽象 现代智能交通系统 &#xff08;ITS&#xff09; 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 &#xff08;…...

Android Wi-Fi 连接失败日志分析

1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分&#xff1a; 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析&#xff1a; CTR…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)

HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

Linux简单的操作

ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...

Python实现prophet 理论及参数优化

文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候&#xff0c;写过一篇简单实现&#xff0c;后期随着对该模型的深入研究&#xff0c;本次记录涉及到prophet 的公式以及参数调优&#xff0c;从公式可以更直观…...

三体问题详解

从物理学角度&#xff0c;三体问题之所以不稳定&#xff0c;是因为三个天体在万有引力作用下相互作用&#xff0c;形成一个非线性耦合系统。我们可以从牛顿经典力学出发&#xff0c;列出具体的运动方程&#xff0c;并说明为何这个系统本质上是混沌的&#xff0c;无法得到一般解…...

云原生安全实战:API网关Kong的鉴权与限流详解

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关&#xff08;API Gateway&#xff09; API网关是微服务架构中的核心组件&#xff0c;负责统一管理所有API的流量入口。它像一座…...

Chromium 136 编译指南 Windows篇:depot_tools 配置与源码获取(二)

引言 工欲善其事&#xff0c;必先利其器。在完成了 Visual Studio 2022 和 Windows SDK 的安装后&#xff0c;我们即将接触到 Chromium 开发生态中最核心的工具——depot_tools。这个由 Google 精心打造的工具集&#xff0c;就像是连接开发者与 Chromium 庞大代码库的智能桥梁…...

python爬虫——气象数据爬取

一、导入库与全局配置 python 运行 import json import datetime import time import requests from sqlalchemy import create_engine import csv import pandas as pd作用&#xff1a; 引入数据解析、网络请求、时间处理、数据库操作等所需库。requests&#xff1a;发送 …...