Flask-RESTPlusでREST APIとSwaggerドキュメントを統合

 

はじめに

この記事は Muroran Institute of Technology Advent Calendar 2017 25日目の記事です。

Flaskを使ってREST APIを作りたいときに、同時にドキュメントとしてSwaggerも整備したいこと、あると思います。しかしドキュメントも書きつつAPIも書くとどっちかを更新したときにどっちかを更新し忘れるなどして、だんだん面倒になってきてしまいがちです。

そこで今回は簡単なToDo管理APIを例に、Flask-RESTPlusを使ってAPI定義とSwaggerによるドキュメント生成を一気に行ってしまう方法をご紹介します。

Python環境の準備

とりあえずPython環境を準備しておきましょう。PyenvとVirtualenvを使ってさくさくっと作っていきます。仮想環境名はrestplusにしてありますが別に何でもかまいません。今回は最新版であるPython3.6.4を用います。

$ mkdir restplus
$ cd restplus
$ pyenv virtualenv 3.6.4 restplus
$ pyenv local restplus

ファイルの準備

ではファイルやディレクトリを準備していきます。以下のような構造で準備してください。それぞれのファイルが何に使われるかは後述します。

restplus/
├── rest_api/
│   ├── apis/
│   │   └── __init__.py
│   ├── models/
│   │   └── __init__.py
│   ├── __init__.py
│   ├── app.py
│   └── settings.py
├── Dockerfile
├── docker-compose.yml
└── requirements.txt

Pythonライブラリの導入

requirements.txtの編集

requirements.txtには今回利用するPythonライブラリを列挙していきます。以下のように書いておきましょう。Flaskを使うのでORMにはSQL Alchemyを採用しました。また、データベースはMySQLを利用することにします。それに伴ってflask-sqlalchemypymysqlをインストールしておきます。

requirements.txt
flask
flask-restplus
flask-sqlalchemy
pymysql

ライブラリのインストール

編集できたら以下のコマンドでインストールしてしまいます。ぶっちゃけるとDockerで動かすので入れなくても良いといえばいいのですが、IDEなどで補完を効かせるために入れておきましょう。

$ pip install -r requirements.txt

コア部分の作成

ここまでできたらいよいよコーディングに入ります。Flaskを使ったWebアプリをいつも通りな感じで構築していきます。

APIエンドポイントの準備

apis/__init__.pyにAPIエンドポイントを登録する準備をします。Flask RESTPlusで用意されたAPIオブジェクトを初期化しておきましょう。

apis/__init__.py
from flask_restplus import Api

# API情報を指定して初期化
api = Api(
    title='Test API',
    version='1.0',
    description='Swaggerを統合したREST APIのサンプル'
)

ORMの準備

models/__init__.pyではSQL Alchemyの初期化を行います。このファイルにはDBのスキーマを定義していきます。

models/__init__.py
from flask_sqlalchemy import SQLAlchemy

# SQLAlchemyの初期化
db = SQLAlchemy()

アプリの諸設定

settings.pyにはアプリの設定を記述していきます。特にSQL接続情報などの記載は必須です。

settings.py
from os import environ

# デバッグモードを有効化
DEBUG = True

# Swaggerのデフォルト表示形式をListにする
SWAGGER_UI_DOC_EXPANSION = 'list'
# Validationの有効化
RESTPLUS_VALIDATE = True

# SQL接続情報
# コンテナ側に環境変数として渡すためこの形式で受け取る
SQLALCHEMY_DATABASE_URI = environ['MYSQL_URL']
SQLALCHEMY_TRACK_MODIFICATIONS = True

Flaskアプリの定義

ここまで準備できたらFlaskアプリを定義していきます。app変数にFlaskオブジェクトを入れて、それを引数で取り回しながらAPIエンドポイントやSQL Alchemyとの連携を行います。最後にrunメソッドを実行すればOKです。このとき、Dockerの中で実行されるためhostの指定を忘れないようにしましょう。

app.py
from flask import Flask

from rest_api import settings
from rest_api.apis import api
from rest_api.models import db

# Flask本体
app = Flask(__name__)


def configure_app(flask_app: Flask) -> None:
    # DB接続先情報やSwaggerの表示形式を指定
    flask_app.config['SQLALCHEMY_DATABASE_URI'] = settings.SQLALCHEMY_DATABASE_URI
    flask_app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = settings.SQLALCHEMY_TRACK_MODIFICATIONS
    flask_app.config['SWAGGER_UI_DOC_EXPANSION'] = settings.SWAGGER_UI_DOC_EXPANSION
    flask_app.config['RESTPLUS_VALIDATE'] = settings.RESTPLUS_VALIDATE


def initialize_app(flask_app: Flask) -> None:
    # FlaskへAPIやDB情報を登録
    configure_app(flask_app)
    api.init_app(flask_app)
    db.init_app(flask_app)
    db.create_all(app=flask_app)


def main() -> None:
    # Flaskを初期化して実行
    initialize_app(app)
    app.run(host='0.0.0.0', debug=settings.DEBUG)


