FastAPIとPostgreSQLでモデル定義とMigrationを行う方法

本記事はPython初学者やFastAPIを学習したいと考えているWebエンジニア向けの入門者向けの記事になります。

この記事はFastAPIとPostgreSQLでモデル定義とMigrationを行う方法です。

Pythonのインストールから実施しますので安心して記事を進めてみてください。

Pythonの高速APIのFastAPIを利用するシーンが多くなってきました。

案件でも使用する機会が増えてきたのでノウハウと公開したいと思います。

すこし前ですとPythonでWebアプリを作成するのはDjango一択でした。

ですが最近ではJavaScriptのフレームワーク(特にReact)+FastAPIで開発することが増えています。

JavaScriptフレームワーク+APIの構成で優れていると感じることはネイティブアプリでもコードや環境を流用することができる点です。

性能的に遅いと言われるPythonで文字通り高速なFastAPIを学習していただければと幸いです。

目次

pydanticとalembicの解説

pydanticとは?

pydanticとは、実行時における型ヒントを提供したり、データのバリデーション時のエラー設定を提供してくれるためのライブラリです。

FastAPIではSQLAlchemyを使用しデータベースのデータを操作する前に、モデルを定義する必要があり、モデル定義後バリデーションにpydanticを使用します。

参考:pydantic

alembicとは?

alembicとはSQLAlchemyのマイグレーションツールになります。

FastAPI以外でもSQLAlchemyを使用する際にはよく使用しますのでぜひこの機会に触ってみましょう。

参考:alembic

ファイル構成とディレクトリ構成の確認

前章のFastAPIでPostgresに接続する方法後、フォルダは以下の様になっています。

fastapi/
 ├ venv/
 └ src/ 
   ├ database.py
   ├ main.py
   ├ models.py
   └ schemas.py

この章で作成するファイルは以下になります。

新規作成するファイル
  • src/models.py
  • src/schemas.py

Ubuntuの場合は「x86_64-linux-gnu-gcc」がないとエラーが出るのでライブラリ「python3-dev」をインストールします。

apt-get install build-essential libssl-dev libffi-dev python3-dev

必要なモジュールをインストールします。

cd fastapi
source venv/bin/activate
pip install psycopg2-binary
# pip install psycopg2
pip install pydantic
pip install alembic

まずはsrc/models.pyを作成しましょう。

from database import Base
import sqlalchemy as sa


class User(Base):
    __tablename__ = "users"
    user_id = sa.Column("user_id",
                        sa.Integer,
                        primary_key=True,
                        autoincrement=True)
    name = sa.Column("name",
                     sa.Text,
                     nullable=False)
    age = sa.Column("age",
                    sa.Integer,
                    nullable=False)
    email = sa.Column("email",
                      sa.Text,
                      nullable=False)

次にsrc/schemas.pyを作成します。

from pydantic import BaseModel


class UserBase(BaseModel):
    name: str
    age: int
    email: str

    class Config:
        orm_mode = True

alembicでマイグレーションの準備

alembicの初期化

まずはalembicに必要なファイルをinitializeします。

cd src/
alembic init alembic

initializeが完了するとalembicのファイル群が作成されます。

fastapi/
 ├ venv/
 └ src/ 
   ├ alembic
   │  ├ env.py
   │  ├ README
   │  ├ script.py.mako
   │  └ versions/
   ├ alembic.ini
   ├ database.py
   ├ main.py
   ├ models.py
   └ schemas.py

alembicの設定

次にalembicの設定を行います。

今回postgresのurlが動的に利用できるようにalembic.iniを使用しない構成をとります。

alembic.iniのURLをコメントアウトします。

# コメントアウト
# sqlalchemy.url = driver://user:pass@localhost/dbname

次にalembic/env.pyを修正します。

from logging.config import fileConfig

from sqlalchemy import engine_from_config
from sqlalchemy import pool

from alembic import context

from database import Base, SQLALCHEMY_DATABASE_URL # 追記
import models # 追記
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config

# Interpret the config file for Python logging.
# This line sets up loggers basically.
fileConfig(config.config_file_name)

# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
# target_metadata = None # 修正
target_metadata = models.Base.metadata # 追記

# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.


