Google Cloudの新DBMS、AlloyDB for PostgreSQLを触ってみた Vol.5
- AlloyDB
- AlloyDB for PostgreSQL
- DBMS
- Google Cloud
目次
まえがき
こんにちは。開発部の高井(Peacock)です。
今回はアプリケーション編として、より実際の利用例に近い形のものを紹介していきます。
(第1回よりテストデータとして使用している)英国不動産取引データを読み書きするWeb APIアプリケーションを作成してみました。
単純なCRUD操作のみを実装してみましたがPostgreSQL互換ということもあり、難なく実装出来たかと思います。
アプリケーション概要
使用したライブラリ・フレームワークについて
Python 3.11系をベースに構築しました。
WebフレームワークにFastAPI, ORMとしてSQLAlchemyを使用しています。今回、大規模ではないのでAlembicのようなマイグレーション用ツールは使用しませんでした。
- FastAPI 0.100.1 (Pydantic 2.1.1)
- SQLAlchemy 2.0.19 (& Psycopg 3.1.9)
ディレクトリ構成
gcloud.py
としてGoogle Cloud SDKのヘルパーモジュールを置いていますが、それ以外は一般的な構成かと思います。
以下のようなディレクトリ構成にしています。パッケージ内は概ね責務で分割しています。
.
├── alloydb_demo # FastAPI Webアプリケーションになるパッケージ
│ ├── __init__.py # アプリケーションルート( app = FastAPI() モジュール)
│ ├── crud.py # SQLAlchemyのクエリヘルパー(後述)
│ ├── db.py # SQLAlchemyでデータベースセッションを作成するためのヘルパー
│ ├── gcloud.py # AlloyDBへ接続するためGoogle Cloud SDKを使用したヘルパー(後述)
│ ├── models.py # SQLAlchemyのモデル情報(今回は1テーブルのみ)
│ ├── routers.py # FastAPIのルーティング関数群(エンドポイント一覧は後述)
│ └── schemas.py # FastAPIが返すレスポンスのスキーマ定義
├── compose.yml # Docker compose定義(後述)
├── constraints.txt # 依存ライブラリのバージョン情報
├── Dockerfile # FastAPI用Docker Image定義
├── entrypoint.py # 起動時に実行するUvicorn(ASGIサーバー)とFastAPIの起動用スクリプト
├── pyproject.toml # Pythonパッケージのメタデータ記述用
└── migrate.py # SQLAlchemyでの簡単なマイグレーション用スクリプト
実際の動作例
FastAPIを使用しているのでSwagger UI( /docs
)からテストなどが出来ます。以下のような画面になります。
エンドポイント一覧を確認できたので、実際にHTTPクライアント(今回はHTTPie)でテストしてみます。
まずはヘルスチェックでアクセスできることを確認します。
$ http get :8000/_genki
HTTP/1.1 200 OK
content-length: 27
content-type: application/json
date: xxx
server: uvicorn
{
"code": 200,
"message": "OK"
}
/list
で取引のキーを取得してみます。取得できたキーのリストをコピーしておきます。
$ http get :8000/list?limit=5
HTTP/1.1 200 OK
content-length: 196
content-type: application/json
date: xxx
server: uvicorn
[
"20e2441a-0f16-49ab-97d4-8737e62a5d93",
"d893ee64-4464-44b5-b01b-8e62403ed83c",
"f9f753a8-e56a-4ecc-9927-8e626a471a92",
"e166398a-a19e-470e-bb5a-83b4c254cf6d",
"a9a3c463-3ca0-4d71-8cf9-83b4d0536eea"
]
そのうちの1つを /read
を叩いて詳細を取得してみます。
$ http get :8000/read/3311b7f4-5d50-43bc-90a6-83b6b7d522c8
HTTP/1.1 200 OK
content-length: 359
content-type: application/json
date: xxx
server: uvicorn
{
"city": "NOTTINGHAM",
"county": "NOTTINGHAMSHIRE",
"district": "NOTTINGHAM",
"duration": "F",
"locality": "NOTTINGHAM",
"newly_built": false,
"paon": "11",
"postcode": "NG5 9BJ",
"ppd_category_type": "A",
"price": "46000",
"property_type": "D",
"record_status": "A",
"saon": null,
"street": "SYKE ROAD",
"transaction": "3311b7f4-5d50-43bc-90a6-83b6b7d522c8",
"transfer_date": "1995-08-24"
}
/update
で適当な値を入れて変更してみます。
$ echo '{
"city": "NOTTINGHAM",
"county": "NOTTINGHAMSHIRE",
"district": "UPDATED DISTRICT",
"duration": "F",
"locality": "NOTTINGHAM",
"newly_built": false,
"paon": "11",
"postcode": "NG5 9BJ",
"ppd_category_type": "A",
"price": "99000",
"property_type": "D",
"record_status": "A",
"saon": null,
"street": "SYKE ROAD",
"transaction": "3311b7f4-5d50-43bc-90a6-83b6b7d522c8",
"transfer_date": "1995-08-24"
}' | jq --indent 0 |
http post :8000/update/3311b7f4-5d50-43bc-90a6-83b6b7d522c8
HTTP/1.1 200 OK
content-length: 365
content-type: application/json
date: xxx
server: uvicorn
{
"city": "NOTTINGHAM",
"county": "NOTTINGHAMSHIRE",
"district": "UPDATED DISTRICT",
"duration": "F",
"locality": "NOTTINGHAM",
"newly_built": false,
"paon": "11",
"postcode": "NG5 9BJ",
"ppd_category_type": "A",
"price": "99000",
"property_type": "D",
"record_status": "A",
"saon": null,
"street": "SYKE ROAD",
"transaction": "3311b7f4-5d50-43bc-90a6-83b6b7d522c8",
"transfer_date": "1995-08-24"
}
削除も /delete
で実行してみます。
$ http delete :8000/delete/3311b7f4-5d50-43bc-90a6-83b6b7d522c8
HTTP/1.1 200 OK
content-length: 32
content-type: application/json
date: xxx
server: uvicorn
{
"code": 200,
"message": "Deleted"
}
同じキーで /read
が404エラーになることも確認できます。
$ http get :8000/read/3311b7f4-5d50-43bc-90a6-83b6b7d522c8
HTTP/1.1 200 OK
content-length: 88
content-type: application/json
date: Fri, 18 Aug 2023 01:31:00 GMT
server: uvicorn
{
"code": 404,
"message": "Not found for transaction: 3311b7f4-5d50-43bc-90a6-83b6b7d522c8"
}
最後に削除したデータを /create
で追加します。今回は自動生成キーと先程コピーしたキーの2種類のデータを追加してみます。
$ echo '[
{
"city": "GRAYS",
"county": "THURROCK",
"district": "THURROCK",
"duration": "F",
"locality": "GRAYS",
"newly_built": true,
"paon": "100000",
"postcode": "RM16 4UR",
"ppd_category_type": "B",
"price": "50",
"property_type": "D",
"record_status": "A",
"saon": null,
"street": "HEATH ROAD",
"transfer_date": "2023-08-17"
},
{
"city": "NOTTINGHAM",
"county": "NOTTINGHAMSHIRE",
"district": "UPDATED DISTRICT",
"duration": "F",
"locality": "NOTTINGHAM",
"newly_built": false,
"paon": "11",
"postcode": "NG5 9BJ",
"ppd_category_type": "A",
"price": "99000",
"property_type": "D",
"record_status": "A",
"saon": null,
"street": "SYKE ROAD",
"transaction": "3311b7f4-5d50-43bc-90a6-83b6b7d522c8",
"transfer_date": "1995-08-24"
}
]' | jq --indent 0 | \
http put http://localhost:8000/create
HTTP/1.1 200 OK
content-length: 710
content-type: application/json
date: xxx
server: uvicorn
[
{
"city": "GRAYS",
"county": "THURROCK",
"district": "THURROCK",
"duration": "F",
"locality": "GRAYS",
"newly_built": true,
"paon": "100000",
"postcode": "RM16 4UR",
"ppd_category_type": "B",
"price": "50",
"property_type": "D",
"record_status": "A",
"saon": null,
"street": "HEATH ROAD",
"transaction": "1d60f8be-ceb5-4da1-ad97-de34dc8c2dbd", // 生成されたキー
"transfer_date": "2023-08-17"
},
{
"city": "NOTTINGHAM",
"county": "NOTTINGHAMSHIRE",
"district": "UPDATED DISTRICT",
"duration": "F",
"locality": "NOTTINGHAM",
"newly_built": false,
"paon": "11",
"postcode": "NG5 9BJ",
"ppd_category_type": "A",
"price": "99000",
"property_type": "D",
"record_status": "A",
"saon": null,
"street": "SYKE ROAD",
"transaction": "3311b7f4-5d50-43bc-90a6-83b6b7d522c8",
"transfer_date": "1995-08-24"
}
]
まとめ
今回はPythonのORMであるSQLAlchemyで単純なCRUD操作を行うWeb APIを作成して、実際に動かしてみました。
他のRDBMSを使用したアプリケーションと同じように開発でき、不自由なく動かすことが出来ました。
他のORMでも使用でき、特にent(Go言語)では実際の案件での使用実績もあります。(ご興味あればお問い合わせください)
次回はこの連載の最後として、発展編としてPythonでStreamlitによるデータ分析アプリとしてのサンプルを紹介できればと思います。