if __name__ == '__main__':
    main()

Dockerの準備

Dockerfile

アプリをDockerに載せるためにDockerfileを書きます。MySQLの起動を待つためにDockerizeも一緒に仕込んでおきます。

FROM python:alpine3.6

WORKDIR /usr/src/app

ENV DOCKERIZE_VERSION v0.6.0

COPY requirements.txt ./
RUN pip install -U --no-cache-dir -r requirements.txt \
 && apk add --no-cache openssl \
 && wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
 && tar -C /usr/local/bin -xzvf dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
 && rm dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz

COPY . .
CMD [ "python", "rest_api/app.py" ]

docker-compose.yml

DockerfileだけではMySQLとの統合が難しいのでdocker-compose.ymlも書いておきましょう。アプリ内部でのimportを使うため、該当ディレクトリをPYTHONPATH環境変数に渡してあげるのを忘れないようにしてください。また、MySQLの接続情報も渡してあげましょう。

version: '3'

services:
  restapi:
    build: .
    ports:
      - "5000:5000"
    environment:
      MYSQL_URL: 'mysql+pymysql://test:user_pass@mysql/test'
      PYTHONPATH: '/usr/src/app'
    links:
      - mysql
    entrypoint:
      - dockerize
      - -timeout
      - 60s
      - -wait
      - tcp://mysql:3306
    command: python /usr/src/app/rest_api/app.py

  mysql:
    image: mariadb:latest
    environment:
      MYSQL_ROOT_PASSWORD: 'root_pass'
      MYSQL_DATABASE: 'test'
      MYSQL_USER: 'test'
      MYSQL_PASSWORD: 'user_pass'
      command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci

起動テスト

ここまでできたら起動のテストを行います。docker-composeでさくさくっとやってしまいましょう。

$ docker-compose build
$ docker-compose up -d

起動し終わったら、http://localhost:5000/ にアクセスすると以下のような画面が表示されます。ここにだんだんとAPIの仕様書ができあがっていきます。

Flask_01.PNG

DBスキーマの定義

続いてデータベースのスキーマを定義していきます。models/__init__.pyにつらつらと書いていきます。ToDoに使うスキーマを簡単に定義します。models/__init__.pyに以下の記述を追記します。db変数に格納したSQLAlchemyオブジェクトを流用して、カラムやその型を定義します。このとき、primary_keyに設定したIntegerのカラムはオートインクリメントが適用されます。

models/__init__.py
from flask_sqlalchemy import SQLAlchemy

# SQLAlchemyの初期化
db = SQLAlchemy()


# Userスキーマの定義
class User(db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, nullable=False, primary_key=True)
    name = db.Column(db.Text, nullable=False)
    email = db.Column(db.Text, nullable=False)


