【Python】0からWebアプリ！ハンズオン(4)～データ成形編～

# 概要
Pythonで0からWebアプリを作る機会があったので、そのまとめとして！
この記事ではデータベースから取得したデータの成形について書いています。
APIから返却されるデータに統一性がないと、フロントエンドエンジニアさんたちは困ってしまいます！
なので、今回はjsonというデータ形式に統一してデータを返す方法を説明します！

これまでの記事
[【Python】0からWebアプリ！ハンズオン(0)～環境構築編～ - Qiita](https://qiita.com/tsumasakky/items/d8069418d1fa6f2fa185)
[【Python】0からWebアプリ！ハンズオン(1)～設計、DB構築編～ - Qiita](https://qiita.com/tsumasakky/items/8993b6645f2d8bef43f2)
[【Python】0からWebアプリ！ハンズオン(2)～ハローワールド編～ - Qiita](https://qiita.com/tsumasakky/items/07105da031340257065e)
[【Python】0からWebアプリ！ハンズオン(3)～API実装編～ - Qiita](https://qiita.com/tsumasakky/items/0aed1f2bd0b983e6d8ec)

# こんな方に読んでほしい
* プログラミング未経験だけどアプリ作ってみたい方
* 新人Webプログラマの方
* 経験者だけどPythonでWebアプリ書いたことない方

# ゴール
HTML, CSS, JavaScript, Python, SQLを使ってCURD機能を持ったWebアプリを作成するのが目標です。

# 必要なもの
* PC（Windows OS）
* インターネット回線
* わくわくした気持ち

# 1. フォルダ構成
今回作成するフォルダ、ファイルは以下の通りです。

todo/
　└ api/
　　├ index.py
　　└ Todo.py

# 2. データベースから取得したデータをクラスオブジェクトに変換する
まずはデータベースから取得したデータをプログラム上で扱いやすくするために、クラスオブジェクトに変換します。
Python3.7以降はdataclassesという機能があり、クラスの定義が楽になりました。
[dataclasses --- データクラス — Python 3.8.5 ドキュメント](https://docs.python.org/ja/3/library/dataclasses.html)

まず、コマンドプロンプト上で以下のコマンドを実行します。

```terminal:cmd
pip install dataclasses
```

続いて、apiフォルダ配下に新しくTodo.pyというファイルを作成してください。

```python:Todo.py
from dataclasses import dataclass

@dataclass
class Todo:
    id: str
    title: str
    created: str
    is_deleted: str
```

そしてindex.pyを修正します。


```python:index.py
# -*- coding:utf-8 -*-

# 外部のパッケージを読み込む
from bottle import route, run
import psycopg2
from Todo import Todo # 上で作成したTodoクラスの読み込みを追加

# データベースへの接続を取得する
def get_connection():
    return psycopg2.connect("host=localhost port=5432 dbname=TodoDatabase user=postgres password=postgres")

# Todoデータを取得
@route('/todos')
def get_todos():
    # データベースとの接続を確立
    with get_connection() as conn:
        # カーソルを生成
        with conn.cursor() as cur:
            # SQLを実行
            cur.execute('SELECT * FROM todo')
            
            # データベースへの問い合わせ結果を1件取得
            data = cur.fetchone()
            
            # DBから取得したデータをクラスオブジェクトに変換（Todoクラスのインスタンス生成）して返却
            todo = Todo(data[0], data[1], data[2], data[3])
            return str(todo)


# Webサーバーの実行構成
# URLの "http://[host]:[port]/[route]" の構成となる
run(host='localhost', port=8080, debug=True)
```
コマンドプロンプト上でアプリのルートフォルダに移動し、`python index.py`を実行してサーバーを起動し、ブラウザで http://localhost:8080/todos にアクセスすると以下のようなデータが表示されると思います。
![2020-08-28_10h47_51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147118/6ef6bac4-9af7-2001-dcff-a97fc798b89b.png)

# 2. クラスオブジェクトをjson形式に変換する
それでは本題のjson形式に変換してみましょう。

dataclassesで生成したクラスはdataclasses_jsonを使用することで簡単にjson形式に変換することができます。
[GitHub - lidatong/dataclasses-json: Easily serialize Data Classes to and from JSON](https://github.com/lidatong/dataclasses-json)

コマンドプロンプト上で以下のコマンドを実行します。

```
pip install dataclasses-json
```

Todo.pyを編集します。
dataclasses_jsonを追加でインポートし、dataclass_jsonアノテーションを追加します。

```python:Todo.py
from dataclasses import dataclass
from dataclasses_json import dataclass_json # 追加

@dataclass_json # 追加
@dataclass
class Todo:
    id: str
    title: str
    created: str
    is_deleted: str
```

そしてindex.pyを以下のように編集します。

```python:index.py
# -*- coding:utf-8 -*-

# 外部のパッケージを読み込む
from bottle import route, run, response # response追加
import psycopg2
from Todo import Todo
import json # 追加

# データベースへの接続を取得する
def get_connection():
    return psycopg2.connect("host=localhost port=5432 dbname=TodoDatabase user=postgres password=postgres")

# Todoデータを取得
@route('/todos')
def get_todos():
    # レスポンスのデータ形式をjsonに変更
    response.headers['Content-Type'] = 'application/json'
    response.headers['Cache-Control'] = 'no-cache'

    # データベースとの接続を確立
    with get_connection() as conn:
        # カーソルを生成
        with conn.cursor() as cur:
            # SQLを実行
            cur.execute('SELECT * FROM todo')
            
            # データベースへの問い合わせ結果を1件取得
            data = cur.fetchone()

            # DBから取得したデータをjsonに変換して返却
            # 作成日時（data[2]）は文字列型にキャストする
            # "ensure_ascii=False"を指定しないと、非ASCII文字が "\uXXXX" の形にエスケープされるので、日本語が返却できない
            # データベースのデータをクラス（Todo()）→辞書型（.__dict__）→json(json.dumps)に変換する必要がある
            todo = Todo(data[0], data[1], str(data[2]), data[3])
            return json.dumps(todo.__dict__, ensure_ascii=False)

# Webサーバーの実行構成
# URLの "http://[host]:[port]/[route]" の構成となる
run(host='localhost', port=8080, debug=True)

```
再度`python index.py`を実行してサーバーを起動し、ブラウザで http://localhost:8080/todos にアクセスすると、こんな形で返ってくるようになりました！
![2020-08-28_11h34_40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147118/ff8aa80e-f0ac-8813-2055-a1be13c73886.png)
このようにデータひとつひとつがkey-valueのセットになって返ってくることで、フロントエンドで非常に扱いやすくなりました。
また、フロントエンドエンジニアからAPIの仕様書を求められたときも非常に書きやすいのです！

# おまけ

ちなみにブラウザはGoogle Chromeで動作確認していますが、json形式のデータをフォーマットしてくれる拡張機能を入れています。
色付けしてくれたり、パネルの開閉ができたりして便利なのでぜひお試しあれ！
[JSON Formatter - Chrome ウェブストア](https://chrome.google.com/webstore/detail/json-formatter/bcjindcccaagfpapjjmafapmmgkkhgoa?hl=ja)

# まとめ
データベースのデータをjson形式で返してくれるRest APIの実装を行いました！

# 次回は･･･
今まではURLを直接入力してAPIにアクセスしていましたが、そろそろページを作っていきたいものです。
次回はHTMLを作成してJavaScriptのコードからアクセスしてみます！