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.pyndoc.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
ドキュメント:https://flask-restplus.readthedocs.io/en/latest/
フラスコ-RESTX
すでに完璧な Flask-RESTPlus があるのに、なぜ Flask-RESTX が必要なのでしょうか?
実際、私は Flask-RESTPlus を長い間使用してきましたが、残念なことに、作者はそれを紛失してしまいました。そうです、それは物理的な意味で失われています。Flask-RESTPlus プロジェクト チームのメンバーは彼を見つけることができません。このプロジェクトを維持し続けるために、チームは別のブランチを開いて Flask-RESTPlus を継続することしかできません。プロジェクトは次のとおりです。フラスコ-RESTX。Flask-RESTX
完全互換Flask-RESTPlus
、Flask-RESTPlus
プロジェクトで蓄積された問題点は完全に継承BUG
さFlask-RESTX
れ、コミュニティチームが積極的にメンテナンスと要約を行っており、GitHub:https://github.com/python-restx/flask-restx
ドキュメント:https://flask-restx.readthedocs.io/en/latest/
ファストAPI
FastAPI は Flask から独立した新しい Web フレームワークであり、Flask や関連拡張機能の影が多く見られますが、無視できない Web フレームワークの 1 つとなっており、最速の Python フレームワークの 1 つとしても知られています。
GitHub: https://github.com/tiangolo/fastapi
ドキュメント: https://fastapi.tiangolo.com
2. クイックスタート
インストール: pip install flask-restful
簡単な例
最小限の Flask-RESTful API は次のようになります。
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
* http://127.0.0.1:5000/ で実行
次に、新しいコマンド ライン ウィンドウを開き、curl を使用して API をテストします。
$カール http://127.0.0.1:5000/
{"こんにちは": "世界"}
「リソース(ビュー)とルート」バインディング
ビュー内のクラスは flask_restful のリソースを継承する必要があります
Flask-RESTful によって提供される主な構成要素はリソースです。リソースは Flask のプラグイン可能なビューの上に構築されており、リソース上でメソッドを定義するだけで複数の HTTP メソッドに簡単にアクセスできます。Todo アプリケーションの基本的な CRUD リソースは次のようになります。
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)
これを試すことができます:
$カール http://localhost:5000/todo1 -d "data=ミルクを覚えておいてください" -X PUT
{"todo1": "ミルクを覚えておいてください"}
$curl http://localhost:5000/todo1
{"todo1": "ミルクを忘れないでください"}
$curl http://localhost:5000/todo2 -d "data=ブレーキパッドを交換してください" -X PUT
{"todo2": "ブレーキパッドを交換してください"}
$curl http://localhost:5000/ todo2
{"todo2": "ブレーキパッドを交換してください"}
または、リクエスト ライブラリがインストールされている場合は、Python シェルから次のようにします。
>>> リクエストから import put, get
>>> put('http://localhost:5000/todo1', data={'data': 'Remember the Milk'}).json() {
u'todo1': u'ミルクを覚えておいてください'}
>>> get('http://localhost:5000/todo1').json()
{u'todo1': u'ミルクを覚えておいてください'}
>>> put('http:/ /localhost:5000/todo2', data={'data': 'ブレーキパッドを交換してください'}).json() {
u'todo2': u'ブレーキパッドを交換してください'}
>>> get('http://localhost :5000/todo2').json()
{u'todo2': u'ブレーキパッドを交換してください'}
Flask-RESTful は、ビューメソッドからの複数のタイプの戻り値をサポートします。Flask と同様に、任意のイテレータを返すことができ、これは元の Flask 応答オブジェクトを含む応答に変換されます。Flask-RESTful は、次のように、応答コードと応答ヘッダーを設定するための複数の戻り値もサポートしています。
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)
テスト
カール -i http://127.0.0.1:5000/todo1
カール -i http://127.0.0.1:5000/todo2
カール -i http://127.0.0.1:5000/todo3
エンドポイント
多くの場合、API ではリソースに複数の URL が含まれます。複数の URL を API オブジェクトの add_resource() メソッドに渡すことができます。それぞれがリソースにルーティングされます
api.add_resource(HelloWorld,
'/',
'/hello')
リソース メソッドのエンドポイント パラメーターを指定することもできます。
api.add_resource(すべて,
'/all/<int:all_id>', endpoint='all_ep');
例
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)
テスト
カール http://127.0.0.1:5000/
カール http://127.0.0.1:5000/hello
カール http://127.0.0.1:5000/todo/1
カール http://127.0.0.1:5000/todo/ 2
パラメータ分析
Flask ではリクエスト データ (クエリ文字列や POST フォーム エンコード データなど) に簡単にアクセスできますが、フォーム データの検証は依然として面倒な場合があります。Flask-RESTful には、 argparseのようなライブラリを使用してリクエスト データを検証するためのサポートが組み込まれています 。
例
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)
テスト
カール -d 'rate=100' http://127.0.0.1:5000/todos
カール -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)
カール -d 'rate2=foo' http://127.0.0.1:5000/todos
データのフォーマット
デフォルトでは、すべてのフィールドは戻りの反復でそのままレンダリングされます。Python データ構造を扱うだけの場合は素晴らしい仕事のように思えるかもしれませんが、実際に扱うとイライラして退屈になる可能性があります。この問題を解決するために、Flask-RESTful はフィールド モジュールと marshal_with() デコレータを提供します。Django ORM や WTForm と同様に、fields モジュールを使用して応答内の構造をフォーマットできます。
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 で記述された変換に適用されます。オブジェクトから抽出される唯一のフィールドはタスクです。field.Url フィールドは、エンドポイント名を引数として受け取り、応答内にそのエンドポイントの URL を生成する特別なフィールドです。必要なフィールド タイプの多くはすでに含まれています。完全なリストについては、フィールド ガイドを参照してください。
$カール http://127.0.0.1:5000/todo
{ "task": "ミルクを忘れない", "uri": "/todo" }
完全な例
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
* ::1 を試行しています...
* TCP_NODELAY セット
* 接続に失敗しました
* ::1 ポート 5000 に接続できませんでした: 接続が拒否されました
* 127.0.0.1 を試行しています。 ..
* TCP_NODELAY 設定
* localhost (127.0.0.1) ポート 5000 (#0) に接続
> DELETE /todos/todo2 HTTP/1.1
> ホスト: localhost:5000
> ユーザー エージェント:curl/7.64.1
> Accept: */ *
>
* HTTP 1.0、本文の後に閉じると仮定します
< 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
<
* 接続を終了 0
新しいタスクを追加する
$curl http://localhost:5000/todos -d "task=something new" -X POST -v
注: -X または --request を不必要に使用すると、POST はすでに推論されます。
* ::1 を試行しています...
* TCP_NODELAY セット
* 接続に失敗しました
* ::1 ポート 5000 に接続しました 失敗: 接続が拒否されました *
127.0.0.1 を試行しています...
* TCP_NODELAY セット
* localhost (127.0.0.1) のポート 5000 に接続しました (# 0)
> POST /todos HTTP/1.1
> ホスト: localhost:5000
> ユーザー エージェント:curl/7.64.1
> 受け入れ: */*
> コンテンツの長さ: 18
> コンテンツ タイプ: application/x-www-form- urlencoded
>
* アップロードは完全に送信されました: 18 バイト中 18 バイト
* HTTP 1.0、本文
< HTTP/1 の後に閉じると想定します。
< 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": "何か新しいこと" } * 接続を終了します 0
タスクを更新する
$curl http://localhost:5000/todos/todo3 -d "task=something Different" -X PUT -v
* ::1 を試行しています...
* TCP_NODELAY 設定
* 接続に失敗しました
* ::1 ポート 5000 への接続に失敗しました:接続が拒否されました
* 127.0.0.1 を試行しています...
* TCP_NODELAY が設定されています
* localhost (127.0.0.1) のポート 5000 (#0) に接続しました
> PUT /todos/todo3 HTTP/1.1
> ホスト: localhost:5000
> ユーザー エージェント:curl/ 7.64.1
> Accept: */*
> Content-Length: 24
> Content-Type: application/x-www-form-urlencoded
>
* アップロードが完全に送信されました: 24 バイト中 24
* HTTP 1.0、本文の後に閉じると想定します
< HTTP/1.0 201 が作成されました
< Content-Type: application/json
< Content-Length: 38
< サーバー: Werkzeug/1.0.1 Python/3.9.2
< 日付: Sat, 06 Mar 2021 03:32:44 GMT
<
{ "task": "something Different" } * 接続 0 を終了します
最新のリストを取得する
$curl http://localhost:5000/todos
{ "todo1": { "タスク": "API を構築する" }, "todo3": { "タスク": "何か別のもの" }, "todo4": { "タスク": "何か新しいもの" } }
Gunicorn の使用、非同期
インストール: pip install gunicorn
Gunicorn がフラスコ プロジェクトをデプロイする簡単な例: 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 フィルター データ
例:
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/ja/3.0.x/
Flask-SQLAlchemy は、Flask で使用する SQLAlchemy 拡張機能であり、Flask アプリケーションでのデータベース操作に SQLAlchemy を使用するための便利な方法とツールを提供します。
flask_sqlalchemy 拡張機能をインストールします: 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_移行
開発中、テーブルを削除して再構築することでデータベースを更新するのは簡単ですが、明らかな欠点は、データベース内のすべてのデータが失われることです。運用環境では、すべてのデータを削除したいと思う人はいないため、現時点では、データベース移行ツールを使用してこの作業を完了する必要があります。SQLAlchemy の開発者 Michael Bayer は、データベースの移行を実現するために、データベース移行ジョブ (Alembic) を作成しました。データベース移行ツールは、データを破壊することなくデータベース テーブルの構造を更新できます。Alembic は錬金術師にとって最も重要なツールです。SQL Alchemy を学ぶには、もちろん alembic の使い方をマスターする必要があります。
Extended Flask-Migrate は Alembic を継承し、データベースの移行に使用できる移行作業を簡素化するためのいくつかの flask コマンドを提供します。
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 が適用される
フロントエンド: アプリ、アプレット、PC ページ
バックエンド: ページなし、mvt: モデル テンプレート ビューはテンプレートを削除します
mv: モデル ビュー
モデルの使用法: 元の使用法と同じ
ビュー: API ビルド ビュー
exts 拡張パッケージに API オブジェクトを作成する
# アプリ作成関数で API をバインドします。これは api.init_app(app=app) と同等です。
api = API(app=app)
# アプリ作成関数のバインディング db は db.init_app(app=app)
db = SQLAlchemy(api=blueprint object) と同等です
ビューを定義する
ビューのクラスにはベース flask_restful のリソースが必要です
from flask_restful import Resource
class xxxApi(Resource):
def get(self):
pass
def post(self):
pass
API ビューをアプリにバインドします (ビュー内で完了)
api.add_resource(xxxApi,'/user')
フォーマットされた出力 (すべてビュー内で実行されます)
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にルートを記述する方法
@app.route('/user')
def user(): ----------->视图函数
.....
return response对象
クエリ ボタンの追加、変更、削除のアクションはすべて自分でテンプレートに記述されています。
フラスコのルーティング パスでのパラメーター解析を確認してください。
ルーティング (パス上のパラメータの解析) の変数ルール
string (デフォルト) スラッシュを含まない任意のテキスト値を受け入れます
int は正の整数を受け入れますfloat はstring と同様の
正の浮動小数点数パスを受け入れますが、スラッシュを受け入れることができますUUID は UUID 文字を受け入れますルート ルール 変数が渡される場合、バインドされた関数も対応するパラメータを渡す必要があります。
パス上のパラメータの大文字と小文字を解析します。
@app.route('/<int:key>') # key是一个变量,默认就是字符串类型
def city(key):
return data.get(key)
安静時のルーティング
休息: ------->api------>インターフェース------>リソース------>uri
基本的な ResuFul セットアップの概要を説明します。
class xxxApi(Response): ------- view class
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>')エンドポイントの使用、API への便利な逆解析
# クラスビューを定義します
class UserResource(Resource):
def put(self):
print('エンドポイントの使用法, 逆解析 API:', url_for('all_user'))
return {'msg': '----- --- >わかりました'}api.add_resource(ユーザーリソース, '/user', endpoint='all_user')
データ受信 (in) (リクエスト)
パラメータ分析:
1. reqparse オブジェクトを使用して受信パラメータを解析します
parser = reqparse.RequestParser() # 解析オブジェクトを作成する
2.
parser.add_argument('username', type=str, required=True, help="アカウント番号を入力する必要があります", location=['form']) args =
parser.parse_args()
username = args.getを使用します。 ('ユーザー名')
ケース: API で使用されるデータを受信したため、検証またはフィルタリングする必要があります
# パラメータ解析
parser = reqparse.RequestParser(bundle_errors=True) # 解析对象
parser.add_argument('username', type=str, required=True, help="必须输入账号", location=['form']) パーサー
。 add_argument('パスワード', 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'])
# リクエストヘッダーから
parser.add_argument('Host', type=str, required=True, location=['headers'])#対応するリクエストで使用します
args = parser.parse_args()
ユーザー名 = args.get('ユーザー名')
パスワード = args.get('パスワード')
電話 = args.get('電話')
アイコン = args.get( ' icon')
ホスト = args.get('ホスト')
データリターン(出力)(レスポンス)
概要: データを返す
メイン: データは json 形式である必要があり、デフォルトの戻り値の型は json 形式ではなく、強制的に変換するとエラーが報告されます。解決策は次のとおりです。データを返す必要がある形式のタイプ
{ 'aa':'はは', 'bb':[ { 'id':1, 'xxx':[ {},{},{} ] } ] }
直接戻る場合は、カスタム オブジェクト (User、Friend...) を持つことはできません。
ある場合は、marshal() が必要であり、marshal_with() は変換のための JSON シリアル化に役立ちます。
- 1. marshal (オブジェクト、オブジェクトのフィールド形式) #オブジェクトのフィールド形式は、辞書マーシャルの出力形式を指します ([オブジェクト, オブジェクト]、オブジェクトのフィールド形式)
- 2. リクエストメソッドを変更するデコレータとしての marshal_with()
@marshal_with(user_friend_fields)
def get(self,id):
.....
data={ xxx:xxx xxx:xxx 'friends': friends_list # @marshal_with(user_friend_fields) が使用されているため、直接リストです } 戻りデータ
この関数にはパラメータが必要で、パラメータは最終的なデータ出力の形式です。
パラメータ: 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. フィールドの役割。入れ子
高度なタイプのデータ変換
field.Nested(fields.String) ---> ['aaa','bbb','bbbbv']
items.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 = 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