FastAPI: MongoDB を使う

こちらを参考にしました。
[FastAPI、React、MongoDBでの開発方法](https://techpr.info/python/farm-stack/)

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

```bash
sudo pip3 install motor
```

## プログラム ##
フォルダー構造

```text
$ tree
.
├── database.py
├── main.py
└── model.py
```

```py:database.py
# ------------------------------------------------------------------
#	database.py
#
#					May/02/2022
# ------------------------------------------------------------------
from model import City

import sys
import	datetime
#
import motor.motor_asyncio
from pymongo import MongoClient
import pymongo

# ------------------------------------------------------------------
client = motor.motor_asyncio.AsyncIOMotorClient('mongodb://localhost/?retryWrites=true&w=majority')
database = client.city
collection = database.tochigi

# ------------------------------------------------------------------
async def fetch_one_city(id):
	document = await collection.find_one({"id":id})
	return document
#
# ------------------------------------------------------------------
async def fetch_all_cities():
	cities = []
	cursor = collection.find({})
	async for document in cursor:
		cities.append(City(**document))
	return cities
#
# ------------------------------------------------------------------
async def create_city(city):
	document = city
	result = await collection.insert_one(document)
	return document
#
# ------------------------------------------------------------------
async def update_city(id, population):
	sys.stderr.write("*** update_city ***\n")
	date_mod = datetime.date.today()
	await collection.update_one({"id":id}, {"$set": {
		'population': population,
		'date_mod': '%s' % date_mod
		}})
	document = await collection.find_one({"id": id})
	return document
#
# ------------------------------------------------------------------
async def remove_city(id):
	await collection.delete_one({"id":id})
	return True
#
# ------------------------------------------------------------------
```

```py:main.py
# ------------------------------------------------------------------
#	main.py
#
#						May/02/2022
# ------------------------------------------------------------------
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from database import (
	fetch_one_city,
	fetch_all_cities,
	create_city,
	update_city,
	remove_city,
)

from model import City

# ------------------------------------------------------------------
app = FastAPI()

origins = [
	'http://localhost:3000',
	'http://localhost',
	]

app.add_middleware(
	CORSMiddleware,
	allow_origins=origins,
	allow_credentials=True,
	allow_methods=["*"],
	allow_headers=["*"],
)

# ------------------------------------------------------------------
@app.get("/")
def read_root():
	return {"Saluton"}

# ------------------------------------------------------------------
@app.get("/api/city")
async def get_cities():
	response = await fetch_all_cities()
	return response

# ------------------------------------------------------------------
@app.get("/api/city/{id}", response_model=City)
async def get_city_by_id(id):
	response = await fetch_one_city(id)
	if response:
		return response
	raise HTTPException(404, f"there is no City item with this id {id}")

# ------------------------------------------------------------------
@app.post("/api/city", response_model=City)
async def post_city(city:City):
	response = await create_city(city.dict())
	if response:
		return response
	raise HTTPException(400, "Sometheng went wrong / Bad Request")

# ------------------------------------------------------------------
@app.put("/api/city/{id}/", response_model=City)
async def put_city(id:str, population:int):
	response = await update_city(id, population)
	if response:
		return response
	raise HTTPException(404, f"there is no City item with this id {id}")

# ------------------------------------------------------------------
@app.delete("/api/city/{id}")
async def delete_city(id):
	response = await remove_city(id)
	if response:
		return "Successfully deleted city item!"
	raise HTTPException(404, f"there is no city item with this id {id}")

# ------------------------------------------------------------------
```

```py:model.py
# ------------------------------------------------------------------
#	model.py
#
#					May/02/2022
# ------------------------------------------------------------------
from pydantic import BaseModel

# ------------------------------------------------------------------
class City(BaseModel):
	id: str
	name: str
	population: int
	date_mod: str
# ------------------------------------------------------------------
```

## サーバーの起動 ##

```bash
uvicorn main:app --reload
```
http://localhost:8000/docs にアクセス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/b5ebb574-6b1d-28c4-4245-ede57449db47.png)


## テストスクリプト ##

```bash:go_create.sh
http  http://localhost:8000/api/city < in01.json
http  http://localhost:8000/api/city < in02.json
http  http://localhost:8000/api/city < in03.json
http  http://localhost:8000/api/city < in04.json
```

```json:in01.json
{
	"id": "t0921",
	"name": "宇都宮",
	"population": "34569",
	"date_mod": "2009-6-7"
}
```

```json:in02.json
{
	"id": "t0922", 
	"name": "小山",
	"population": "17952",
	"date_mod": "2009-5-19"
}
```

```json:in03.json
{
 	"id": "t0923",
	"name": "佐野",
	"population": "26929",
	"date_mod": "2009-3-28"
}
```

```json:in04.json
{
	"id": "t0924",
	"name": "足利",
	"population": "25197",
	"date_mod": "2009-12-21"
}
```

実行結果を mongosh で確認

```text
city> show collections
tochigi
city> db.tochigi.find()
[
  {
    _id: ObjectId("626fb6cb78ddd6a16c5f7eca"),
    id: 't0921',
    name: '宇都宮',
    population: 34569,
    date_mod: '2009-6-7'
  },
  {
    _id: ObjectId("626fb6cb78ddd6a16c5f7ecb"),
    id: 't0922',
    name: '小山',
    population: 17952,
    date_mod: '2009-5-19'
  },
  {
    _id: ObjectId("626fb6cc78ddd6a16c5f7ecc"),
    id: 't0923',
    name: '佐野',
    population: 26929,
    date_mod: '2009-3-28'
  },
  {
    _id: ObjectId("626fb6cd78ddd6a16c5f7ecd"),
    id: 't0924',
    name: '足利',
    population: 25197,
    date_mod: '2009-12-21'
  }
]
city> 
```

全てのデータを取得

```bash:get_all.sh
http http://localhost:8000/api/city
```

id を指定してデータを取得

```bash://get_one.sh
http http://localhost:8000/api/city/t0923
```

データの更新

```bash:update_one.sh
http PUT http://localhost:8000/api/city/t0923/ population==42100
```

データの削除

```bash:go_del.sh
http DELETE http://localhost:8000/api/city/t0922
```