Explanation of Python flask-restful framework

1 Introduction

Django and Flask have always been the first choice for developing the Web with Python, and Flask's microkernel is more suitable for the current cloud-native microservice framework. But Flask is just a tiny web engine, so we need to extend Flask to make it more powerful.

Detailed explanation of python flask framework: https://blog.csdn.net/shifengboy/article/details/114274271

Flask-RESTful

Flask-RESTful is the leader among Flask extensions. It adds support for rapid construction of RESTful APIs and encapsulates Flask to make it easier, faster, and more convenient to develop RESTful APIs.

GitHub: https://github.com/flask-restful/flask-restful
English documentation: https://flask-restful.readthedocs.io/en/latest/
Chinese documentation: http://www.pyndoc.com/Flask -RESTful/

Flask-RESTPlus

We know that Flask-RESTful is an extension of Flask, and Flask-RESTPlus is an extension of Flask-RESTful, which is fully compatible with Flask-RESTful and has enhanced interface document support.

Flask-RESTPlus provides a coherent set of decorators and tools to describe the parameters and objects required by the document API, and use Swagger to parse them into correct interface documents.

GitHub:https://github.com/noirbizarre/flask-restplus
Docs:https://flask-restplus.readthedocs.io/en/latest/

Flask-RESTX

Since there is already a perfect Flask-RESTPlus, why do you need Flask-RESTX?
In fact, I have been using Flask-RESTPlus for a long time, but the sad thing is that the author lost it! That's right, it is lost in the physical sense. Members of the Flask-RESTPlus project team can't find him. In order to continue to maintain this project, the team can only open another branch to continue Flask-RESTPlus. The project is Flask-RESTX. Flask-RESTX Fully compatible  Flask-RESTPlus, Flask-RESTPlus the problems accumulated in the project BUG are  Flask-RESTX fully inherited and the community team is actively maintaining and summarizing,

GitHub:https://github.com/python-restx/flask-restx
Docs:https://flask-restx.readthedocs.io/en/latest/

FastAPI

FastAPI is a new web framework independent of Flask. Although you can see many shadows of Flask and related extensions, it has also become one of the web frameworks that cannot be ignored, and FastAPI is also known as one of the fastest Python frameworks.
GitHub: https://github.com/tiangolo/fastapi
Docs: https://fastapi.tiangolo.com

2. Quick start

Installation: pip install flask-restful

simple example

A minimal Flask-RESTful API looks like this:

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)

Save the above code as api.py and run it in your Python interpreter. Note that we've enabled  Flask's debug  mode, which provides code reloading and better error messages. Debug mode should never be used in a production environment.

$ python api.py
 * Running on http://127.0.0.1:5000/

Now open a new command line window and use curl to test your API:

$ curl http://127.0.0.1:5000/
{"hello": "world"}

"resource (view) and route" binding

The class in the view needs to inherit the Resource in flask_restful

The main building blocks provided by Flask-RESTful are resources. Resources are built on top of Flask's pluggable views, allowing easy access to multiple HTTP methods by simply defining methods on the resource . A basic CRUD resource for a todo application looks like this:

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)

You can try this:

$ 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"}

Or from the python shell if you have the requests library installed:

>>> 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 supports multiple types of return values ​​from view methods. As with Flask, you can return any iterator, which will be converted to a response containing the original Flask response object. Flask-RESTful also supports multiple return values ​​to set response codes and response headers, as follows:

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)

test

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

Many times, in an API, your resources will have multiple urls. Multiple urls can be passed to the add_resource() method on the Api object. Each will be routed to Resource

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

You can also specify endpoint parameters for your resource methods.

api.add_resource(All,
    '/all/<int:all_id>', endpoint='all_ep');

example

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)

test

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

Parameter analysis

Even though Flask provides easy access to request data (such as query strings or POST form-encoded data), validating form data can still be a pain. Flask-RESTful has built-in support for validating request data using a library like  argparse  .

example

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)

test

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

Unlike the argparse module, reqparse.RequestParser.parse_args() returns a Python dictionary rather than a custom data structure.

The input module provides many commonly used conversion functions, such as inputs.date() and inputs.url().
Calling parse_args with strict=True ensures that an error is thrown if the request contains arguments that your parser does not define.

args = parser.parse_args(strict=True)

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

data formatting

By default, all fields will be rendered as-is in your return iteration. Although it may seem like a great job when you're just dealing with Python data structures, it can be frustrating and boring when you actually deal with them. To solve this problem, Flask-RESTful provides fields module and marshal_with() decorator. Like Django ORM and WTForm, you can use the fields module to format structures in your responses.

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)

The above example takes a python object and prepares it for serialization. The marshal_with() decorator will be applied to transformations described by resource_fields. The only field extracted from the object is task. The fields.Url field is a special field that accepts an endpoint name as an argument and generates a URL for that endpoint in the response. Many of the field types you'll need are already included. See the fields guide for a complete list.

$ curl  http://127.0.0.1:5000/todo
{
    "task": "Remember the milk",
    "uri": "/todo"
}

complete example

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)

test

curl http://localhost:5000/todos get list
curl http://localhost:5000/todos/todo3 get a single task

delete a task

$ 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

add a new task

$ 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

update a task

$  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

get the latest list

$ curl http://localhost:5000/todos
{
    "todo1": {
        "task": "build an API"
    },
    "todo3": {
        "task": "something different"
    },
    "todo4": {
        "task": "something new"
    }
}

