４０代おっさん面会予約アプリを作ってみる②

## 本記事について

この記事はプログラミング初学者の私が学んでいく中でわからない単語や概要を分かりやすくまとめたものです。
もし不正などありましたらコメントにてお知らせいただければ幸いです。

## 前回の記事

<https://qiita.com/kou1121/items/bea7575176b07bd16bb9>

## データベース構築

今作っているフォルダーに新しく
`sql_app`を作る

その中に
`database.py`
`__init__.py`
ファイルを作る

database.pyはデータベースの基本設定を記載
__init__.pyがないと読み込まないので必要になる

### database.py

とりあえず、必要なものがあるので、それらを書きます。

```python
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

SQLALCHEMY_DATABASE_URL = 'sqlite:///./sql_app.db'

engine = create_engine(
    SQLALCHEMY_DATABASE_URL, connect_args={'check_same_thread': False} # sqlite使うのに必要
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
```

すいません。ここらへんは難しいので、後日勉強をします。

### モデルを書く

`sql_app`フォルダー内に
`models.py`を作る

```python
from sqlalchemy import Column, ForeignKey, Integer, String, DateTime
from .database import Base # database.pyのBaseをインポート

class User(Base): # Baseを継承
    __tablename__ = 'users' # テーブル名
    user_id = Column(Integer, primary_key=True, index=True) # プライマリーキーで設定、int型、indexを入れると検索が早くなる。
    username = Column(String, unique=True, index=True) # uniqueでかぶらないようにする。

class Room(Base): # Baseを継承
    __tablename__ = 'rooms' # テーブル名
    room_id = Column(Integer, primary_key=True, index=True) # プライマリーキーで設定、int型、indexを入れると検索が早くなる。
    room_name = Column(String, unique=True, index=True) # uniqueでかぶらないようにする。
    capacity = Column(Integer) # 定員

class Booking(Base): # Baseを継承
    __tablename__ = 'bookings' # テーブル名
    booking_id = Column(Integer, primary_key=True, index=True) # プライマリーキーで設定、int型、indexを入れると検索が早くなる。
    user_id = Column(Integer, ForeignKey('users.user_id', ondelete='SET NULL'), nullable=False) # ForeignKeyでusers.user_idに紐づけondelete='SET NULL'でusersが消されたらNULLにする
    room_id = Column(Integer, ForeignKey('rooms.room_id', ondelete='SET NULL'), nullable=False) # ForeignKeyでrooms.room_idに紐づけondelete='SET NULL'でroomsが消されたらNULLにする
    booked_num = Column(Integer)
    start_datetime = Column(DateTime, nullable=False)
    end_datetime = Column(DateTime, nullable=False)
```

### schemas.py

`sql_app`フォルダー内に
`schemas.py`を作る

```python
import datetime
from pydantic import BaseModel, Field

class Booking(BaseModel):
    booking_id: int # 予約ID
    user_id: int # ユーザーID usersテーブルと紐づけ
    room_id: int # 面会室ID roomsテーブルと紐づけ
    booked_num: int # 予約人数 面会室の定員まで
    start_datetime: datetime.datetime # 開始時刻
    end_datetime: datetime.datetime # 終了時刻

    class Config:
        orm_mode = True # ormモデルに対応させる

class User(BaseModel):
    user_id: int # ユーザーID
    username: str # = Field(max_length=12) # ユーザー名（12字文字まで）

    class Config:
        orm_mode = True # ormモデルに対応させる

class Room(BaseModel):
    room_id: int # 面会室ID
    room_name: str = Field(max_length=12) # 面会室名（12文字まで）
    capacity: int # 定員　各面会室ごとに定める

    class Config:
        orm_mode = True # ormモデルに対応させる
```

#### orm_modeについて

- さて、読み込み、アイテム、ユーザーのPydanticモデルの中に、内部のConfigクラスを追加します。このConfigクラスはPydanticに設定を提供するために使用します。
- Configクラスの中で、ORM_MODE = Trueという属性を設定します。
- PydanticのORM_MODEは、データがdictではなくORMモデル（または属性を持つ他の任意のオブジェクト）であっても、Pydanticモデルにデータを読み込むように指示します。そして、これでPydanticのモデルはORMと互換性があり、パス操作でresponse_modelの引数に宣言するだけでOKです。データベースモデルを返すことができるようになり、そこからデータを読み込んでくれるようになります。

＊参照
<https://qiita.com/nakapar_/items/4edd149abfa277c7b797>

### クラッド操作を関数にする

CRUD (クラッド）とは

システムに必要な4つの主要機能である「Create（生成）」「Read（読み取り）」「Update（更新）」「Delete（削除）」の頭文字を並べた用語

今回は

Create
Read
だけ

`sql_app`フォルダー内に
`crud.py`を作る

#### Read部分

```python
from sqlalchemy.orm import Session
from . import models, schemas # 同じフォルダー内のmodels,schemasをインポート

# ユーザー一覧取得
def get_users(db: Session, skip: int = 0, limit: int =100):
    return db.query(models.User).offset(skip).limit(limit).all()

# 面会室一覧取得
def get_rooms(db: Session, skip: int = 0, limit: int =100):
    return db.query(models.Room).offset(skip).limit(limit).all()

# 予約一覧取得
def get_bookings(db: Session, skip: int = 0, limit: int =100):
    return db.query(models.Booking).offset(skip).limit(limit).all()
```

`db.query(models.User)`
models.pyのUserモデルから参照する
`offset(skip)`skipに入った数からデータを持ってくる
`limit(limit)`limitに入れた数の件数を取ってくる

#### Create部分

```python
# 上を参照

# ユーザー登録
def create_user(db: Session, user: schemas.User): # schemas.Userのデータ構造のクラスを受け取る
    db_user = models.User(username=user.username) # インスタンスの生成
    db.add(db_user) #データベースに追加
    db.commit() # コミット
    db.refresh(db_user) # インスタンスをリフレッシュ
    return db_user

# 面会室登録
def create_room(db: Session, room: schemas.Room): # schemas.Roomのデータ構造のクラスを受け取る
    db_room = models.Room(room_name=room.room_name, capacity=room.capacity) # インスタンスの生成
    db.add(db_room) #データベースに追加
    db.commit() # コミット
    db.refresh(db_room) # インスタンスをリフレッシュ
    return db_room

# 予約登録
def create_booking(db: Session, booking: schemas.Booking): # schemas.Bookingのデータ構造のクラスを受け取る
    db_booking = models.Booking(
        user_id = booking.user_id,
        room_id = booking.room_id,
        booked_num = booking.booked_num,
        start_datetime = booking.start_datetime,
        end_datetime = booking.end_datetime
    ) # インスタンスの生成
    db.add(db_booking) #データベースに追加
    db.commit() # コミット
    db.refresh(db_booking) # インスタンスをリフレッシュ
    return db_booking
```

## 参考

https://www.udemy.com/course/python-fastapi