ビルドREST APIをフラスコ&SQLchemy


記事を読むhere
フラスコは、すぐにPythonでWebアプリケーションを構築することができます素晴らしいフレームワークです.それは速く、小さく、楽しい仕事をする.このチュートリアルでは、いくつかの他のサポートツールを使用して、RESTful APIを構築します.
このチュートリアルの目的は、地面からフラスコサーバーを構築するという概念を理解することです.オブジェクトリレーショナルマッパを介してSQLデータベースとの通信方法を学び、オブジェクト指向のデザインパターンでRESTful APIを設計することです.
このチュートリアルの最後には、作成、読み取り、更新、および削除機能を使用してブログ投稿データを管理できるREST APIを構築できます.

始める
私は私のgithubにこのチュートリアルの完成したプロジェクトを発表しましたhere または、以下のコマンドを実行してマシンにクローンします.
$ git clone https://github.com/rahmanfadhil/flask-rest-api.git
次に、新しいPython仮想環境を作成し、依存関係をPIPでインストールします.
$ python -m venv env
$ source env/bin/activate
(env) $ pip install -r requirements.txt

依存
そのドキュメントによると、フラスコはマイクロフレームワークです.これは、そこに他のウェブフレームワークとは異なり、フラスコは、箱の外の多くの空想的なものと来ていない.
これは、フラスコをより簡単に、より簡単に学ぶことができます.しかし同時に、我々はよく共通の問題を解決するために自分の解決策を見つけることを余儀なくされた.例えば、データベースへの接続、認証システムの実装など.
しかし、フラスココミュニティは、それらをインストールして、設定するだけで、それらの問題を急速に解決するのを許すいくつかの役に立つオープンソース拡張を提供しました.
APIをビルドするために使用するサードパーティ製のパッケージがあります.

  • Flask SQLAlchemy : サポートを追加するフラスコ拡張モジュールSQLAlchemy , SQLデータベースとの対話を容易にするオブジェクトリレーショナルマッパー.

  • Flask RESTful : オブジェクト指向デザインパターンですぐに残りのAPIを構築するための別のフラスコの拡張子.

  • Flask Marshmallow : もう一つのフラスコ拡張Marshmallow , オブジェクトシリアライズ用のPythonライブラリ.

  • 地面からフラスコプロジェクト
    我々は、新しい仮想環境を初期化することによって我々のプロジェクトを始めることができますPython venv モジュールです.
    $ python3 -m venv env
    $ source env/bin/activate
    
    次に、このコマンドを実行してPIP経由で依存関係をインストールすることができます.
    $ pip install Flask \
        Flask-SQLAlchemy \
        Flask-RESTful \
        flask-marshmallow
    
    最後に、main.py (または、名前を付けたいものは何でも).ここでは、我々のブランドの新しいフラスコサーバを設定します.
    from flask import Flask
    
    app = Flask(__name__)
    
    if __name__ == '__main__':
        app.run(debug=True)
    
    テストするには、以下のコマンドでPythonスクリプトを実行できます.
    (env) $ python main.py
    
    オープンhttp://localhost:5000 , 404ページが表示されます.

    データベース設定
    ここでは、私たちのブログ投稿データを格納するSQLデータベースを使用します.
    学習目的のために、私は使用するつもりですSQLite , 非常に簡単に取得し、実行する小さなSQLデータベースの実装.もっと信頼性の高いデータベースを考えたいと思うかもしれませんPostgreSQL or MySQL 生産環境で.
    FlaskプロジェクトでSqlAlchemyを設定するにはflask_sqlalchemy パッケージ(以前にインストールしました)、パッケージをラップしますapp 新しい変数SQLAlchemy オブジェクト.セットアップもしたいSQLALCHEMY_DATABASE_URI 我々のフラスコアプリケーションの設定では、どのデータベースを使用してどのようにアクセスするかを指定します.
    from flask import Flask
    from flask_sqlalchemy import SQLAlchemy # new
    
    app = Flask(__name__)
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db' # new
    db = SQLAlchemy(app) # new
    
    if __name__ == '__main__':
        app.run(debug=True)
    

    データベースモデル
    モデルはあなたのデータベースの表現ではありません.そこではデータを格納したり、フェッチしたり、操作したりできます.それはあなたのアプリケーションとデータベースの中間として考えてください.通常、単一のテーブル/コレクションを表します.
    それはあなたのPythonコードから直接その特定のテーブルにすべてを行うことができます.それで、あなたはもうSQL質問でまわりで混乱する必要はありません.
    ブログ投稿データを表すモデルを作りましょう.
    class Post(db.Model):
        id = db.Column(db.Integer, primary_key=True)
        title = db.Column(db.String(50))
        content = db.Column(db.String(255))
    
        def __repr__(self):
            return '<Post %s>' % self.title
    
    ここでは、我々はid プロパティが自動生成された主キーフィールドです.それから、私たちもtitle and content フィールド、長さが定義された通常の文字列フィールド.
    この部分は任意ですが、すべてが明らかであることを確認するには、我々は__repr__ すべてのポストオブジェクトをコンソールに印刷する方法.
    次に、モデルのスキーマを設定する必要があります.ポストオブジェクトをJSONレスポンスに解析したいので、これが必要です.この場合、我々は利用できるflask_marshmallow パッケージ.
    from flask import Flask
    from flask_sqlalchemy import SQLAlchemy
    from flask_marshmallow import Marshmallow # new
    
    app = Flask(__name__)
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
    db = SQLAlchemy(app)
    ma = Marshmallow(app) # new
    
    # ...
    
    if __name__ == '__main__':
        app.run(debug=True)
    
    新しいマシュマロスキーマを作成しますPost モデル.
    class PostSchema(ma.Schema):
        class Meta:
            fields = ("id", "title", "content")
    
    post_schema = PostSchema()
    posts_schema = PostSchema(many=True)
    
    このスキーマでは、どのフィールドをユーザーに公開するかを選択できます.モデルに何らかの敏感なデータがあるならば、あなたはそれをここで除外したいかもしれません.それから、私たちはまたposts_schema and post_schema . 用途posts_schema 投稿の配列をシリアル化するにはそうでなければ、post_schema .
    大部分の人々が通常忘れている次の重要な部分(私自身を含む)はdb.create_all Python対話型シェル( REPL )内のSQLchemyインスタンスからの関数.RPLでこれを行う理由は、一度スキーマを初期化する必要があるからです.
    また、あなたが正しいPython環境にいることを確認してください.
    (env) $ python
    >>> from main import db
    >>> db.create_all()
    >>> exit()
    

    静かな路線
    最後に、RESTfulハンドラを定義し始めることができます.我々は、オブジェクト指向設計を備えたRESTfulルートを構築するのに役立つツールのセット、フラスコRESTfulパッケージを使用します.
    フラスコRESTful拡張機能をセットアップして、フラスコサーバで稼働している必要があります.
    from flask import Flask
    from flask_sqlalchemy import SQLAlchemy
    from flask_restful import Api, Resource # new
    
    app = Flask(__name__)
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
    db = SQLAlchemy(app)
    api = Api(app) # new
    
    # ...
    
    if __name__ == '__main__':
        app.run(debug=True)
    
    新しいRESTfulリソースを作成します.この方法では、データベースからデータを取得する方法や、認証の方法など、ビジネスロジックを定義します.
    class PostListResource(Resource):
        def get(self):
            posts = Post.query.all()
            return posts_schema.dump(posts)
    
    api.add_resource(PostListResource, '/posts')
    
    ここでは、我々はGET リクエストを作成し、すべての記事を取得するクエリを作成しますPost モデル.それから、我々はposts_schema データベースからデータをシリアル化し、クライアントに対する応答として返します.
    最後に、リソースをapi.add_resource メソッドのルーティンエンドポイントを定義します.
    サーバを起動し、リクエストを送信する/posts エンドポイント、空の配列を取得します.
    $ curl http://localhost:5000/posts
    []
    
    クール、今では、作成操作で動作する時間です.
    # ...
    from flask import Flask, request # change
    
    # ...
    
    class PostListResource(Resource):
        def get(self):
            posts = Post.query.all()
            return posts_schema.dump(posts)
    
        # new
        def post(self):
            new_post = Post(
                title=request.json['title'],
                content=request.json['content']
            )
            db.session.add(new_post)
            db.session.commit()
            return post_schema.dump(new_post)
    
    # ...
    
    私たちは新しいpost リソースのメソッドで、リクエストデータを持つ新しいPOSTオブジェクトをインスタンス化し、レコードをデータベースに保存します.最後に、クライアントへの応答としてポストデータを返します.
    ポストリクエストを送る/todos POSTデータを持つエンドポイント.
    $ curl http://localhost:5000/posts \
        -X POST \
        -H "Content-Type: application/json" \
        -d '{"title":"Post 1", "content":"Lorem ipsum"}'
    {
        "content": "Lorem ipsum",
        "id": 1,
        "title": "Post 1"
    }
    
    個々のポストを取得するための新しいリソースを作成しましょう.
    class PostResource(Resource):
        def get(self, post_id):
            post = Post.query.get_or_404(post_id)
            return post_schema.dump(post)
    
    api.add_resource(PostResource, '/posts/<int:post_id>')
    
    ここでは、我々も受け入れるGET リクエストではなく、すべての投稿を問い合わせる代わりに、指定したIDを持つ単一のポストを取得します.存在しない場合は404エラーを発生します.
    取ろうとする/todos/<int:id> , そして、あなたは我々がちょうど作成したポストを得ます.
    $ curl http://localhost:5000/posts/1
    {
        "title": "Post 1",
        "content": "Lorem ipsum",
        "id": 1
    }
    
    そして、存在しないIDでポストを要求するならば、あなたは404エラーを得ます.
    $ curl http://localhost:5000/posts/12 -I
    HTTP/1.0 404 NOT FOUND
    ...
    
    では、残りのCRUD操作、更新、削除を追加しましょう.
    class PostResource(Resource):
        # ...
    
        def patch(self, post_id):
            post = Post.query.get_or_404(post_id)
    
            if 'title' in request.json:
                post.title = request.json['title']
            if 'content' in request.json:
                post.content = request.json['content']
    
            db.session.commit()
            return post_schema.dump(post)
    
        def delete(self, post_id):
            post = Post.query.get_or_404(post_id)
            post.delete()
            db.session.commit()
            return '', 204
    
    patch メソッドが存在する場合、最初にPOSTオブジェクトを取得し、リクエスト本文で定義されているプロパティを更新しますrequest.json ). だから、両方のプロパティをチェックする必要がありますin エクスプレッション.データベースを使用して変更を保存しますdb.session.commit() クライアントに更新データを送信します.
    delete メソッドもポストオブジェクトを取得します.しかし今回は、オブジェクトを削除しますdelete POSTオブジェクトからのメソッド.変更を保存し、クライアントに何も返しません.
    今、我々はすべての機能をアップロードしている、それを試してみましょう!
    更新:
    $ curl http://localhost:5000/posts/1 \
        -X PATCH \
        -H "Content-Type: application/json" \
        -d '{"title":"Updated Post", "content":"Updated post content"}'
    {
        "content": "Updated post content",
        "id": 1,
        "title": "Updated Post"
    }
    
    投稿を削除
    $ curl http://localhost:5000/posts/1 -X DELETE -I
    HTTP/1.0 204 NO CONTENT
    ...