Using gunicorn, async

Installation: pip install gunicorn

A simple example of gunicorn deploying a flask project: 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 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 filter data

Example:

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': '[email protected]',
}

# 定义字段格式和过滤器
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 is a SQLAlchemy extension for use with Flask, which provides convenient methods and tools for using SQLAlchemy for database operations in Flask applications.

Install the flask_sqlalchemy extension: pip install flask_sqlalchemy

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='[email protected]')
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 = '[email protected]'
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

During development, it is straightforward to update the database by dropping tables and rebuilding them, but the obvious drawback is that all data in the database will be lost. In a production environment, no one wants to delete all the data. At this time, you need to use a database migration tool to complete this work. The developer of SQLAlchemy, Michael Bayer, wrote a database migration job—Alembic to help us realize the migration of the database. The database migration tool can update the structure of the database table without destroying the data. Alembic is the most important tool for alchemists. To learn SQL Alchemy, of course, you must master the use of alembic.

Extended Flask-Migrate inherits Alembic and provides some flask commands to simplify the migration work, which can be used to migrate the database.

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. Summary: overall process

Restful is applied to front-end and back-end separation

Front-end: app, applet, pc page
Back-end: no page, mvt: model template view removes t template
mv: model view
Model usage: same as original usage
View: api build view

Create an api object in the exts extension package

# Bind the API in the function of creating app, which is equivalent to api.init_app(app=app)

api = Api(app=app)

# Binding db in the function of creating app is equivalent to db.init_app(app=app)
db = SQLAlchemy(api=blueprint object)  

define view

The class in the view needs the Resource in the base flask_restful

from flask_restful import Resource


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

    def post(self):
        pass

Bind the api view to the app (completed in the view)

api.add_resource(xxxApi,'/user')

Formatted output (all done in view)

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

routing

How to write routes in flask

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

The actions of adding, modifying, and deleting query buttons are all written in the template by yourself.

Review the parameter parsing on the flask routing path:

Variable rules for routing (parsing parameters on the path)
string (default) accepts any text value that does not contain a slash
int accepts a positive integer
float accepts a positive floating point number
path similar to string, but can accept slashes
UUID accepts UUID characters
route rule If a variable is passed in, the bound function must also pass in the corresponding parameter

Parsing the parameter case on the path:

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

Routing in restful

restful: ------->api------->interface------->resource------>uri

To outline the basic ResuFul setup:

class xxxApi(Response): ------- view class
    def get(self):
        pass
     ....

http://127.0.0.1:5000/user What this path can do:
        get
        post
        put
        delete
        ...
        
add modify delete query is done by request

Default:
api.add_resource(xxxApi,'/user')
api.add_resource(xxxApi,'/goods')
api.add_resource(xxxApi,'/order');

API parsing path parameters: http://127.0.0.1:5000/user/1
Path parameter parsing in api (with path parameter parsing type in the template):
class UserGetIdResource(Resource):
    @marshal_with(user)
    def get(self , uid):
        users = User.query.get(uid)
        return users
api.add_resource(UserGetIdResource, '/user/<int:uid>')

The use of endpoint, convenient reverse parsing to api

# Define class view
class UserResource(Resource):
    def put(self):
        print('endpoint usage, reverse parsing api:', url_for('all_user'))
        return {'msg': '----- --->ok'}

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

Data incoming (in) ( request )

Parameter analysis:

1. Use the reqparse object to parse the incoming parameters

parser = reqparse.RequestParser() # create parsing object

2. Use
parser.add_argument('username', type=str, required=True, help="You must enter an account number", location=['form'])

args = parser.parse_args()
username = args.get('username ')

Case: the data used in the api is received, we need to verify or filter

# 参数解析
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'])

#Use in the corresponding request
args = parser.parse_args()
username = args.get('username')
password = args.get('password')
phone = args.get('phone')
icon = args.get( 'icon')
host = args. get('Host')

data return (out) ( response )

Overview: return data
Main: data must be in json format, the default return type is not in json format, and an error will be reported if forced conversion. The solution is as follows. The format type in which the data needs to be returned

    {
        'aa':'haha',
        'bb':[
            {
            'id':1,
            'xxx':[
                    {},{},{}
                ]
            }
        ]
    }

If you return directly, you cannot have custom objects: User, Friend...
If there is, marshal() is required, and marshal_with() helps JSON serialization for conversion.

  • 1. marshal (object, object's fields format) #The object's fields format refers to the output format of the dictionary marshal ([object, object], the object's fields format)
  • 2. marshal_with() as a decorator to modify your request method

 @marshal_with(user_friend_fields)
 def get(self,id):
    .....
    data={         xxx:xxx         xxx:xxx         'friends': friend_list # It is directly a list, because @marshal_with(user_friend_fields) is used     }     return data




The function requires parameters, and the parameters are the format of the final data output.
Parameter: user_friend_fields, type: dict type
such as:

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. The role of fields.Nested

Advanced type of data conversion
fields.Nested(fields.String) ---> ['aaa','bbb','bbbbv']
fields.Nested(user_fields) ---> user_fields is a dictionary structure, each inside Convert an object to user_fields---->[user,user,user]

Sample code:

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

Guess you like

Origin blog.csdn.net/freeking101/article/details/132167978