# ToDoスキーマの定義
class ToDo(db.Model):
    __tablename__ = 'todo'
    id = db.Column(db.Integer, nullable=False, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
    title = db.Column(db.Text, nullable=False)
    description = db.Column(db.Text, nullable=False)

APIエンドポイントの定義

定義ファイルの作成

データベースの準備ができたらAPIエンドポイントを設計しましょう。apis/ディレクトリにtodo.pyuser.pyを新規作成し、そこに定義を書いていきます。

$ touch apis/todo.py apis/user.py

NamespaceとJSONモデルの定義

異なるAPIエンドポイントを区別するためにNamespaceを作成します。さらに、返したいJSONのモデルも定義しましょう。使ってもいないのにResourceをimportしていますが、この後すぐ使うのでここでimportしてしまいます。

Namespaceオブジェクトを生成する際の第1引数にはエンドポイントのプレフィックスを指定します。例えばtodo.pyではtodoと指定していますが、/todo以下でこれから定義するエンドポイントが呼べるようになります。作成したNamespaceは忘れずにAPIに登録してください。

JSONモデルの定義はDBのカラム名と同様の文字列をキーにした辞書で行います。各要素について必須かどうかや詳しい説明、例となる値を指定することができます。これらの情報は全てSwaggerドキュメントに反映されます。

apis/todo.py
from flask_restplus import Namespace, fields, Resource

# Namespaceの初期化
todo_namespace = Namespace('todo', description='ToDoのエンドポイント')

# JSONモデルの定義
todo = todo_namespace.model('ToDo', {
    'user_id': fields.Integer(
        required=True,
        description='ToDoを登録したユーザーID',
        example='0'
    ),
    'id': fields.Integer(
        required=True,
        description='ToDoのID',
        example='1'
    ),
    'title': fields.String(
        required=True,
        description='ToDoタイトル',
        example='起きる'
    ),
    'description': fields.String(
        required=True,
        description='ToDoの詳細',
        example='朝7時に起きたい'
    )
})
apis/user.py
from flask_restplus import Namespace, fields, Resource

# Namespaceの初期化
user_namespace = Namespace('users', description='ユーザー関連のエンドポイント')

# JSONモデルの定義
user = user_namespace.model('User', {
    'id': fields.Integer(
        required=True,
        description='',
        example='0'
    ),
    'name': fields.String(
        required=True,
        description='',
        example='Aruneko'
    ),
    'email': fields.String(
        required=True,
        description='',
        example='aruneko@example.com'
    )
})

エンドポイントの追加

NemespaceとJSONモデルが定義できたらいよいよエンドポイントを追加します。やり方は簡単で、Resourceクラスを継承したクラスを作ってHTTPメソッドと同名のメソッドを作成し、ルーティングに必要なアノテーションを付加するだけです。これらを踏まえて、apis以下のファイルの最終形は以下のようになります。

各メソッドの先頭にダブルクォートを6つ使った複数行コメントを差し込むことで、API説明をSwaggerに反映させることができます。1行目が簡易的な説明で、2行目以降はMarkdown記法を使った詳細な説明を記入できます。

marshal_withmarshal_list_withでは返ってくるJSONの形式を指定できます。前者は単体を、後者はリスト化したものを返します。先ほど定義したJSONモデルを引数にとってあげましょう。expectはその逆で、送って欲しいJSONのモデルを指定します。

apis/todo.py
from flask_restplus import Namespace, fields, Resource

from rest_api.apis import api
from rest_api.models import ToDo, db

# Namespaceの初期化と登録
todo_namespace = Namespace('todo', description='ToDoのエンドポイント')

# JSONモデルの定義
todo = todo_namespace.model('ToDo', {
    'user_id': fields.Integer(
        required=True,
        description='ToDoを登録したユーザーID',
        example='0'
    ),
    'id': fields.Integer(
        required=True,
        description='ToDoのID',
        example='1'
    ),
    'title': fields.String(
        required=True,
        description='ToDoタイトル',
        example='起きる'
    ),
    'description': fields.String(
        required=True,
        description='ToDoの詳細',
        example='朝7時に起きたい'
    )
})


@todo_namespace.route('/')
class ToDoList(Resource):
    # todoモデルを利用して結果をパースしてリストで返す
    @todo_namespace.marshal_list_with(todo)
    def get(self):
        """
        一覧取得
        """
        return ToDo.query.all()

    @todo_namespace.marshal_with(todo)
    @todo_namespace.expect(todo, validate=True)
    def post(self):
        """
        ToDo登録
        """
        # ちょっとやっかいなので実装はまた今度
        pass


@todo_namespace.route('/<int:todo_id>')
class ToDoController(Resource):
    # todoモデルを利用して結果をパースして単体で返す
    @todo_namespace.marshal_with(todo)
    def get(self, todo_id):
        """
        ToDo詳細
        """
        # ただし1個も見つからなかったら404を返す
        return ToDo.query.filter(ToDo.id == todo_id).first_or_404()

    def delete(self, todo_id):
        """
        ToDo削除
        """
        # 見つからなかったときの処理してないけど許して
        target_todo = ToDo.query.filter(ToDo.id == todo_id).first()
        db.session.delete(target_todo)
        return {'message': 'Success'}, 200
apis/user.py
from flask_restplus import Namespace, fields, Resource

from rest_api.apis import api

# Namespaceの初期化と登録
from rest_api.apis.todo import todo
from rest_api.models import User, db, ToDo

user_namespace = Namespace('users', description='ユーザー関連のエンドポイント')

# JSONモデルの定義
user = user_namespace.model('User', {
    'id': fields.Integer(
        required=True,
        description='',
        example='0'
    ),
    'name': fields.String(
        required=True,
        description='',
        example='Aruneko'
    ),
    'email': fields.String(
        required=True,
        description='',
        example='aruneko@example.com'
    )
})


@user_namespace.route('/')
class UserList(Resource):
    @user_namespace.marshal_list_with(user)
    def get(self):
        """
        ユーザー一覧
        """
        return User.query.all()

    @user_namespace.marshal_with(user)
    @user_namespace.expect(user)
    def post(self):
        """
        ユーザー登録
        """
        # ちょっとやっかいなので実装はまた今度
        pass


@user_namespace.route('/<int:user_id>')
class UserController(Resource):
    @user_namespace.marshal_with(user)
    def get(self, user_id):
        """
        ユーザー詳細
        """
        return User.query.filter(User.id == user_id).first_or_404()

    def delete(self, user_id):
        """
        ユーザー削除
        """
        target_user = User.query.filter(User.id == user_id).first()
        db.session.delete(target_user)
        return {'message': 'Success'}, 200


@user_namespace.route('/<int:user_id>/todo')
class ToDoByUser(Resource):
    @user_namespace.marshal_list_with(todo)
    def get(self, user_id):
        """
        ユーザーごとのToDo取得
        """
        return ToDo.query.filter(ToDo.user_id == user_id).all()

エンドポイントの登録

エンドポイントを作成し終わったら登録を行います。apis/__init__.pyに記入しましょう。add_namespaceメソッドを利用してください。

apis/__init__.py
from flask_restplus import Api

from rest_api.apis.todo import todo_namespace
from rest_api.apis.user import user_namespace

# API情報を指定して初期化
api = Api(
    title='Test API',
    version='1.0',
    description='Swaggerを統合したREST APIのサンプル'
)

api.add_namespace(todo_namespace)
api.add_namespace(user_namespace)

テスト起動

再びdocker-composeを使ってテスト起動してみます。

$ docker-compose build
$ docker-compose up -d

http://localhost:5000/ を確認してください。以下のように、APIドキュメントが表示されるはずです。

完成したAPIドキュメント

各項目をクリックすると、実際にAPIを試すことができるようになっています。また、先ほど指定したJSONモデルもしっかりと表示されていることがわかります。

Flask_03.PNG

さらに、curlなどでアクセスすればきちんとレスポンスが返ってくることも確認できます。今回はデータを入れていないので空のJSONだけが返ってきますけれども。

$ curl http://localhost:5000/todo/ 
[]

おわりに

ここまでFlask-RESTPlusを使ってSwaggerを統合したRESTfulなAPIを作成してきました。モデルの定義とドキュメント作成が同時に行えるので非常に便利です。

今回は紹介しませんでしたが、このほかにもリクエストのパースやエラーハンドリングなど、多彩な機能を取りそろえています。詳しいことは公式ドキュメントも参考にしてみてください。

最後になりましたが、 Muroran Institute of Technology Advent Calendar 2017 にご参加いただいた皆さん、ありがとうございました!

 

Flask-RESTPlus에서 REST API와 Swagger 문서를 통합

 

시작하기

이 기사는 Muroran Institute of Technology Advent Calendar 2017 25 일 기사입니다.

Flask를 사용하여 REST API를 만들고 싶을 때 동시에 문서로 Swagger도 정비하고 싶은 것, 있다고 생각합니다. 그러나 문서도 작성하면서 API를 쓰면 어느 쪽을 업데이트 할 때 어느 쪽을 업데이트 잊을 등, 점점 귀찮아지고 쉽습니다.

경축! 아무것도 안하여 에스천사게임즈가 새로운 모습으로 재오픈 하였습니다.
어린이용이며, 설치가 필요없는 브라우저 게임입니다.
https://s1004games.com

그래서 이번에는 간단한 ToDo 관리 API를 예로 들어 Flask-RESTPlus를 사용하여 API 정의와 Swagger 의한 문서 생성을 단번에 가버 방법을 소개합니다.

Python 환경 준비

우선 Python 환경을 준비하여 둡시다. Pyenv과 Virtualenv을 사용하여 바삭 바삭와 만들어갑니다. 가상 환경 이름은 restplus하고 있습니다 만 따로이든 상관 없습니다. 이번에는 최신 버전이다 Python3.6.4을 사용합니다.

$ mkdir restplus
 $ cd restplus
 $ pyenv virtualenv 3.6.4 restplus
 $ pyenv local restplus

파일의 준비

는 파일이나 디렉토리를 준비하고 있습니다. 다음과 같은 구조로 준비하십시오. 각각의 파일이 무엇에 쓰이는지는 후술합니다.

restplus /
├── rest_api /
│ ├── apis /
│ │ └── __init__.py
│ ├── models /
│ │ └── __init__.py
│ ├── __init__.py
│ ├── app.py
│ └── settings.py
├── Dockerfile
├── docker-compose.yml
└── requirements.txt

Python 라이브러리 도입

requirements.txt 편집

requirements.txt는 이번 이용하는 Python 라이브러리를 열거하고 있습니다. 다음과 같이 써 둡시다. Flask를 사용하기 때문에 ORM에는 SQL Alchemy를 채용했습니다. 또한 데이터베이스는 MySQL을 사용하기로합니다. 이에 따라 flask-sqlalchemy채택 pymysql을 설치해야합니다.

requirements.txt
flask
flask-restplus
flask-sqlalchemy
pymysql

라이브러리 설치

편집 한 후에 다음 명령으로 설치하게됩니다. 체하면 차다하면 Docker에서 달리기에 넣지 않아도 좋다고 말하면 좋겠지 만, IDE 등으로 보완을 듣게하기 위해 넣어 둡시다.

$ pip install  -r requirements.txt

코어 부분의 작성

여기까지되면 드디어 코딩에 들어갑니다. Flask를 이용한 Web 응용 프로그램을 평소대로 느낌으로 구축하고 있습니다.

API 엔드 포인트의 준비

apis/__init__.py이 API 엔드 포인트를 등록 할 준비를합니다. Flask RESTPlus에서 준비된 API객체를 초기화 해 둡시다.

apis / __ init__.py
from  flask_restplus  import  Api

# API 정보를 지정하고 초기화 
api  =  Api ( 
    title = 'Test API' , 
    version = '1.0' , 
    description = 'Swagger을 통합 한 REST API에 대한 샘플' 
)

ORM 준비

models/__init__.py는 SQL Alchemy 초기화합니다. 이 파일에는 DB 스키마를 정의하고 있습니다.

models / __ init__.py
from  flask_sqlalchemy  import  SQLAlchemy

# SQLAlchemy 초기화 
db  =  SQLAlchemy ()

응용 프로그램의 여러 설정

settings.py는 응용 프로그램의 설정을 설명하고 있습니다. 특히 SQL 연결 정보 등의 기재는 필수입니다.

settings.py
from  os  import  environ

# 디버그 모드를 활성화 
DEBUG  =  True

# Swagger의 기본 형식을 List로하는 
SWAGGER_UI_DOC_EXPANSION  =  'list' 
# Validation 활성화 
RESTPLUS_VALIDATE  =  True

# SQL 연결 정보 
# 컨테이너 측에 환경 변수로 전달하는이 형식으로받을 
SQLALCHEMY_DATABASE_URI  =  environ [ 'MYSQL_URL' ] 
SQLALCHEMY_TRACK_MODIFICATIONS  =  True

Flask 응용 프로그램의 정의

여기까지 준비되면 Flask 응용 프로그램을 정의하고 있습니다. app변수에 Flask 객체를 넣어 그것을 인수로 처리하면서 API 엔드 포인트 및 SQL Alchemy와의 제휴를 실시합니다. 마지막으로 run메소드를 실행하면 OK입니다. 이때 Docker에서 실행되기 때문에 host지정 잊지 않도록합시다.

app.py
from  flask  import  Flask

from  rest_api  import  settings 
from  rest_api.apis  import  api 
from  rest_api.models  import  db

# Flask 본체 
app  =  Flask ( __name__ )


def  configure_app ( flask_app :  Flask )  ->  None : 
    # DB 연결 정보와 Swagger의 표시 형식을 지정 
    flask_app . config [ 'SQLALCHEMY_DATABASE_URI' ]  =  settings . SQLALCHEMY_DATABASE_URI 
    flask_app . config [ 'SQLALCHEMY_TRACK_MODIFICATIONS' ]  =  settings . SQLALCHEMY_TRACK_MODIFICATIONS 
    flask_app . config [ 'SWAGGER_UI_DOC_EXPANSION' ]  =  settings . SWAGGER_UI_DOC_EXPANSION
    flask_app . config [ 'RESTPLUS_VALIDATE' ]  =  settings . RESTPLUS_VALIDATE


def  initialize_app ( flask_app :  Flask )  ->  None : 
    # Flask에 API와 DB 정보를 등록 
    configure_app ( flask_app ) 
    api . init_app ( flask_app ) 
    db . init_app ( flask_app ) 
    db . create_all ( app = flask_app )


def  main ()  ->  None : 
    # Flask를 초기화하고 실행 
    initialize_app ( app ) 
    app . run ( host = '0.0.0.0' ,  debug = settings . DEBUG )


if  __name__  ==  '__main__' : 
    main ()

Docker 준비

Dockerfile

응용 프로그램을 Docker에 싣는 데 Dockerfile을 씁니다. MySQL의 시작을 기다리는 Dockerize도 함께 빚어 둡니다.

FROM python : alpine3.6

WORKDIR / usr / src / app

ENV DOCKERIZE_VERSION v0.6.0

COPY requirements.txt ./ 
RUN pip install  -U  --no-cache-dir  -r requirements.txt \
  && apk add --no-cache openssl \
  && wget https://github.com/jwilder/dockerize/releases/ download / $ DOCKERIZE_VERSION / dockerize-alpine-linux-amd64- $ DOCKERIZE_VERSION .tar.gz \
  &&  tar  -C / usr / local / bin -xzvf dockerize-alpine-linux-amd64- $ DOCKERIZE_VERSION .tar.gz \
  &&  rm dockerize -alpine-linux-amd64- $ DOCKERIZE_VERSION.tar.gz

COPY . 
CMD [ "python", "rest_api / app.py"]

docker-compose.yml

Dockerfile뿐만 MySQL의 통합이 어렵 기 때문에 docker-compose.yml도 써 둡시다. 앱 내부에서 import를 사용하기 때문에 해당 디렉토리를 PYTHONPATH환경 변수로 전달하여주는 것을 잊지 않도록하십시오. 또한 MySQL의 연결 정보도 전달 줍시다.

version :  ' 3'

services : 
  restapi : 
    build :  . 
    ports : 
      -  " 5000 : 5000" 
    environment : 
      MYSQL_URL :  ' mysql + pymysql : // test : user_pass @ mysql / test' 
      PYTHONPATH :  ' / usr / src / app' 
    links : 
      -  mysql 
    entrypoint : 
      -  dockerize 
      -  -timeout 
      -  60s 
      -  -wait 
      -  tcp : // mysql : 3306 
    command :  python /usr/src/app/rest_api/app.py

  mysql : 
    image :  mariadb : latest 
    environment : 
      MYSQL_ROOT_PASSWORD :  ' root_pass' 
      MYSQL_DATABASE :  ' test' 
      MYSQL_USER :  ' test' 
      MYSQL_PASSWORD :  ' user_pass' 
      command :  mysqld --character-set-server = utf8 --collation-server = utf8_unicode_ci

시작 테스트

여기까지 할 수 있으면 시작 테스트를 실시합니다. docker-compose 표시 くさくっ와 해버입니다.

$ docker-compose build
 $ docker-compose up -d

시작했으면 http : // localhost : 5000 / 에 방문하면 다음과 같은 화면이 나타납니다. 여기에 점점 API의 스펙이 완성되어갑니다.

Flask_01.PNG

DB 스키마 정의

이어 데이터베이스의 스키마를 정의하고 있습니다. models/__init__.py에 곰곰히 쓰고갑니다. ToDo 사용하는 스키마를 쉽게 정의합니다. models/__init__.py다음의 설명을 덧붙이합니다. db변수에 저장 한 SQLAlchemy 개체를 유용하여 컬럼과 그 유형을 정의합니다. 이때 primary_key설정 한 Integer컬럼은 자동 증가가 적용됩니다.

models / __ init__.py
from  flask_sqlalchemy  import  SQLAlchemy

# SQLAlchemy 초기화 
db  =  SQLAlchemy ()


# User 스키마 정의 
class  User ( db . Model ) : 
    __tablename__  =  'users' 
    id  =  db . Column ( db . Integer ,  nullable = False ,  primary_key = True ) 
    name  =  db . Column ( db . Text ,  nullable = False ) 
    email  =  db . Column ( db . Text,  nullable = False )


# ToDo 스키마 정의 
class  ToDo ( db . Model ) : 
    __tablename__  =  'todo' 
    id  =  db . Column ( db . Integer ,  nullable = False ,  primary_key = True ) 
    user_id  =  db . Column ( db . Integer ,  db . ForeignKey ( 'users.id' )  nullable = False ) 
    title  = db . Column ( db . Text ,  nullable = False ) 
    description  =  db . Column ( db . Text ,  nullable = False )

API 엔드 포인트의 정의

정의 파일 만들기

데이터베이스의 준비가되면 API 엔드 포인트를 설계합시다. apis/디렉토리에 todo.py도시 user.py를 새로 만들고 거기에 정의를 써서갑니다.

$ touch apis / todo.py apis / user.py

Namespace 및 JSON 모델의 정의

다른 API 엔드 포인트를 구별하는 Namespace를 작성합니다. 또한 반환 싶은 JSON 모델을 정의하자. 사용해도 없는데 Resource을 import하고 있습니다 만,이 후 즉시 사용하므로 여기에서 import 해 버립니다.

Namespace 객체를 생성 할 때의 제 1 인수에는 엔드 포인트의 접두사를 지정합니다. 예를 들어 todo.py에서 todo지정하고 있습니다 만, /todo다음에 앞으로 정의하는 엔드 포인트가 부를 수 있습니다. 만든 Namespace는 잊지 않고 API에 가입하십시오.

JSON 모델의 정의는 DB의 컬럼 이름과 같은 문자열을 키로 사용하여 사전에 실시합니다. 각 요소에 대해 필수 여부 또는 주는데,과 같은 값을 지정할 수 있습니다. 이러한 정보는 모두 Swagger 문서에 반영됩니다.

apis / todo.py
from  flask_restplus  import  Namespace ,  fields ,  Resource

# Namespace 초기화 
todo_namespace  =  Namespace ( 'todo' ,  description = 'ToDo 끝점' )

# JSON 모델의 정의 
todo  =  todo_namespace . model ( 'ToDo' ,  { 
    'user_id' :  fields . Integer ( 
        required = True , 
        description = 'ToDo 등록 된 사용자 ID' , 
        example = '0' 
    ), 
    'id' :  fields . Integer ( 
        required = True , 
        description = 'ToDo의 ID' , 
        example = '1' 
    ), 
    'title' :  fields. String ( 
        required = True , 
        description = 'ToDo 제목' , 
        example = '일어나' 
    ), 
    'description' :  fields . String ( 
        required = True , 
        description = 'ToDo 상세' , 
        example = '아침 7시에 일어나 싶다' 
    ) 
})
apis / user.py
from  flask_restplus  import  Namespace ,  fields ,  Resource

# Namespace 초기화 
user_namespace  =  Namespace ( 'users' ,  description = '사용자 관련 끝점' )

# JSON 모델의 정의 
user  =  user_namespace . model ( 'User' ,  { 
    'id' :  fields . Integer ( 
        required = True , 
        description = '' , 
        example = '0' 
    ), 
    'name' :  fields . String ( 
        required = True , 
        description = '' , 
        example = 'Aruneko' 
    ), 
    'email' :  fields .String ( 
        required = True , 
        description = '' , 
        example = 'aruneko@example.com' 
    ) 
})

엔드 포인트의 추가

Nemespace과 JSON 모델이 정의되면 드디어 엔드 포인트를 추가합니다. 방식은 간단하고 Resource클래스를 상속 한 클래스를 만들어 HTTP 메소드와 같은 이름의 메소드를 작성하고 라우팅에 필요한 어노테이션뿐입니다. 이를 바탕으로 apis 다음 파일의 최종 형태는 다음과 같습니다.

각 메소드의 시작에 큰 따옴표를 6 개의 사용한 여러 줄 주석을 꽂아 API 설명을 Swagger에 반영 할 수 있습니다. 1 행이 간이적인 설명에서 2 행에는 Markdown 문법을 사용한 상세한 설명을 기입 할 수 있습니다.

marshal_with marshal_list_with는 돌아 오는 JSON의 형식을 지정할 수 있습니다. 전자는 단체를, 후자는 목록 화 한 것을 돌려줍니다. 앞서 정의한 JSON 모델을 인수에 취해 줍시다. expect그 역에서 보내 주었으면 JSON 모델을 지정합니다.

apis / todo.py
from  flask_restplus  import  Namespace ,  fields ,  Resource

from  rest_api.apis  import  api 
from  rest_api.models  import  ToDo ,  db

# Namespace 초기화 및 등록 
todo_namespace  =  Namespace ( 'todo' ,  description = 'ToDo 끝점' )

# JSON 모델의 정의 
todo  =  todo_namespace . model ( 'ToDo' ,  { 
    'user_id' :  fields . Integer ( 
        required = True , 
        description = 'ToDo 등록 된 사용자 ID' , 
        example = '0' 
    ), 
    'id' :  fields . Integer ( 
        required = True , 
        description = 'ToDo의 ID' , 
        example = '1' 
    ), 
    'title' :  fields. String ( 
        required = True , 
        description = 'ToDo 제목' , 
        example = '일어나' 
    ), 
    'description' :  fields . String ( 
        required = True , 
        description = 'ToDo 상세' , 
        example = '아침 7시에 일어나 싶다' 
    ) 
})


@ todo_namespace.route ( '/' ) 
class  ToDoList ( Resource ) : 
    # todo 모델을 이용하여 결과를 분석해서 목록에서 반환 
    @ todo_namespace.marshal_list_with ( todo ) 
    def  get ( self ) : 
        "" "
        목록 취득
        "" " 
        return  ToDo . query . all ()

    @ todo_namespace.marshal_with ( todo ) 
    @ todo_namespace.expect ( todo ,  validate = True ) 
    def  post ( self ) : 
        "" "
        ToDo 등록
        "" " 
        # 조금 귀찮은 때문에 구현 또한 이번 
        pass


@ todo_namespace.route ( '/ <int : todo_id>' ) 
class  ToDoController ( Resource ) : 
    # todo 모델을 이용하여 결과를 분석해서 단체로 반환 
    @ todo_namespace.marshal_with ( todo ) 
    def  get ( self ,  todo_id ) : 
        "" "
        ToDo 상세
        "" " 
        # 단 1 개도 발견되지 않는 경우 404를 반환 
        return  ToDo . query . filter ( ToDo . id  ==  todo_id ) . first_or_404 ()

    def  delete ( self ,  todo_id ) : 
        "" "
        ToDo 삭제
        "" " 
        # 찾지 못한 경우의 처리 않지만 용서 
        target_todo  =  ToDo . query . filter ( ToDo . id  ==  todo_id ) . first () 
        db . session . delete ( target_todo ) 
        return  { 'message' :  ' Success ' }  200
apis / user.py
from  flask_restplus  import  Namespace ,  fields ,  Resource

from  rest_api.apis  import  api

# Namespace 초기화 및 등록 
from  rest_api.apis.todo  import  todo 
from  rest_api.models  import  User ,  db ,  ToDo

user_namespace  =  Namespace ( 'users' ,  description = '사용자 관련 끝점' )

# JSON 모델의 정의 
user  =  user_namespace . model ( 'User' ,  { 
    'id' :  fields . Integer ( 
        required = True , 
        description = '' , 
        example = '0' 
    ), 
    'name' :  fields . String ( 
        required = True , 
        description = '' , 
        example = 'Aruneko' 
    ), 
    'email' :  fields .String ( 
        required = True , 
        description = '' , 
        example = 'aruneko@example.com' 
    ) 
})


@ user_namespace.route ( '/' ) 
class  UserList ( Resource ) : 
    @ user_namespace.marshal_list_with ( user ) 
    def  get ( self ) : 
        "" "
        사용자 목록
        "" " 
        return  User . query . all ()

    @ user_namespace.marshal_with ( user ) 
    @ user_namespace.expect ( user ) 
    def  post ( self ) : 
        "" "
        등록
        "" " 
        # 조금 귀찮은 때문에 구현 또한 이번 
        pass


@ user_namespace.route ( '/ <int : user_id>' ) 
class  UserController ( Resource ) : 
    @ user_namespace.marshal_with ( user ) 
    def  get ( self ,  user_id ) : 
        "" "
        사용자 상세
        "" " 
        return  User . query . filter ( User . id  ==  user_id ) . first_or_404 ()

    def  delete ( self ,  user_id ) : 
        "" "
        사용자 삭제
        "" " 
        target_user  =  User . query . filter ( User . id  ==  user_id ) . first () 
        db . session . delete ( target_user ) 
        return  { 'message' :  'Success' }  200


@ user_namespace.route ( '/ <int : user_id> / todo' ) 
class  ToDoByUser ( Resource ) : 
    @ user_namespace.marshal_list_with ( todo ) 
    def  get ( self ,  user_id ) : 
        "" "
        사용자 당 ToDo 취득
        "" " 
        return  ToDo . query . filter ( ToDo . user_id  ==  user_id ) . all ()

엔드 포인트 등록

엔드 포인트를 만든 후에 등록합니다. apis/__init__.py에 기입합시다. add_namespace메소드를 이용하십시오.

apis / __ init__.py
from  flask_restplus  import  Api

from  rest_api.apis.todo  import  todo_namespace 
from  rest_api.apis.user  import  user_namespace

# API 정보를 지정하고 초기화 
api  =  Api ( 
    title = 'Test API' , 
    version = '1.0' , 
    description = 'Swagger을 통합 한 REST API에 대한 샘플' 
)

api . add_namespace ( todo_namespace ) 
api . add_namespace ( user_namespace )

테스트 시작

다시 docker-compose를 사용하여 테스트 시작하려고합니다.

$ docker-compose build
 $ docker-compose up -d

http : // localhost : 5000 / 을 확인하십시오. 다음과 같이 API 문서가 나타날 것입니다.

완성 된 API 문서

각 항목을 클릭하면 실제로 API를 시도 할 수있게되어 있습니다. 또한 이전에 지정한 JSON 모델도 제대로 표시되는 것을 알 수 있습니다.

Flask_03.PNG

또한 curl 등으로 접속하면 제대로 응답이 되돌아 오는 것을 확인할 수 있습니다. 이번에는 데이터를 넣지 않기 때문에 빈 JSON 만이 돌아왔다 그러나.

$ curl http : // localhost : 5000 / todo / 
 []

결론

여기까지 Flask-RESTPlus를 사용하여 Swagger을 통합 한 RESTful API를 작성했습니다. 모델의 정의 및 문서 작성이 동시에 실시 할 수 있으므로 매우 편리합니다.

이번은 소개하지 않았지만,이 밖에도 요청 퍼스 및 오류 처리 등 다양한 기능을 갖추고 있습니다. 자세한 사항은 공식 문서 도 참고해보십시오.

마지막으로했지만 Muroran Institute of Technology Advent Calendar 2017 에 참가해 주신 여러분, 감사합니다!

 

[출처] https://qiita.com/Aruneko/items/2adbf12bb5bace32e002

 

 

 

본 웹사이트는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.
번호 제목 글쓴이 날짜 조회 수
251 Data Structures and Algorithm In python 파이썬으로 자료구조와 알고리즘 : 출처 인터넷 file 졸리운_곰 2018.08.27 315
250 Mastering Basic Algorithms in the Python Language 이북 출처 인터넷 file 졸리운_곰 2018.08.27 381
249 텐서플로우-#1 자료형의 이해 file 졸리운_곰 2018.08.15 272
248 How to build a simple neural network in 9 lines of Python code file 졸리운_곰 2018.08.14 294
247 node.js python-shell을 활용하여 python 실행 file 졸리운_곰 2018.08.14 365
246 Python, PyV8로 javascript 실행하기 file 졸리운_곰 2018.08.14 221
245 파이썬 플라스크 프레임워크 소개 졸리운_곰 2018.08.03 130
244 주피터(jupyter notebook) 원격 접속 file 졸리운_곰 2018.07.10 142
243 Pycharm 원격 서버 연결하기 file 졸리운_곰 2018.07.10 249
242 The Ultimate Flask Front-End – Part 2 file 졸리운_곰 2018.06.22 85
241 The Ultimate Flask Front-End file 졸리운_곰 2018.06.22 139
240 Django + djangorestframework + django_rest_swagger 시작 file 졸리운_곰 2018.05.27 65
239 Does Python SciPy need BLAS? 졸리운_곰 2018.05.26 85
238 PyCharm과 함께 DJango와 RestFramework를 활용한 웹 사이트 구축하기 file 졸리운_곰 2018.05.22 192
» Flask-RESTPlus에서 REST API와 Swagger 문서를 통합 file 졸리운_곰 2018.05.22 353
236 Building beautiful REST APIs using Flask, Swagger UI and Flask-RESTPlus file 졸리운_곰 2018.05.22 263
235 [Python] Flask & flask-restplus && swagger ui file 졸리운_곰 2018.05.22 147
234 Django에서 MySQL DB를 연동하기 pycharm file 졸리운_곰 2018.04.10 427
233 Python Flask 로 간단한 REST API 작성하기 file 졸리운_곰 2018.04.07 226
232 Mining English and Korean text with Python file 졸리운_곰 2018.03.26 8187
대표 김성준 주소 : 경기 용인 분당수지 U타워 등록번호 : 142-07-27414
통신판매업 신고 : 제2012-용인수지-0185호 출판업 신고 : 수지구청 제 123호 개인정보보호최고책임자 : 김성준 sjkim70@stechstar.com
대표전화 : 010-4589-2193 [fax] 02-6280-1294 COPYRIGHT(C) stechstar.com ALL RIGHTS RESERVED