FastAPI: SQLAlchemy で MariaDB を使う (その 2)

こちらのプログラムを改造しました。
[FastAPI: SQLAlchemy で MariaDB を使う](https://qiita.com/ekzemplaro/items/9f84596fba2c532d45a9)
変更点
　id を string に
　age を追加

## プログラム ##

フォルダー構造は同じです。

```text
$ tree
.
├── __init__.py
├── crud.py
├── database.py
├── main.py
├── models.py
└── schemas.py
```

```py:crud.py
from sqlalchemy.orm import Session

from . import models, schemas

def get_user(db: Session, user_id: str):
    return db.query(models.User).filter(models.User.id == user_id).first()

def get_user_by_email(db: Session, email: str):
    return db.query(models.User).filter(models.User.email == email).first()

def get_users(db: Session, skip: int = 0, limit: int = 100):
    return db.query(models.User).offset(skip).limit(limit).all()

def create_user(db: Session, user: schemas.UserCreate):
    fake_hashed_password = user.password + "notreallyhashed"
    db_user = models.User(id=user.id,email=user.email, hashed_password=fake_hashed_password, name=user.name, age=user.age)
    db.add(db_user)
    db.commit()
    db.refresh(db_user)
    return db_user
```

```py:database.py
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
#
host ='localhost'
data_base = 'db_test'
user ='scott'
password = 'tiger123'
#
db_url = "mysql://" + user + ":" + password + "@" + host + "/" + data_base
#

engine = create_engine(db_url)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

Base = declarative_base()
```

```py:main.py
from fastapi import Depends, FastAPI, HTTPException
from sqlalchemy.orm import Session

from . import crud, models, schemas
from .database import SessionLocal, engine

models.Base.metadata.create_all(bind=engine)

app = FastAPI()


# Dependency
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()


@app.post("/users/", response_model=schemas.User)
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
    db_user = crud.get_user_by_email(db, email=user.email)
    if db_user:
        raise HTTPException(status_code=400, detail="Email already registered")
    return crud.create_user(db=db, user=user)


@app.get("/users/", response_model=list[schemas.User])
def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
    users = crud.get_users(db, skip=skip, limit=limit)
    return users


@app.get("/users/{user_id}", response_model=schemas.User)
def read_user(user_id: str, db: Session = Depends(get_db)):
    db_user = crud.get_user(db, user_id=user_id)
    if db_user is None:
        raise HTTPException(status_code=404, detail="User not found")
    return db_user
```

```py:models.py
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String
from sqlalchemy.orm import relationship

from .database import Base


class User(Base):
	__tablename__ = "users"

	id = Column(String(15), primary_key=True, index=True)
	email = Column(String(255), unique=True, index=True)
	hashed_password = Column(String(255))
	is_active = Column(Boolean, default=True)
	name = Column(String(255), index=True)
	age = Column(Integer)
```

```py:schemas.py
from pydantic import BaseModel

class UserBase(BaseModel):
	email: str

class UserCreate(UserBase):
	id: str
	password: str
	name: str
	age: int

class User(UserBase):
	id: str
	is_active: bool
	name: str
	age: int

	class Config:
		orm_mode = True
```

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

データの作成

```bash:http_create.sh
http http://127.0.0.1:8000/users/ id="t001" email="aa@bbb.com" password="secret" name="大谷" age=25
http http://127.0.0.1:8000/users/ id="t002" email="bb@bbb.com" password="secret" name="菊池" age=28
http http://127.0.0.1:8000/users/ id="t003" email="cc@bbb.com" password="secret" name="鈴木" age=26
```

データの取得

```bash:http_get.sh
http 'http://127.0.0.1:8000/users/?skip=0&limit=100'
```

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

```bash:http_user.sh
http http://127.0.0.1:8000/users/t001
```

実行結果

```text
$ ./http_user.sh 
HTTP/1.1 200 OK
content-length: 76
content-type: application/json
date: Fri, 19 May 2023 08:25:15 GMT
server: uvicorn

{
    "age": 25,
    "email": "aa@bbb.com",
    "id": "t001",
    "is_active": true,
    "name": "大谷"
}
```

## MariaDB の状態 ##

```text
MariaDB [db_test]> show columns from users;
+-----------------+--------------+------+-----+---------+-------+
| Field           | Type         | Null | Key | Default | Extra |
+-----------------+--------------+------+-----+---------+-------+
| id              | varchar(15)  | NO   | PRI | NULL    |       |
| email           | varchar(255) | YES  | UNI | NULL    |       |
| hashed_password | varchar(255) | YES  |     | NULL    |       |
| is_active       | tinyint(1)   | YES  |     | NULL    |       |
| name            | varchar(255) | YES  | MUL | NULL    |       |
| age             | int(11)      | YES  |     | NULL    |       |
+-----------------+--------------+------+-----+---------+-------+
6 rows in set (0.002 sec)

MariaDB [db_test]> select * from users;
+------+------------+-----------------------+-----------+--------+------+
| id   | email      | hashed_password       | is_active | name   | age  |
+------+------------+-----------------------+-----------+--------+------+
| t001 | aa@bbb.com | secretnotreallyhashed |         1 | 大谷   |   25 |
| t002 | bb@bbb.com | secretnotreallyhashed |         1 | 菊池   |   28 |
| t003 | cc@bbb.com | secretnotreallyhashed |         1 | 鈴木   |   26 |
+------+------------+-----------------------+-----------+--------+------+
3 rows in set (0.000 sec)
```