Python SQLAlchemyとmarshmallowを使ってjson出力できるAPIを実装する

# 概要
FlaskによるWEBアプリケーションで最も使われるORMはSQLAlchemyだと思っていますが、SQLAlchemyによって実装されたModelをjsonify等を使ってjsonに変換しようとすると直接変換することができません。
この場合、[Marshmallow](https://flask-marshmallow.readthedocs.io/en/latest/)を活用すると簡単に実装することが可能なのですが、意外と日本語記事が少なかったのでまとめてみました！ 

また今回の実装コードは[github](https://github.com/human92/marshmallow)にて公開しています。

#環境
- OS: Windows 10
- Python: Python 3.6.6 (Anaconda custom)
- DB: MySQL 8.0.12
- Framework: Flask 1.0.2  
※仮想環境はpipenvを使用

#アプリの実装
かの有名な[Flask Tutorial](https://study-flask.readthedocs.io/ja/latest/02.html)をベースに実装していきます。

###初期準備
Tutorialをベースに各ファイルを作成していきます。各ファイルの記述はTutorial通りにやればはまらないはず。

```Bash:ディレクトリ構成
C:.
│  manage.py
│  Pipfile
│  Pipfile.lock
│  
└─flaskr
    │  config.py
    │  models.py
    │  views.py
    │  __init__.py
    │  
    ├─static
    │      style.css
    │      
    └─templates
            layout.html
            show_entries.html
```
###DB(MySQL)との連携
参考までに記載しておきます。 
まず、pymysqlをインストールします。

```python
pipenv install pymysql
```

config.pyの接続情報を下記のように更新します。

```python:config.py
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://user:password@localhost/DBname?charset=utf8'
SECRET_KEY = 'secret_key'
SQLALCHEMY_TRACK_MODIFICATIONS = True
```
また、\_\_init__.pyの

```python:__init__.py
from flask.ext.sqlalchemy import SQLAlchemy
```
の部分を下記の通りに変更すると無用なエラーがでません。

```python:__init__.py
#from flask.ext.sqlalchemy import SQLAlchemy
#↑はエラーがでる
from flask_sqlalchemy import SQLAlchemy
```
###接続テスト
コマンドプロンプト上でPythonを起動し、下記のコマンドをうつとMysqlの任意のDB内に"entries"というテーブルができます。

```
>>> from flaskr.models import init
>>> init()
```
###画面での確認
Tutorialの記事を参考にしながら投稿データを作成し、下記のコードをコマンドプロンプト上に入力し、サーバーを起動してみてください。

```
python manage.py
```
そして[http://127.0.0.1:5000](http://127.0.0.1:5000/)にアクセスするとブログの画面が出てくるはずです！
#APIの実装
では本題のAPIの実装を行っていきます。 
概要にも書きましたが、今回は[Marshmallow](https://flask-marshmallow.readthedocs.io/en/latest/)とその関連パッケージを使用してAPIを作成していきます。
###関連パッケージのインストール
3種類のパッケージをインストールします。

```
pipenv install marshmallow
pipenv install flask_marshmallow
pipenv install marshmallow-sqlalchemy
```
###初期化ファイルの更新
\_\_init__.pyを更新し、作成したmodelをjsonに変換するスキーマを定義します。
Marshmallowを使用する場合、maでスキーマを定義するのが一般的（らしい）。

```python:__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow

app = Flask(__name__)
app.config.from_object('flaskr.config')

db = SQLAlchemy(app)
ma = Marshmallow(app)

import flaskr.views
```
###API連携用のModelの作成
API用のModelを定義します。
Tutorialで作成したEntryクラスを使用して、Marshmallowのスキーマを定義します。
SQLAlchemyを使用している場合、Metaで指定してあげるだけなので簡単！

```python:models.py
from flaskr import db, ma
from marshmallow_sqlalchemy import ModelSchema

#DBのモデリングを行ったModel-----------------
class Entry(db.Model):
    __tablename__ = 'entries'
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.Text)
    text = db.Column(db.Text)

    def __repr__(self):
        return '<Entry id={id} title={title!r}>'.format(
                id=self.id, title=self.title)

#json変換用----------------------------------
class EntrySchema(ma.ModelSchema):
    class Meta:
        model = Entry
```
###API用のURL(View)の作成
API用のURLをviews.pyに作成していきます。
今回はAPIにリクエストを渡す⇒jsonで内容が返ってくる、ということをしたいので、下記のように更新していきます。


```python:views.py
from flask import request, redirect, url_for, render_template, flash, jsonify
from flaskr import app, db, ma
from flaskr.models import Entry
from flaskr.models import EntrySchema

#ブラウザから見れる画面用-------------------------------------------------------
@app.route('/')
def show_entries():
        entries = Entry.query.order_by(Entry.id.desc()).all()
        return render_template('show_entries.html', entries=entries)

#json変換用(API)----------------------------------------------------------------
@app.route('/api', methods=['POST'])
def show_entries_json():
        entries = Entry.query.order_by(Entry.id.desc()).all()
        entries_schema = EntrySchema(many=True)
        return jsonify({'entries': entries_schema.dump(entries).data})
```
>entries_schema = EntrySchema(many=True)

DBに複数のレコードが入力されている場合、many=Trueを指定してあげないとレコードが返ってこなくなってしまうので注意です。
###テスト
うまく実装できているかテストを行います。 
テストには[Postman](https://www.getpostman.com/)というAPIの確認を行えるツールを使います。 
サーバーを起動させて、[http://127.0.0.1:5000/api](http://127.0.0.1:5000/api)のリクエストをsendしてみてください。
うまくいっていれば画面下部にjsonデータが返ってきているはずです！

```json:entriesのデータ
{
    "entries": [
        {
            "id": 1,
            "text": "text",
            "title": "title"
        }
    ]
}
```
#まとめ
SQLAlchemyを使用している場合、シンプルなmodelならばそれほど時間をかけることなくjson変換をすることができました。 使いどころとしてはサーバーサイドがFlask、フロントサイドはVue.jsなどのSPAで開発がしたい！という場合、けっこう有用かと思います。  
ただmodelが多くなってくると、その分定義するスキーマも多くなっているので、管理のためにはファイルを分けるなどを検討してほうがよさげですね。 

#参考
公式のドキュメントリンクを記載しておきます。
- flask-marshmallow: https://flask-marshmallow.readthedocs.io/en/latest/
- marshmallow-sqlalchemy: https://marshmallow-sqlalchemy.readthedocs.io/en/latest/
- marshmallow: https://marshmallow.readthedocs.io/en/3.0/index.html