LoginSignup
1
1

More than 1 year has passed since last update.

初心者が、docker上で、Fastapi経由でpostgresにデータの追加、更新、削除を行ってみた。

Posted at

目次

  1. はじめに
  2. やること
  3. 前提条件
  4. 必要なフレームワーク
  5. 各種概念説明
  6. 構成
  7. 手順
  8. 確認
  9. まとめ
  10. 参考

1. はじめに

内容・目的

初心者が、docker上でpostgresqlに対してfastapi経由でデータの登録等ができるようにやったことをまとめた。
サクッと、dockerでpostgresとfastapiを動かしてみたいと初心者が思ったときに、
完全に合致する内容で書いてある記事があまりなかったので、過去の自分を助けるという意味合いも込めて書いた。

想定している対象

pythonをつかってwebアプリを作成してみたいけど、何から手をつければよいかわからない人向け

想定読了時間

読むだけなら10分程度
コードを動かしてみるなら30分程度

2.やること

ローカル環境上でFastAPI経由でデータベースにデータを登録、更新、削除する。

要件・仕様

取得

idを指定し、データベースからユーザー情報を取得する
ユーザーはidを入力

  • 取得が成功した場合 返り値:200_ok,登録情報
  • 取得が失敗した場合 返り値:400_error,"user_mail is not found"

登録時

データベースにユーザーを登録する。
ユーザーは、メールアドレス、PW、名前を入力。

  • 登録が成功した場合 返り値:200_ok,登録情報
  • 登録が失敗した場合 返り値:500_error,"user_mail is wrong"

削除時

メールアドレスおよびPWが入力し、正しい場合、ユーザーデータを削除する。

  • 削除が成功した場合、返り値:200_ok,削除した情報
  • 削除が失敗した場合、返り値:404_error ,"not match"

を返す

更新時

メールアドレスおよびPWを入力し、正しい場合、ユーザーデータのPWを更新できる

  • 更新が成功した場合 返り値:200_ok,更新した情報
  • 更新が失敗した場合 返り値:404_error,"user_mail is wrong" 

を返す

3.前提条件

dockerを利用する環境構築完了していること。

実行環境について

docker desktop for windowsで実行。

4.言語や必要なフレームワーク

今回利用するもの一覧は下記の通り。

  • Python
  • docker
  • FastAPI
  • Postgresql
  • SQLAlchemy

選定理由

Python

Qiita でもタグランキングで上位に位置し、初心者向きのため。

Docker

コンテナツールの中でデファクトスタンダードのため。

FastAPi

ドキュメントがしっかりしているため。

Postgresql

過去に触ったことがあったため。
~~ MySQLでもやってみます、いつか~~

SQLAlchemy

SQLツールキットのデファクトスタンダードであるため。

5.各種キーワード説明

docker

コンテナとは、ある環境から別の環境へと移動した際にアプリケーションを迅速かつ確実に実行できるように、コードとその依存関係をすべてパッケージ化したソフトウェアの標準単位である。Dockerkコンテナイメージは、軽量、スタンドアローンな実行可能なソフトウェアパッケージである。