def run_migrations_offline():
    """Run migrations in 'offline' mode.

    This configures the context with just a URL
    and not an Engine, though an Engine is acceptable
    here as well.  By skipping the Engine creation
    we don't even need a DBAPI to be available.

    Calls to context.execute() here emit the given string to the
    script output.

    """
    # コメントアウト
    #url = config.get_main_option("sqlalchemy.url")
    url = SQLALCHEMY_DATABASE_URL # 追記
    context.configure(
        url=url,
        target_metadata=target_metadata,
        literal_binds=True,
        dialect_opts={"paramstyle": "named"},
    )

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


def run_migrations_online():
    """Run migrations in 'online' mode.

    In this scenario we need to create an Engine
    and associate a connection with the context.

    """
    configuration = config.get_section(config.config_ini_section)
    configuration["sqlalchemy.url"] = SQLALCHEMY_DATABASE_URL
    connectable = engine_from_config(
        configuration,
        prefix="sqlalchemy.",
        poolclass=pool.NullPool,
    )
    # コメントアウト
    # connectable = engine_from_config(
    #     config.get_section(config.config_ini_section),
    #     prefix="sqlalchemy.",
    #     poolclass=pool.NullPool,
    # )

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

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


if context.is_offline_mode():
    run_migrations_offline()
else:
    run_migrations_online()

これでalembicの準備は完了です。

alembicでマイグレーション(Migration)実行

ではここからマイグレーションを実行していきましょう。

alembiceでのマイグレーションに必要なファイルの作成コマンドは以下になります。

alembic revision --autogenerate -m "create users table"

それでは実行してみましょう。

以下のように実行されればOKです。

# alembic revision -m "create user table"
INFO  [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO  [alembic.runtime.migration] Will assume transactional DDL.
INFO  [alembic.autogenerate.compare] Detected added table 'users'
  Generating /root/fastapi/src/alembic/versions/1f3b2391d90c_create_users_table.py ...  done

次にsrc/alembic/versions/xxxxxxxx_create_user_table.pyが作成されているはずです。

作成されたpythonファイルを確認してみましょう。

"""create users table

Revision ID: 1f3b2391d90c
Revises:
Create Date: 2022-03-24 12:14:06.340797

"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = '1f3b2391d90c'
down_revision = None
branch_labels = None
depends_on = None


def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table('users',
    sa.Column('user_id', sa.Integer(), autoincrement=True, nullable=False),
    sa.Column('name', sa.Text(), nullable=False),
    sa.Column('age', sa.Integer(), nullable=False),
    sa.Column('email', sa.Text(), nullable=False),
    sa.PrimaryKeyConstraint('user_id')
    )
    # ### end Alembic commands ###


def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_table('users')
    # ### end Alembic commands ###

def upgrade()にusersテーブルを作成するためのコードが出力されています。

これでマイグレーションファイルの作成は完了です。

実際にマイグレーションを実行してみましょう。以下が実行コマンドになります。

alembic upgrade head

以下のように出力されているはずです。

# alembic upgrade head
INFO  [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO  [alembic.runtime.migration] Will assume transactional DDL.
INFO  [alembic.runtime.migration] Running upgrade  -> 1f3b2391d90c, create tables

これでマイグレーションが完了して、テーブルが作成されました。

作成されたテーブルの確認

最後に本当にテーブルが作成されたか確認してみましょう。

postgresユーザにスイッチしてsqlコマンドでDBに接続してみます。

su - postgres
psql -U fastapi -h 127.0.0.1 -p 5432 fastapi

接続出来たら \dt でテーブルが作成されていることを確認しましょう。

su - postgres
psql -U fastapi -h 127.0.0.1 -p 5432 fastapi
Password for user fastapi:
psql (14.2 (Ubuntu 14.2-1.pgdg20.04+1))
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
Type "help" for help.

fastapi=> \dt
             List of relations
 Schema |      Name       | Type  |  Owner
--------+-----------------+-------+---------
 public | alembic_version | table | fastapi
 public | users           | table | fastapi

上記のようにusersテーブルが作成されていることを確認できました。

前回からの記事と本記事でデータベースの接続とマイグレーションが完了しました。

次は本格的にデータを参照してみたいと思います。

次の記事はこちら:FastAPI+PostgreSQLでデータ参照。SELECT文を実行する方法

前の記事はこちらFastAPIでPostgreSQLに接続する方法

よかったらシェアしてね!
  • URLをコピーしました!
目次