LoginSignup
1
0

More than 1 year has passed since last update.

FastAPI+React+DockerでQiitaみたいなサイトを作ってみたい -3日目-

Last updated at Posted at 2021-12-31

目次

3日目 - DBとModelの作成

DBの用意

APIの大枠は作成できたので、中身をこれから作っていく。
まずはdocker-compose.ymlにmysqlコンテナの設定を入れていく

docker-compose.yml
~
 db:
  image: mysql@5.7
  environment:
   - MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD
   - MYSQL_DATABASE=qiita_copy
  volumes:
   - ./mysql/data:/var/lib/mysql
   - ./mysql/my.cnf:/etc/mysql/conf.d/my.cnf
  ports:
   - 33306:3306

で、ローカルの環境変数にMYSQL_ROOT_PASSWORDを設定(zsh使いなので.zshrcに記述)

.zshrc
export MYSQL_ROOT_PASSWORD="ルートのパスワード"

mysqlはデフォルトでは日本語入力に対応していないので./mysqlフォルダを作って、その下にmy.cnfを作成

my.cnf
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_bin

[mysql]
default-character-set = utf8mb4

[client]
default-character-set = utf8mb4

でコンテナを立ち上げる

terminal
docker-compose up

立ち上がったら中に入って

terminal
docker-compose exec db bash

設定したルート用のパスワードでログインできればOK

bash
mysql -uroot -h db -p qiita_copy

DB接続設定

一旦ローカルからbackendの中に入る

terminal
docker-compose exec backend bash

sqlalchemyとMySQLドライバーのPyMySQLをインストールする

bash
poetry add sqlalchemy pymysql

インストールし終わったらローカルに戻ってDB接続用のファイルを作成

backend/api/v1/db.py
import os
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

USER_NAME = 'root'
USER      = 'root'
PASSWORD  = os.environ["MYSQL_ROOT_PASSWORD"]
HOST      = 'db'
PORT      = 3306
DATABASE  = 'qiita_copy'

SQLALCHEMY_DATABASE_URL = "mysql+pymysql://{0}:{1}@{2}:{3}/{4}?charset=utf8".format(
  USER,PASSWORD,HOST,PORT,DATABASE
)

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

Base = declarative_base()

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

これでget_dbのメソッドを利用してDBに接続できるようになる。

Modelの作成

DBに接続できるようになったので早速Modelを作っていく
今回はUser、PostとLgtmModelを作る

UserModel

api/v1/models/user.py
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import relationship
from api.v1.db import Base
import api.v1.models.post # リレーション貼る時に呼び出し順によってはentityが見つけられず、うまく動かなくなるので

class User(Base):
  __tablename__ = "users"

  id = Column(Integer, primary_key=True)
  login_id = Column(String(255), unique=True, nullable=False)
  password_hash = Column(String(255), nullable=False)
  name = Column(String(255), nullable=False)
  description = Column(String(255))

  posts = relationship('api.v1.models.post.Post',back_populates='user')

PostModel

api/v1/models/post.py
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.dialects.mysql import LONGTEXT
from sqlalchemy.orm import relationship
from api.v1.db import Base
import api.v1.models.user # リレーション貼る時に呼び出し順によってはentityが見つけられず、うまく動かなくなるので
import api.v1.models.lgtm # リレーション貼る時に呼び出し順によってはentityが見つけられず、うまく動かなくなるので

class Post(Base):
  __tablename__ = "posts"

  id = Column(Integer, primary_key=True)
  title = Column(String(255), nullable=False)
  context = Column(LONGTEXT)
  user_id = Column(Integer, ForeignKey("users.id"), nullable=False)#ForeignKeyのついたカラムのオプションはForeginKeyより後ろに書かなければならない

  user = relationship('api.v1.models.user.User',back_populates="posts") #schemaの設定と同じ命名にしておく
  lgtms  = relationship('api.v1.models.lgtm.Lgtm',back_populates='post')

LgtmModel

api/v1/models/lgtm.py
from sqlalchemy import Column, Integer, ForeignKey
from sqlalchemy.orm import relationship
from api.v1.db import Base
import api.v1.models.post # リレーション貼る時に呼び出し順によってはentityが見つけられず、うまく動かなくなるので

class Lgtm(Base):
  __tablename__ = "lgtms"

  id = Column(Integer, primary_key=True)
  user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
  post_id = Column(Integer, ForeignKey("posts.id"), nullable=False)

  post = relationship('api.v1.models.post.Post',back_populates='lgtms')

テーブルのマイグレーション

Modelを作成したので、このモデルを元にマイグレーションを作成する。
マイグレーションの管理にはalembicを使うので、いつも通り中に入ってインストールする

terminal
docker-compose exec backend bash
bash
poetry add alembic

インストールが完了したら、イニシャライズする。

bash
poetry run alembic init migration # migration は可変

イニシャライズで作成されたalembic.iniとenv.pyを編集する

alembic.ini
# ~~
sqlalchemy.url = mysql+pymysql://root:%(MYSQL_ROOT_PASSWORD)s@db/qiita_copy
# ~~
migration/env.py
# ~~~
import os #追加
from api.v1.db import Base #追加
from api.v1.models.user import User #追加
from api.v1.models.post import Post #追加
from api.v1.models.lgtm import Lgtm #追加

# ~~~
def run_migrations_online():
    config.set_section_option("alembic", "MYSQL_ROOT_PASSWORD", os.environ.get("MYSQL_ROOT_PASSWORD"))
    url = config.get_main_option("sqlalchemy.url")

    connectable = engine_from_config(
        config.get_section(config.config_ini_section),
        prefix="sqlalchemy.",
        poolclass=pool.NullPool,
    )

    with connectable.connect() as connection:
        context.configure(
            url=url,
            connection=connection,
            target_metadata=target_metadata
        )

        with context.begin_transaction():
            context.run_migrations()
# ~~~

これでOK

マイグレーション

まずはマイグレーレーションファイルを作成

bash
poetry run alembic revision --autogenerate -m {ファイル名}

作成したマイグレーションを実行

bash
poetry run alembic upgrade head

これでMYSQLにそれぞれのテーブルが作成される。

※マイグレーションの巻き戻し

poetry run alembic downgrade base

おかしくなっちゃった時は

マイグレーションのバージョン管理は設定したDatabaseのalembic_versionテーブルで保存されている。
なのでdbコンテナの中に入って、alembicテーブルのレコードを削除する。

terminal
docker-compose exec db bash
bash
mysql -uroot -h db -p qiita_copy
mysql-cli
delete from alembic_version;

で、migration/versionsに保存されているマイグレーションファイルを全削除すれば初期状態に戻る。

これでDBとの接続からDBに設定を流し込むところまでできた。
あとはCrudを作って割り当てればAPI側の設定はOKかな。

1
0
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
0