引用:docker(https://www.docker.com/resources/what-container/)

Dockerは仮想化技術の一つで、コンテナ型に分類される。
仮想化技術は、ホスト型、ハイパーバイザー型、コンテナ型の三種類ある。

引用:(https://www.itmanage.co.jp/column/virtualization-server-integration/)

Dockerが属するコンテナ型は、ホスト型、ハイパーバイザー型よりも構築、破棄が容易なので、コンテナ型を採用。

FastAPI

pythonでAPIを構築するためのWebフレームワーク。

Postgresql

オープンソースのリレーショナルデータベース管理システム(RDBS)の一つ。

リレーショナルデータベース管理システム(RDBS)

データベースは、階層型、ネットワーク型、リレーショナル型の三つに分類される。
リレーショナルデータベースは列と行で構成されているもの。
イメージとしては、エクセルが近い。

SQLAlchemy

SQLAlchemyはpython SQKのツールキットで、Object Relational Mapperである。
要するに、postgresqlのデータベースを操作する(selectやdelete)のをpythonで実行できるようにしたツール

  • Object Relational Mapperについて
    Object Relational Mapper/Object Relational Mapping(ORM)はデータベースとオブジェクト指向プログラミング言語の間の非互換なデータを変換するプログラミング技法。

6.構成

構成図

ローカルマシンにdockerコンテナを作成する。
作成するコンテナはdatabase用とwepapp用の二つ
構成は下記のようになる。
Untitled.png

データベース

Column value
user_id int
user_mail str
user_name str
user_password str

7.手順

ディレクトリ構成

下記のようにディレクトリを構成する

--app--
|    |-app
|    |   |-router
|    |        |-user.py
|    |   
|    |-crud.py
|    |-database.py
|    |-Dockerfile
|    |-main.py
|    |-models.py
|    |-requirements.txt
|    |-schemas.py
|
|-docker-compose.yml

コンテナの準備

Dockerfile

FROM python:3


WORKDIR /app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
RUN apt-get update && apt install -y tzdata && apt-get clean && rm -rf /var/lib/apt/lists/* 
ENV TZ Asia/Tokyo

COPY . .

docker-compose.yml

version: '3'
services:
  webapp_fastapi:
    build: 
      context: ./app ## docker build コマンドを実行した時のカレントワーキングディレクリをビルドコンテキストと呼ぶ。
      dockerfile: Dockerfile
    volumes:
      - .:/usr/src/app
      - /var/run/docker.sock:/var/run/docker.sock
    command: uvicorn main:app --reload --workers 1 --host 0.0.0.0 --port 8000
    ports:
      - 8000:8000
    depends_on:
      - postgres_fastapi

  postgres_fastapi:
    image: postgres:13.1-alpine
    environment:
      TZ: Asia/Tokyo
      POSTGRES_DB: postgres_test
      POSTGRES_USER: postgres_test
      POSTGRES_PASSWORD: postgres_sample
      PGDATA: /var/lib/postgresql/data/pgdata
    volumes:
      - ./app:/var/lib/postgresql/data
    ports:
      - 5432:5432

models.py

postgresqlのテーブルのカラムの設定は下記の通り

from sqlalchemy import String, Column, Integer
from sqlalchemy.ext.declarative import declarative_base
from database import Base
from uuid import uuid4

# データモデルの作成
# sqlalchemyによってデータベースのテーブルが生成

class User(Base):
    __tablename__ = 'user'
    user_id = Column(Integer, primary_key=True, autoincrement= True)  # intで連番で採番される
    user_mail = Column(String)
    user_name = Column(String)
    user_password = Column(String)

main.py

from typing import Optional

from fastapi import FastAPI
import models
import database
from sqlalchemy.orm import sessionmaker
from router import user


# DBコンテナとappコンテナが更新するために必要なもの

engine = database.engine
models.Base.metadata.create_all(engine)
DBsession = sessionmaker(bind=engine)
session = DBsession()

# パスオペレーションの設定
app = FastAPI()
app.include_router(user.router)

crud.py

from sqlalchemy.orm import Session
import models
from sqlalchemy.exc import DBAPIError
from fastapi.encoders import jsonable_encoder
from fastapi.responses import JSONResponse
from fastapi import HTTPException

# 概要
# dbに対して操作するコード

# user_idをキーにuserの情報を取得する
def get_user(session:Session, user_id:int) -> any:
    response =  session.query(models.User).filter(models.User.user_id == user_id).first()
    if response is None:
        raise HTTPException(status_code=404, detail="user_id is not found")

    return response

# userを新規作成すeる
def create_user(session, user_name:str, user_mail:str ,user_password: str) -> any:
    user_obj = models.User(
        user_mail =  user_mail,
        user_name =  user_name,
        user_password = user_password
    )
    try:
        session.add(user_obj)
    except:
        raise HTTPException(status_code=500, detail='error')
    session.commit()
    session.refresh(user_obj)
    return user_obj

    

# userのpasswordを更新する。
def update_password(session:Session, user_id:int, user_mail:str,user_password:str) -> any:
    response = get_user(session,user_id)
    # user_idがある場合のみの処理でよい。
    # user_idがない場合は、get_user関数でHTTPexceptionが返るので、この関数内では不要。
    if response:
        if response.user_mail == user_mail:
            response.user_password = user_password
            session.commit()
            # コミットしたresponseを取得するには、再度sessionを取得する必要がある。
            session.refresh(response)
            return response
        elif response.user_mail != user_mail:
            raise HTTPException(status_code=404, detail='user_mail is wrong')


# userを削除する
def delete_user(session:Session, user_id:int,user_mail:str, user_password:str):
    # user_idがデータベースに存在しているかを確認する
    response = get_user(session, user_id)
    # user_idがデータベースに存在している場合
    if response:
        # idのuserのデータが空ではなく、かつ、malとpasswordともに合致しているか
        if response.user_mail == user_mail and response.user_password == user_password:
            session.delete(response)
            session.commit()

            return response
        else:
            raise HTTPException(status_code=404, detail="not match")

user.py

from fastapi import APIRouter,Depends, HTTPException
from database import SessionLocal, engine
import models,crud
import schemas
from sqlalchemy.orm import Session
import typing

# 概要
# APIの受け口

# 依存関係を作成する
# リクエスト毎に独室したデータベースセッション(データベースへの接続)を持つ必要がある。
models.Base.metadata.create_all(bind = engine)

router = APIRouter()

def get_db():
    db = SessionLocal()
    try:
        yield db # returnではなくyieldがを記載することで繰り返し利用することが可能。
    finally:
        db.close

# ユーザーを取得する
@router.get("/user/get")
def read_user(user_id:int, db:Session=Depends(get_db)) -> any:
    return crud.get_user(db, user_id)

    # idをキーに取得したデータを返す

# ユーザーを新規作成
@router.post("/user/create")
def create_user(user_name:str,user_mail:str, user_password:str, db:Session = Depends(get_db)):
    return  crud.create_user(db, user_name,user_mail, user_password)


# passwordを更新する。
@router.put("/user/update")
def update_password(user_id:int, user_mail:str, user_password:str, db:Session = Depends(get_db) ):
    return crud.update_password(db,user_id, user_mail,user_password)
    
# ユーザーを削除する
@router.delete("/user/delete")
def delete_user(user_id:int, user_mail:str, user_password:str, db:Session = Depends(get_db)):
    return crud.delete_user(db, user_id,user_mail, user_password)

8. 確認

作成

http:127.0.0.1/docsから下記内容でuserを作成する。

項目 内容
name nobunaga_oda
mail nobunaga.oda@mail
password owari

キャプチャの通り、crud.py上で記載したレスポンスが返ってきている。
image.png

取得

成功する場合

idを引き数に作成したユーザー情報が出力されるかを確認する。
先ほど作成したidで実行し、ユーザー情報が返されている。

image.png

失敗する場合

データベースに登録されていないユーザーidを入力する。
[2_user_data]

image.png

下記の通り、レスポンスが返ってくる。
response bodyには。HHTPExcetptionのdetailが返ってくる

image.png

更新

passwordの更新を行う。

失敗する場合

  • 間違ったmailを入力し、"user_mai wrong"が返ってくるかを確認

image.png

  • 間違ったidを入力し、"user_id is not found"が返ってくるかを確認

image.png

成功する場合

正しい情報を入力し、PWが更新されるかを確認

image.png

削除

別途ユーザーを作成

image.png

削除するユーザー情報の確認

image.png

失敗する場合

  • user_idがない場合

image.png

  • mailが間違っている場合

image.png

  • passwordが間違っている場合

image.png

成功する場合

  • 削除成功のレスポンス

image.png

  • 削除したuser_idでuser情報の取得。削除されているので見つからない。

image.png

まとめ

main.pyでfastapiの実行ファイルをルーティングしている。
今回は、routerディレクトリ配下のuser.pyのみ。
user.pyは、http://127.0.0.1/docsで表示される。

image.png

fastapiのuser.pyをうけて、crud.pyを実行する。
今回は、ユーザーのデータベースのみで、crud.pyからデータベースに対してアクセスし、削除、更新、作成を行う。

参考リンク

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1