skydum

個人的な作業記録とか備忘録代わりのメモ

FastAPI + SQLAlchemyでscoped_sessionを使うとエラーが発生する

FastAPI + SQLAlchemyでscoped_sessionを使うとエラーが発生する

  • 発生した環境は以下
    • Python 3.8.10
    • SQLAlchemy 2.0.18
    • fastapi 0.99.1

エラー内容

FastAPIを使っていてSQLAlchemyでDBにアクセスを連続で行ったときに以下のようなエラーが発生することがある。

  File "./fastapi-session-test/.venv/lib/python3.8/site-packages/sqlalchemy/orm/session.py", line 926, in _raise_for_prerequisite_state
    raise sa_exc.InvalidRequestError(
sqlalchemy.exc.InvalidRequestError: This session is provisioning a new connection; concurrent operations are not permitted (Background on this error at: https://sqlalche.me/e/20/isce)

エラー内容からするとSQLAlchemyのSession周りのエラーで同時に複数スレッドからSessionを共有している場合に発生するらしい。 SQLAlchemyはThreadLocal変数を利用してる(Thread-Local Scope)ので複数スレッドから同時にSessionを作っても別々の変数が割当たるので問題がないと思っていたが、どうも違うのかもしれない…。

今回エラーが発生するソースコードではscoped_sessionを使うとエラーが発生するが、socped_sessionを使わない場合はエラーが発生しない。 どうやったらこのエラーの発生する原因を突き止められるかなと考えたが、すぐには思いつかなかった。 今のところはscoped_sessionを使わない方法でエラーを回避する。

ソースコードから抜粋

DB_FILENAME = "sql_app.db"

DATABASE_URL = f"sqlite:///./{DB_FILENAME}"
engine = create_engine(DATABASE_URL, echo=False)
SessionLocal = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=engine))
# SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

検証に利用したテスト用のソースコード

uvicorn main:appで起動して/user/[id]に同時にアクセスすると時々エラーが発生する。

import logging

from fastapi import Depends, FastAPI, Request
from sqlalchemy import create_engine, insert
from sqlalchemy.orm import DeclarativeBase, Mapped, Session, mapped_column, scoped_session, sessionmaker
from sqlalchemy.sql.sqltypes import Integer, String
from starlette.exceptions import HTTPException as StarletteHTTPException

DB_FILENAME = "sql_app.db"

DATABASE_URL = f"sqlite:///./{DB_FILENAME}"
engine = create_engine(DATABASE_URL, echo=False)
SessionLocal = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=engine))
# SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)


logger = logging.getLogger("uvicorn.error")
logger.setLevel(logging.DEBUG)
fh = logging.FileHandler("sql_app.log")
fh.setLevel(logging.DEBUG)
formatter = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
fh.setFormatter(logging.Formatter(formatter))
logger.addHandler(fh)


class Base(DeclarativeBase):
    pass


class User(Base):
    __tablename__ = "users"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
    name: Mapped[str] = mapped_column(String)
    email: Mapped[str] = mapped_column(String)
    password: Mapped[str] = mapped_column(String)


Base.metadata.create_all(bind=engine)


def insert_test_data(max_data=100000000):
    print("start test data insert")
    session = SessionLocal()

    print("create insert data")
    add_data = [dict(name="XXXXXXXXXX", email="XXXXX@exmaple.com", password="123456") for _ in range(max_data)]
    print("created insert data")

    session.execute(insert(User), add_data)
    session.commit()
    session.close()
    print("data inserted!")


if not DB_FILENAME:
    insert_test_data(1000000)


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


app = FastAPI()


@app.middleware("http")
async def exception_handling_middleware(request: Request, call_next):
    try:
        return await call_next(request)
    except StarletteHTTPException as e:
        logger.error(f"url: {request.url}, error: {e}")
        logger.error("", exc_info=True, stack_info=True)
        raise e


@app.get("/")
def api_status():
    return {"status": "ok"}


@app.get("/users")
def get_usere(db: Session = Depends(get_db)):
    print(f"users: {id(db)}")

    return db.query(User).all()


@app.get("/user/{user_id}")
def get_user(request: Request, user_id: int, db: Session = Depends(get_db)):
    logger.debug(f"url: {request.url}, user: {id(db)}")

    return db.query(User).filter(User.id == user_id).first()

Flaskでファイルを使った排他処理

Flaskでファイルを使った排他処理

どうしてもFlask(マルチプロセス)で動作しているプログラムで排他処理をしたくなった。(必要に迫られた)

Linuxなら以下を利用すると良いと思うけれども、Windows環境なので利用できず。

Windowsでファイルを使った履いた処理ってどうするのかな?と思って調べたので、忘れないように記載しておく。

msvcrt

Windowsの場合は以下のモジュールを利用すれば良いらしい

msvcrt.locking

以下のmsvcrt.lockingを使うことによってファイルのロックの取得とリリースを行うことができる。
オプションがいくつかあって使わないと忘れてしまいそう。

msvcrt.locking(fd, mode, nbytes)

使い方

事前に排他処理を行うために利用するファイルを作成しておく。 その上で

LOCKFILE = open("lockfile.lock", "bw")

msvcrt.locking(LOCKFILE.fileno(), msvcrt.LK_NBLCK, 1)

の様にするとファイルのロックを取得できる。

第1引数

ファイルディスクリプタ(fd):ファイルの番号を指定する

第2引数に指定できるオプション

読み込み、書き込みともにロックを掛ける
mode 機能説明
msvcrt.LK_LOCK 指定されたnbytesの範囲にロックを掛ける
ロックが取得できるまで1秒間隔で10回リトライを行う
10回リトライを行ってもロックが取得できない場合OSErrorが返る
msvcrt.LK_NBLCK 指定されたnbytesの範囲にロックを掛ける
ロックが取得できない場合即時にOSErrorが返る
ファイルの読み込みはできるが、書き込みができない様にロックを掛ける

今回は利用しないので検証していません

mode 機能説明
msvcrt.LK_NBRLCK 指定されたnbytesの範囲にロックを掛ける
ロックが取得できるまで1秒間隔で10回リトライを行う
10回リトライを行ってもロックが取得できない場合OSErrorが返る
msvcrt.LK_RLCK 指定されたnbytesの範囲にロックを掛ける | ロックを掛けた範囲を読み込むことはできるが書き込むことはできない

第3引数

ファイルにロックを書ける範囲をバイト数で指定する

サンプルコード

以下のコードでは、2つのエンドポイント(/lockと/unlock)を作成し、ファイルのロックを取得、開放するサンプルです

  • /lock:ファイルのロックを取得する
  • /unlock:ファイルのロックを開放する

機能

  1. /lockにアクセスするとファイルのロックを取得することができ、再度/lockにアクセスするとロックに失敗することが確認できます
  2. /unlockにアクセスした後/lockにアクセスすると/lockが再度取得できることが確認できます
  3. /lockを取得した後、再度/lockを取得すると失敗しますが、ロックの取得は内部的に1秒間隔で10回挑戦を行いますので、挑戦を行っている間に/unlockにアクセスすると問題なくロックが取得できることが確認できます
import msvcrt
import time

from flask import Flask, jsonify

app = Flask(__name__)

# ロックファイルを開く
LOCKFILE = open("lockfile.lock", "bw")

# ロックリトライの最大回数とインターバル
LOCK_RETRY_MAX = 10
LOCK_INTERVAL = 1

# ロックを取得するためのデコレータ
def retry(func):
    def wrapper(*args, **kwargs):
        for _ in range(LOCK_RETRY_MAX):
            res = func(*args, **kwargs)

            if res is True:
                return res

            # ロックが取得できなかった場合、インターバルを待って再試行する
            time.sleep(LOCK_INTERVAL)

        return False

    return wrapper

# ロックを取得する関数
@retry
def acquire_lock() -> bool:
    try:
        msvcrt.locking(LOCKFILE.fileno(), msvcrt.LK_NBLCK, 1)  # ノンブロッキングロックを取得
        return True
    except OSError:
        return False

# ロックを解放する関数
def release_lock() -> bool:
    try:
        msvcrt.locking(LOCKFILE.fileno(), msvcrt.LK_UNLCK, 1)  # ロックを解放
        return True
    except OSError:
        return False

# ロックを取得するエンドポイント
@app.route("/lock")
def acquire_lock_test():
    res = acquire_lock()

    if res is True:
        return jsonify({"status": "locked"})
    else:
        return jsonify({"status": "lock failed"})

# ロックを解放するエンドポイント
@app.route("/unlock")
def release_lock_test():
    res = release_lock()

    if res is True:
        return jsonify({"status": "unlocked"})
    else:
        return jsonify({"status": "unlock failed"})

# アプリケーションの実行
if __name__ == "__main__":
    app.run()

Vue3 + Vuetify3の環境構築

Vue3 + Vuetify3

Vue2が2023/12/31でEOLを迎えるのでそろそろVue3も勉強しないとなと思いつつも放置していたが、そろそろ触らないといけない機会が増えてきそう。 Vuetify3も正式リリースされているので、環境をまずは構築する。

環境の構築

Node.jsのインストール

インストールはどんどんNextで進めてOK。 以下の画面が出たらチェックを付けずにNextを押して進める。

チェックを入れた場合は「Chocolatey」を言うものがインストールされる。
利用したことがないので詳しく分からないが、コマンドラインからアプリケーションの追加削除ができて、開発環境の構築が楽にできるらしい。

Node.jsのインストールが完了したら、コマンドプロンプトを起動してNode.jsが起動するか確認する。 インストールしたNode.jsのバージョンが表示されたらOK。

Vuetify3の環境を構築する

コマンドプロンプトを開いて開発を始めたいフォルダに移動します。

途中選択肢が出てきますので、作成したいプロジェクトに合わせて選択してください。 今回は以下を選択しました。

  • Project name: >> vuetify-project
  • Which preset would you like to install? >> Default (Vuetify)
  • Use TypeScript? » No / Yes >> No
  • Would you like to install dependencies with yarn, npm, or pnpm? >> yarn

プロジェクトの作成が完了すると現在のフォルダに先程指定したプロジェクト名のフォルダができています。

プロジェクトを実行して画面を表示してみる

コマンドプロンプトを開いて先ほど作成したプロジェクトのフォルダへ移動してyarn runを実行しプロジェクトで作成された初期状態で起動してみる。

内臓のwebサーバが起動してhttp://localhost:3000/でアクセスできる状態になっているのでブラウザにアドレスを入力する

アドレスを入力すると以下の画面が表示される(右の方にvuetifyの表示が寄っているのは仕様なのか…)

ソースコードを確認すると見たことがないv-responsiveというのがあり、これが原因っぽい。

デベロッパーツールで確認してみると確かに原因のようだが、ドキュメントを見てもよくわからない。
Aspect ratios
https://vuetifyjs.com/en/components/aspect-ratios/#aspect-ratios:embed

環境の構築はできたので簡単なプログラム書いて勉強をしてみる。

vscodeデバッグする

vscodeでctrl + shift + dを押してデバッグメニューを出して歯車を押してlunch.jsonができたら、以下の内容を設定する。
index.htmlファイルにはブレイクポイントをつけられないが、*.vueファイルにはブレイクポイントを設定することができる。

{
    // IntelliSense を使用して利用可能な属性を学べます。
    // 既存の属性の説明をホバーして表示します。
    // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "chrome",
            "request": "launch",
            "name": "localhost に対して Chrome を起動する",
            "url": "http://localhost:3000",
            "webRoot": "${workspaceFolder}",
            "sourceMaps": true,
        }
    ]
}

デバッグのやり方

  1. ターミナルを開いてyarn run devを入力してサーバーを起動する
    起動した時に出てきたlocal: http://localhost:3000/とlunch.jsonのurlの記載が同一であることを確認(異なる場合はlunch.jsonのurlを変更する)
  2. 任意の場所にブレイクポイントを設定する
  3. ctrl + shift + dでデバッグメニューを開いて「localhost に対して Chrome を起動する」が選択されていることを確認してF5でデバッグを実行
  4. ブラウザが起動するのでブレイクポイントを設定した部分のコードが実行されるとブレイクポイントが有効になりデバッグができる

Ubuntu 18.04 LTSのサポート終了が終了するので20.04へアップグレードする

Ubuntu 18.04 LTSのサポート終了

最近忙しくて気づいていなかったがUbuntu 18.04 LTSのサポートが2023/05/31で終わるらしい。

2023年5月31日のUbuntu 18.04 LTS標準サポート終了に備えて
https://jp.ubuntu.com/blog/18-04-end-of-standard-support-jp:title

CentOSはOSのバージョンアップを実行したことがあるがUbuntuはOSのバージョンアップを行ったことがないので上げてみた。

Ubuntu 18.04.6 LTS (Bionic Beaver)

OSのアップデートの手順を確認

Ubuntuのアップデートを行うためには以下の手順が必要

  1. 現行のバージョンで適用できるアップデートをすべて行う
  2. do-release-upgradeを実行してOS自体のアップグレードを行う
  3. 以上

と簡単らしい。

OSのバージョンアップを実行

  • 現在のOSのバージョンを念のため確認
user@Ubuntu18:~$ cat /etc/os-release
NAME="Ubuntu"
VERSION="18.04.6 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.6 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic
user@Ubuntu18:~$
  • テスト環境なのでrootで作業する
user@Ubuntu18:~$ sudo su -
[sudo] user のパスワード:
root@Ubuntu18:~#
  • パッケージ情報をアップデートする
root@Ubuntu18:~# apt-get update
ヒット:1 http://jp.archive.ubuntu.com/ubuntu bionic InRelease
取得:2 http://security.ubuntu.com/ubuntu bionic-security InRelease [88.7 kB]
取得:3 http://jp.archive.ubuntu.com/ubuntu bionic-updates InRelease [88.7 kB]
取得:4 http://jp.archive.ubuntu.com/ubuntu bionic-backports InRelease [83.3 kB]
261 kB を 1秒 で取得しました (180 kB/s)
パッケージリストを読み込んでいます... 完了
  • バッケージをアップグレードする
root@Ubuntu18:~# apt-get upgrade -y

初期状態からだと300個ぐらいパッケージがアップグレードされた
  • OSのアップグレードをするコマンドの確認
root@Ubuntu18:~# do-release-upgrade --help
Usage: do-release-upgrade [options]

Options:
  -h, --help            show this help message and exit
  -V, --version         バージョンを表示して終了
  -d, --devel-release   サポートされている最新のリリースを使用している場合、開発版リリースにアップグレードする
  --data-dir=DATA_DIR   データファイルの含まれるディレクトリ
  -p, --proposed        アップグレードソフトウェアを使って $distro-proposed
                        から最新のリリースへのアップグレードを試す
  -m MODE, --mode=MODE  特別なアップグレードモードで実行する。 現在、デスクトップシステムの標準的なアップグレードを行う
                        'desktop' オプションと、サーバーシステム向けの 'server'
                        オプションがサポートされています。
  -f FRONTEND, --frontend=FRONTEND
                        特定のフロントエンドで実行
  -c, --check-dist-upgrade-only
                        新しいディストリビューション・リリースが利用可能かどうかチェックし、終了コードで結果を通知する
  --allow-third-party   Try the upgrade with third party mirrors and
                        repositories enabled instead of commenting them out.
  • OSのバージョンアップが可能かチェックする
root@Ubuntu18:~# do-release-upgrade -c
新しい Ubuntu のリリースをチェックしています
新しいリリース '20.04.6 LTS' が利用可能になっています。
'do-release-upgrade' を実行してアップグレードしてください
root@Ubuntu18:~#
  • OSを20.04.6 LTSへアップグレード可能らしいのでアップグレードを実行する
root@Ubuntu18:~# do-release-upgrade
新しい Ubuntu のリリースをチェックしています
Please install all available updates for your release before upgrading.
root@Ubuntu18:~#
  • コマンドを一考する前に利用可能なアップグレードをするように怒られた
root@Ubuntu18:~# apt-get update
ヒット:1 http://jp.archive.ubuntu.com/ubuntu bionic InRelease
取得:2 http://security.ubuntu.com/ubuntu bionic-security InRelease [88.7 kB]
取得:3 http://jp.archive.ubuntu.com/ubuntu bionic-updates InRelease [88.7 kB]
取得:4 http://jp.archive.ubuntu.com/ubuntu bionic-backports InRelease [83.3 kB]
261 kB を 2秒 で取得しました (146 kB/s)
パッケージリストを読み込んでいます... 完了
root@Ubuntu18:~# apt-get upgrade
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
アップグレードパッケージを検出しています... 完了
以下のパッケージが自動でインストールされましたが、もう必要とされていません:
  gir1.2-gst-plugins-base-1.0 gir1.2-gstreamer-1.0 gir1.2-gudev-1.0 gir1.2-udisks-2.0 grilo-plugins-0.3-base gstreamer1.0-gtk3 libcdr-0.1-1 libcolamd2 libdazzle-1.0-0
  libedataserverui-1.2-2 libevent-2.1-6 libexiv2-14 libfreerdp-client2-2 libfreerdp2-2 libgc1c2 libgee-0.8-2 libgexiv2-2 libgom-1.0-0 libgpod-common libgpod4
  liblirc-client0 liblua5.3-0 libmediaart-2.0-0 libmspub-0.1-1 libqqwing2v5 libraw16 libsgutils2-2 libssh-4 libsuitesparseconfig5 libvncclient1 libwinpr2-2
  libxapian30 lp-solve media-player-info python3-mako python3-markupsafe syslinux syslinux-common syslinux-legacy usb-creator-common
これを削除するには 'apt autoremove' を利用してください。
以下のパッケージは保留されます:
  linux-generic-hwe-18.04 linux-headers-generic-hwe-18.04 linux-image-generic-hwe-18.04 python3-software-properties software-properties-common software-properties-gtk
アップグレード: 0 個、新規インストール: 0 個、削除: 0 個、保留: 6 個。
root@Ubuntu18:~#
  • 不要なパッケージがあるのとアップグレードが保留されているので、要らないパッケージの削除から行う
root@Ubuntu18:~# apt autoremove -y
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
以下のパッケージは「削除」されます:
  gir1.2-gst-plugins-base-1.0 gir1.2-gstreamer-1.0 gir1.2-gudev-1.0 gir1.2-udisks-2.0 grilo-plugins-0.3-base gstreamer1.0-gtk3 libcdr-0.1-1 libcolamd2 libdazzle-1.0-0
  libedataserverui-1.2-2 libevent-2.1-6 libexiv2-14 libfreerdp-client2-2 libfreerdp2-2 libgc1c2 libgee-0.8-2 libgexiv2-2 libgom-1.0-0 libgpod-common libgpod4
  liblirc-client0 liblua5.3-0 libmediaart-2.0-0 libmspub-0.1-1 libqqwing2v5 libraw16 libsgutils2-2 libssh-4 libsuitesparseconfig5 libvncclient1 libwinpr2-2
  libxapian30 lp-solve media-player-info python3-mako python3-markupsafe syslinux syslinux-common syslinux-legacy usb-creator-common
アップグレード: 0 個、新規インストール: 0 個、削除: 40 個、保留: 6 個。
この操作後に 27.0 MB のディスク容量が解放されます。
(データベースを読み込んでいます ... 現在 123840 個のファイルとディレクトリがインストールされています。)

たくさん削除された
  • 念のためもう一回パッケージのアップグレードを実行しておく
root@Ubuntu18:~# apt-get update
ヒット:1 http://jp.archive.ubuntu.com/ubuntu bionic InRelease
取得:2 http://jp.archive.ubuntu.com/ubuntu bionic-updates InRelease [88.7 kB]
取得:3 http://security.ubuntu.com/ubuntu bionic-security InRelease [88.7 kB]
取得:4 http://jp.archive.ubuntu.com/ubuntu bionic-backports InRelease [83.3 kB]
261 kB を 2秒 で取得しました (163 kB/s)
パッケージリストを読み込んでいます... 完了
root@Ubuntu18:~# apt-get upgrade
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
アップグレードパッケージを検出しています... 完了
以下のパッケージは保留されます:
  linux-generic-hwe-18.04 linux-headers-generic-hwe-18.04 linux-image-generic-hwe-18.04 python3-software-properties software-properties-common software-properties-gtk
アップグレード: 0 個、新規インストール: 0 個、削除: 0 個、保留: 6 個。
root@Ubuntu18:~#

-保留されているアップグレードを完了させる

root@Ubuntu18:~# apt install -y linux-generic-hwe-18.04 linux-headers-generic-hwe-18.04 linux-image-generic-hwe-18.04 python3-software-properties software-properties-common software-properties-gt
k
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
以下のパッケージが自動でインストールされましたが、もう必要とされていません:
  gir1.2-goa-1.0 gir1.2-snapd-1
これを削除するには 'apt autoremove' を利用してください。
以下の追加パッケージがインストールされます:
  linux-headers-5.4.0-149-generic linux-hwe-5.4-headers-5.4.0-149 linux-image-5.4.0-149-generic linux-modules-5.4.0-149-generic linux-modules-extra-5.4.0-149-generic
  ubuntu-advantage-desktop-daemon
提案パッケージ:
  fdutils linux-hwe-5.4-doc-5.4.0 | linux-hwe-5.4-source-5.4.0 linux-hwe-5.4-tools
以下のパッケージが新たにインストールされます:
  linux-headers-5.4.0-149-generic linux-hwe-5.4-headers-5.4.0-149 linux-image-5.4.0-149-generic linux-modules-5.4.0-149-generic linux-modules-extra-5.4.0-149-generic
  ubuntu-advantage-desktop-daemon
以下のパッケージはアップグレードされます:
  linux-generic-hwe-18.04 linux-headers-generic-hwe-18.04 linux-image-generic-hwe-18.04 python3-software-properties software-properties-common software-properties-gtk
アップグレード: 6 個、新規インストール: 6 個、削除: 0 個、保留: 0 個。
76.5 MB のアーカイブを取得する必要があります。
この操作後に追加で 368 MB のディスク容量が消費されます。

保留されていたパッケージのインストールが完了
  • 念のためもう一回パッケージのアップグレードを実行しておく
root@Ubuntu18:~# apt-get update
取得:1 http://security.ubuntu.com/ubuntu bionic-security InRelease [88.7 kB]
ヒット:2 http://jp.archive.ubuntu.com/ubuntu bionic InRelease
取得:3 http://jp.archive.ubuntu.com/ubuntu bionic-updates InRelease [88.7 kB]
取得:4 http://jp.archive.ubuntu.com/ubuntu bionic-backports InRelease [83.3 kB]
261 kB を 1秒 で取得しました (184 kB/s)
パッケージリストを読み込んでいます... 完了
root@Ubuntu18:~# apt-get upgrade
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
アップグレードパッケージを検出しています... 完了
以下のパッケージが自動でインストールされましたが、もう必要とされていません:
  gir1.2-goa-1.0 gir1.2-snapd-1
これを削除するには 'apt autoremove' を利用してください。
アップグレード: 0 個、新規インストール: 0 個、削除: 0 個、保留: 0 個。
root@Ubuntu18:~#
  • もう必要ないパッケージが残っているとメッセージが出ていたので再度削除
root@Ubuntu18:~# apt autoremove -y
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
以下のパッケージは「削除」されます:
  gir1.2-goa-1.0 gir1.2-snapd-1
アップグレード: 0 個、新規インストール: 0 個、削除: 2 個、保留: 0 個。
この操作後に 150 kB のディスク容量が解放されます。
(データベースを読み込んでいます ... 現在 159419 個のファイルとディレクトリがインストールされています。)
gir1.2-goa-1.0:amd64 (3.28.0-0ubuntu2.1) を削除しています ...
gir1.2-snapd-1:amd64 (1.58-0ubuntu0.18.04.0) を削除しています ...
root@Ubuntu18:~#
  • もう一回OSのアップグレードに挑戦する(2回目)
root@Ubuntu18:~# do-release-upgrade
新しい Ubuntu のリリースをチェックしています
You have not rebooted after updating a package which requires a reboot. Please reboot before upgrading.
root@Ubuntu18:~#

-パッケージのアップグレードを実行したあと再起動が必要なパッケージがあるが再起動していないので、OSのアップグレードを実行する前にOSの再起動を実行しろと言われたので再起動する

root@Ubuntu18:~# reboot
  • 再起動が完了したら再度rootに入ってOSのアップグレードに挑戦する(3回目)
user@Ubuntu18:~$ sudo su -
[sudo] user のパスワード:
root@Ubuntu18:~# do-release-upgrade
新しい Ubuntu のリリースをチェックしています
0% [作業中]                                                                                                                                                             0% [jp.archive.ubuntu.com へ接続しています]                                                                                                                             0% [jp.archive.ubuntu.com (2001:67c:1562::18) へ接続しています]                                                                                                         0% [jp.archive.ubuntu.com (185.125.190.39) へ接続しています]                                                                                                            0% [ヘッダの待機中です]                                                                                                                                                 取得:1 ツールの署名のアップグレード [1,554 B]                                                                                                                           
99% [作業中]                                                                                                                                                            99% [ヘッダの待機中です]                                                                                                                                                取得:2 ツールのアップグレード [1,338 kB]                                                                                                                                
100% [作業中]                                                                                                                                                           1,340 kバイト/0秒 を取得しました (0 B/秒)                                                                                                                               
「focal.tar.gz.gpg」を用いて「focal.tar.gz」の認証を行ないます
'focal.tar.gz' の展開中

キャッシュを読み込み中

パッケージマネージャーをチェック中です

SSH経由で実行していますが、続けますか?


このセッションはSSH上で実行されているようです。アップグレードをSSH越しに行うことは推奨されません。アップグレードに失敗した時の復元が困難になるからです。

続行する場合、追加のSSHデーモンをポート '1022' で起動します。
本当に作業を進めてよろしいですか?

続行する[yN]
  • SSH経由でOSのアップグレードを行おうとすると注意される。
    テスト環境なので続行するが、続行すると1022番ポートでもSSH接続ができるようになるらしい。
    (後で接続できるか確認する)
予備のsshdを開始します

障害が起こったときに復旧しやすくするため、ポート '1022' でもう一つの sshd
を開始します。現在実行中のsshにおかしなことが起きても、もう一方のポートに接続することができます。

ファイアウォールを実行している場合、このポートを一時的に開く必要があります。この操作は、潜在的な危険があるため自動的には行われません。以下の例のようにしてポートを開けます:
'iptables -I INPUT -p tcp --dport 1022 -j ACCEPT'

続けるには [ENTER] キーを押してください
  • 【寄り道】もう一個別にターミナルを開いてiptablesを確認
    何も設定されていないのでこちらのターミナルは閉じる
    もしなにか設定されている場合は'iptables -I INPUT -p tcp --dport 1022 -j ACCEPT'を実行して1022番ポートを開放すること
root@Ubuntu18:~# iptables --list
Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
root@Ubuntu18:~#
  • Enterをして続きを実行
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
0% [作業中]                                                                                                                                                             0% [作業中]                                                                                                                                                             0% [jp.archive.ubuntu.com へ接続しています]                                                                                                                             0% [jp.archive.ubuntu.com へ接続しています]                                                                                                                             0% [jp.archive.ubuntu.com (2001:67c:1562::15) へ接続しています]                                                                                                         0% [jp.archive.ubuntu.com (2001:67c:1562::15) へ接続しています]                                                                                                         0% [jp.archive.ubuntu.com (91.189.91.39) へ接続しています]                                                                                                              0% [jp.archive.ubuntu.com (91.189.91.39) へ接続しています]                                                                                                              0% [jp.archive.ubuntu.com (91.189.91.39) へ接続しています] [security.ubuntu.com へ接続しています]                                                                       0% [jp.archive.ubuntu.com (91.189.91.39) へ接続しています] [security.ubuntu.com へ接続しています]                                                                       0% [jp.archive.ubuntu.com (91.189.91.39) へ接続しています] [security.ubuntu.com (2001:67c:1562::18) へ接続しています]                                                   0% [jp.archive.ubuntu.com (91.189.91.39) へ接続しています] [security.ubuntu.com (2001:67c:1562::18) へ接続しています]                                                   0% [jp.archive.ubuntu.com (91.189.91.39) へ接続しています] [security.ubuntu.com (185.125.190.39) へ接続しています]                                                      0% [jp.archive.ubuntu.com (91.189.91.39) へ接続しています] [security.ubuntu.com (185.125.190.39) へ接続しています]                                                      0% [Connected to jp.archive.ubuntu.com (91.189.91.39)] [security.ubuntu.com (185.125.190.39) へ接続しています]                                                          0% [Connected to jp.archive.ubuntu.com (91.189.91.39)] [security.ubuntu.com (185.125.190.39) へ接続しています]                                                          0% [ヘッダの待機中です] [security.ubuntu.com (185.125.190.39) へ接続しています]                                                                                         0% [ヘッダの待機中です] [security.ubuntu.com (185.125.190.39) へ接続しています]                                                                                         0% [ヘッダの待機中です] [ヘッダの待機中です]                                                                                                                            0% [ヘッダの待機中です] [ヘッダの待機中です]                                                                                                                            ヒット: http://jp.archive.ubuntu.com/ubuntu bionic InRelease                                                                                                            

----- 省略 -----

パッケージマネージャーをチェック中です
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了

変更点を確認中

変更点を確認中

アップグレードを開始しますか?


4 個のインストール済みパッケージは Canonical
によってサポートされなくなりました。ただしコミュニティからのサポートは受けることができます。

17 個のパッケージが削除されます。 324 個の新規パッケージがインストールされます。 1304
個のパッケージがアップグレードされます。

合計 1,325 M をダウンロードする必要があります。 このダウンロードは約 4 分 かかります。

アップグレードをインストールするのに数時間かかることがあります。ダウンロードが完了してしまうと、処理はキャンセルできません。

 続行する[yN]  詳細 [d]
  • 詳細[d]を押して中身を見てみる
サポートされなくなりました: libdbusmenu-gtk4 libegl1-mesa libllvm6.0
  mozc-utils-gui


削除: libfwup1 libzinnia0v5

(自動インストールされた) fwupdate-signed gnome-settings-daemon-schemas
  libapt-inst2.0 libapt-pkg5.0 libcupscgi1 libcupsmime1 libcupsppdc1
  libebook-1.2-19 libedata-book-1.2-25 libedata-cal-1.2-28 libldb1
  libpolkit-backend-1-0 libsensors4 libsnmp30 uno-libs3 を削除


インストール: alsa-topology-conf alsa-ucm-conf apt-config-icons-hidpi
  bind9-dnsutils bind9-libs build-essential cpp-9 docbook-xml
  dpkg-dev enchant-2 fakeroot fonts-smc-gayathri fonts-urw-base35
  fonts-yrsa-rasa fprintd g++ g++-9 gamemode gcc gcc-10-base gcc-9
  gcc-9-base gir1.2-clutter-1.0 gir1.2-clutter-gst-3.0
  gir1.2-cogl-1.0 gir1.2-coglpango-1.0 gir1.2-graphene-1.0
  • インストールされるパッケージとか削除されるパッケージとかの一覧がエディタ上で確認ができる
    エディタを抜けると「 続行する[yN] 詳細 [d]」がまた出てくるのでyを押して続行する
 続行する[yN]  詳細 [d]

アップグレードが始まる
取得中
0% [作業中]                                                                                                                                                             0% [作業中]                                                                                                                                                             0% [jp.archive.ubuntu.com へ接続しています]                                                                                                                             0% [jp.archive.ubuntu.com へ接続しています]                                                                                                                             0% [jp.archive.ubuntu.com (2001:67c:1562::15) へ接続しています]                                                                                                         0% [jp.archive.ubuntu.com (2001:67c:1562::15) へ接続しています]                                                                                                         0% [jp.archive.ubuntu.com (91.189.91.38) へ接続しています]                                                                                                              0% [jp.archive.ubuntu.com (91.189.91.38) へ接続しています]                                                                                                              0% [ヘッダの待機中です]                                                                                                                                                 0% [ヘッダの待機中です]                                                                                                                                                 取得:1 http://jp.archive.ubuntu.com/ubuntu focal-updates/main amd64 gcc-10-base amd64 10.3.0-1ubuntu1~20.04 [20.2 kB]

----- 省略 -----

古いソフトウェアを検索しています
状態情報を読み取っています... 完了

サポートが中止された(あるいはリポジトリに存在しない)パッケージを削除しますか?


196 個のパッケージが削除されます。 3 個の新規パッケージがインストールされます。

アップグレードの取得とインストールには数時間かかることがあります。ダウンロードが完了してしまうと、処理はキャンセルできません。

 続行する[yN]  詳細 [d]
  • 45分くらい待つと上のメッセージが出たので、続行する
続行する[yN]  詳細 [d]y
0% [作業中]                                                                                                                                                             0% [作業中]                                                                                                                                                             0% [jp.archive.ubuntu.com へ接続しています]                                                                                                                             0% [jp.archive.ubuntu.com へ接続しています]                                                                                                                             0% [jp.archive.ubuntu.com (2001:67c:1562::15) へ接続しています]                                                                                                         0% [jp.archive.ubuntu.com (2001:67c:1562::15) へ接続しています]                                                                                                         0% [jp.archive.ubuntu.com (91.189.91.38) へ接続しています]                                                                                                              0% [jp.archive.ubuntu.com (91.189.91.38) へ接続しています]                                                                                                              0% [ヘッダの待機中です]                                                                                                                                                 0% [ヘッダの待機中です]                                                                                                                                                 取得:1 http://jp.archive.ubuntu.com/ubuntu focal/main amd64 libcommon-sense-perl amd64 3.74-2build6 [20.1 kB]

----- 省略 -----

fontconfig (2.13.1-2ubuntu3) のトリガを処理しています ...

システムのアップグレードが完了しました。

再起動が必要です

アップグレードを完了するには再起動が必要です。
'Y' を選択すると再起動します。

続行する[yN]
  • アップグレードが完了したので再起動する
続行する[yN] y
  • 再起動完了後再度ログインしてOSのバージョンを確認
user@Ubuntu18:~$ cat /etc/os-release
NAME="Ubuntu"
VERSION="20.04.6 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.6 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal
user@Ubuntu18:~$
  • 無事アップグレード完了

ChatGPT対抗のGoogleのBardが公開

ChatGPT対抗のGoogleのBard

Google製のAIチャットツールが公開になったので申し込んでみたところ30分ぐらいで利用可能になった。 以下のページからwish listに登録できます。

Google Bard
https://bard.google.com/

現状日本語対応しておらず、日本語で聞いたらサポートしていないと英語で怒られた。

今後以下の記事にあるように

グーグル、対話型AI「Bard」に「GitHub Copilot」対抗のコーディング支援機能を実装へ。社内テスト開始
https://www.businessinsider.jp/post-268676

との記載もあるので、興味がある人は早めに申し込んだほうがいいかなと思う。

Bardを使ってみた

ログインすると以下のような画面が出てくる。 インターフェイスはChatGPTとよく似ていて、下側のEnter a prompt hereに質問を入れると回答してくれる。

以下のような質問をしてみた。

SQLAlchemy2.0でデータを追加する方法についてなるべく詳しく方法を教えてください。
↓
Please tell me how to add data in SQLAlchemy 2.0 in as much detail as possible.

回答が現れるまで4,5秒程度で、回答の長さにかかわらず同じ時間で回答が返ってくるので結構早いなと思う。

回答の上の方にDraft 1, Draft 2, Draft 3と表示されているところをクリックすると回答内容が変わる。
ChatGPTは同じ質問に対しては基本的に同じ回答が返ってくるが、異なった回答が返ってくることがある。
何度かReset chatをして同じ質問をすると以前出力された同じ回答が返ってくることもある。
英語だと質問するのも大変なので早めに日本語サポートをしてくれるとありがたい。

最新情報を知っているのかなと思い以下の質問をした。

codewhispereって知ってる?
↓
are you familiara with codewhispere?

知っていると回答が来たので、最新情報を知っているし、URLを提示してくれるので結構便利な気がする。 日本語に対応してくれたらBingのチャットより便利かもしれない。

Yes, I am familiar with CodeWhisperer. It is an AI coding companion that generates real-time, single-line or full-function code suggestions in your Integrated Development Environment (IDE) to help you quickly build software. CodeWhisperer is trained on billions of lines of code and can generate code suggestions ranging from snippets to full functions in real time based on your comments and existing code. It can also flag or filter code suggestions that resemble open-source training data.

Here are some of the benefits of using CodeWhisperer:

Increased productivity: CodeWhisperer can help you write code faster and more efficiently by providing you with real-time code suggestions. This can free up your time to focus on more creative and strategic aspects of your work.
Improved code quality: CodeWhisperer can help you write more reliable and secure code by suggesting code that is based on best practices. This can help you avoid costly bugs and security vulnerabilities.
Reduced learning curve: CodeWhisperer can help you learn new programming languages and frameworks more quickly by providing you with code suggestions that are specific to the language or framework you are using.
If you are a developer who wants to improve your productivity, code quality, and learning curve, then I recommend using CodeWhisperer.

Sources
1. 
aws.amazon.com/codewhisperer/

AWS CodeWhispererをvscodeで使う

Amazon CodeWhisperer

AmazonからGitHubのCopilotの様なサービスが発表された。
個人ユーザーは無料で利用できるらしい。
GitHubのCopilotは便利そうだけれども使ったことがないのでこっちを使ってみた。

vscodeで使ってみた

環境

使ってみた

Djangoでプロジェクトを作って、アプリを作ってプロジェクト側のurls.pyにアプリ側のルートを追加しようとしたらコード補完が出てきた。
コードの補完が出ている状態でTABを押すと決定となり、文字が自動的に挿入される。
Amazon CodeWhispererを使う時はAmazon CodeWhisperer以外のコード補完機能は止めるように記載があったので止めたほうが良さそう。
しばらく使ってみようかと思う。

コードの補完はalt + cを押すといつでも可能です。

vscodeでCodeWhispererを使う

vscodeで利用する際の設定方法(書いてあることは公式と同じです)

  1. vscodeを開いて拡張機能(ctrl+shift+x)を選択
  2. aws拡張機能を検索して、AWS Toolkitをインストール
  3. 増えたawsのアイコンをクリックし、その中にある「CodeWhisperer」をクリックすると「▶Start」が現れるのでクリック
  4. 「Use a personal email to sign up and sign in with AWS Builder ID」をクリック
  5. 以下のようなウィンドウが現れて、黒く塗りつぶしたところに「XXXX-XXXXX」の様な文字が現れるので、現れた文字を覚えたら、「Copy Code and Proceed」を押してブラウザを起動する
  6. 入力欄が現れるので先程現れた文字を入力する
    1.「AWS Builer ID」を持っている場合は既に持っているIDでログイン、持っていない場合は「Eメールアドレス」にメールアドレスを入力してアカウントを登録する。
    今回は新規登録を行う。
  7. Your Nameの所へ登録したい名前を入力する。
    「this might be visible to other people using AWS.」とあるので、ここに入力した名前は他のユーザーから見える可能性があるので、見られても問題のない名前を入力。
  8. 登録したメールアドレスに「Verification code」が届くので届いたコードを入力。
  9. ログインする際に利用したいパスワードを入力する
  10. AWS ToolkitがVSCodeのデータにアクセスしてもよいか確認されるので、「Allow」を押す
  11. 以下の画面が出たら完了
  12. 次回以降もSSO(Single Sign On)は求められるが登録したID, パスワードは不要

SQLAlchemy2.0の使い方(少しずつ追記予定)

SQLAlchemy 2.0

ずっとSQLAlchemy1.4を使ってきたがそろそSQLAlchemy2.0を使おうかなと思いドキュメントを見たら書き方が結構変わっていて混乱したのでまとめておく。

ドキュメントへのリンクは基本的にSQLAlchemy2.0のドキュメントへのリンクです。

公式サイトの「SQLAlchemy 2.0 - Major Migration Guide」を見ると1.4から2.0への移行のためのガイドが書かれている。
SQLAlchemyの公式サイトはとても見づらい。

SQLAlchemyの基本的な使い方

SQLAlchemyの基本的な使い方を参考にしてください。
ネットで検索すると他にもたくさんの記事が出てくるかと思います。
ここでは基本的な使い方の説明は記載しません。
SQLAlchemy1.4→2.0で変更された部分についての記載と行います。

engineの作り方(変更なし)

# SQLAlchem 1.0
from sqlalchemy import create_engine
engine = create_engine(DATABASE_URL, echo=True)

# SQLAlchem 2.0
from sqlalchemy import create_engine
engine = create_engine(DATABASE_URL, echo=True)

モデルの作成方法とmigrateの方法

モデルの作成方法(結構変わっている)

  • モデルを作る際にdeclarative_baseを継承して作っていたが、新しい書き方ではDeclarativeBaseを継承してベースクラスを作成する。
  • カラムの定義を行う際にColumnを利用していたがmapped_columnを利用してカラムの定義を行い、型情報を持たせることができるようになった。
  • 型情報の定義にはMappedを利用し、定義方法はpythonの型アノテーションを利用する。
  • 型情報を持たせることができるようになったので、誤った型のデータを入れようとすると警告を出すことができるようになった。

migrateの方法(変更なし)

  • Baseの作り方は変わったがmigrateをする際の方法自体に変更はなし。
# SQLAlchem 1.0
# SQLAlchem 2.0
Base.metadata.create_all(engine)

モデルの定義方法とmigrateのサンプル

以下を実行するとsample.sqlite3としてusersテーブルが作成される。

SQLAlchemy1.0

from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.ext.declarative import declarative_base

DATABASE_URL = "sqlite:///sample.sqlite3"
engine = create_engine(DATABASE_URL, echo=True)

Base = declarative_base()


class User(Base):
    __tablename__ = "users"

    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, index=True)
    age = Column(Integer)


Base.metadata.create_all(engine)

SQLAlchemy2.0

型の定義ができるのがとても良い。

from typing import Optional

from sqlalchemy import Integer, String, create_engine
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column

DATABASE_URL = "sqlite:///sample.sqlite3"
engine = create_engine(DATABASE_URL, echo=True)


class Base(DeclarativeBase):
    pass


class User(Base):
    __tablename__ = "users"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
    name: Mapped[str] = mapped_column(String(50), nullable=False)
    fullname: Mapped[Optional[str]] = mapped_column(String)
    nickname: Mapped[Optional[str]] = mapped_column(String(30))


Base.metadata.create_all(bind=engine)

セッション

変更点

2.0になってsessionmakerの引数からautocommitが廃止された。
Sessionにはパラメーターとしては残っているがFalseにして置く必要がある。 https://docs.sqlalchemy.org/en/20/orm/session_api.html#sqlalchemy.orm.Session.params.autocommit

セッションの種類

  • 通常のアプリケーションの場合

    • sessionmaker
      • sessionmakerで作ったsessionはsessionのファクトリ関数のため、実行するたびに別のsessionが作られる。
      • sessionのclose, commitを手動で行う必要がある。
  • マルチスレッドのアプリケーション(Flake等)の場合

    • scoped_session
      • scoped_sessionで作ったsessionは何度sessionを作っても同一スレッド内であれば同じsessionが取得できる。
      • 処理が終わる際にsessionが自動的にcloseされる。

セッションの作り方

SQLAlchemy1.4

from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker

DATABASE_URL = "sqlite:///sample.sqlite3"
engine = create_engine(DATABASE_URL, echo=True)

Base = declarative_base()


class User(Base):
    __tablename__ = "users"

    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, index=True)
    age = Column(Integer)


Base.metadata.create_all(engine)


def create_session():
    # 追加するデータを作る
    u = User(id=1, name="name", age=11)

    # sesson(ファクトリー関数)を作る
    session_local = sessionmaker(bind=engine, autoflush=False, autocommit=False)

    # scoped_sessionを使用する場合
    # session_local = scoped_session(sessionmaker(bind=engine, autoflush=False, autocommit=False))

    # sessionを作る
    session = session_local()

    # データを追加してコミットする
    session.add(u)
    session.commit()
    session.close()

    # (別の方法)with 文を使ってsession(ファクトリー関数から)作成
    # データを追加してコミットする
    with session_local() as session:
        u = User(id=2, name="name2", age=12)
        session.add(u)
        session.commit()

SQLAlchemy2.0

from sqlalchemy import Integer, String, create_engine
from sqlalchemy.orm import DeclarativeBase, Mapped, Session, mapped_column, scoped_session, sessionmaker

DATABASE_URL = "sqlite:///sample.sqlite3"
engine = create_engine(DATABASE_URL, echo=True)


class Base(DeclarativeBase):
    pass


class User(Base):
    __tablename__ = "users"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
    name: Mapped[str] = mapped_column(String(50), nullable=False)
    age: Mapped[int] = mapped_column(Integer)


Base.metadata.create_all(bind=engine)


def create_session():
    # 追加するデータを作る
    u = User(id=1, name="name", age=11)

    # session(ファクトリー関数)を作る
    # autoflushのパラーメータが廃止された
    session_local = sessionmaker(bind=engine, autoflush=False)

    # scoped_sessionを使用する場合
    # session_local = scoped_session(sessionmaker(bind=engine, autoflush=False))

    # sessionを作る
    session = session_local()

    session.add(u)
    session.commit()
    session.close()

    # (別の方法)with文を使ってsessionを作成(1.4と違ってengieを渡して一気にsessionを作れる)
    # 後方五感のためにautocommitが残っているが指定する場合はFalseにする必要がある
    with Session(engine, autoflush=False, autocommit=False) as session:
        u = User(id=2, name="name2", age=12)
        session.add(u)
        session.commit()
        session.close()

データの追加

毎回コードの全文を記載すると長くなるので、ここからは必要な部分のコードのみを記載します。

変更点

SQLAlchemy1.4とSQLAlchemy2.0で大きな違いはなし。

データの追加方法

  • 共通 SQLAlchemy1.4, SQLAlchemy2.0

    • session.addでmodelオブジェクトを1件を追加する
    • session.add_allで複数のmodelオブジェクトを一括で追加する
    • session.bulk_save_objectsでmodelオブジェクトを一括で追加する
  • SQLAlchemy1.4

    • session.executeでDBのテーブルと同じキー名を持つdictを一括で追加する
    • session.bulk_insert_mappingsでmodelオブジェクトを指定してList[Dict]を一括で追加する
  • SQLAlchemy2.0

    • session.executeでinsertでモデルを指定してList[Dict]を一括で追加する

SQLAlchemy1.4, SQLAlchemy2.0

# 1件のデータを追加するパターン
def insert_data_basic():
    """発行されるSQL
    SQL      : INSERT INTO users (id, name, age) VALUES (?, ?, ?)
    parameter: (1, 'name', 11)
    """

    session = get_session()

    # 登録するオブジェクトをモデルから作成
    u = User(id=1, name="name", age=11)

    # 作成したオブジェクトをセッションに追加
    session.add(u)

    # セッションへ変更内容を反映する(コミットはしていないため、rollbackすることが可能)
    # flushをしたところでSQLが発行される
    session.flush()

    # セッションの内容をrollbackして取り消す
    session.rollback()

    # セッションnを閉じる
    session.close()


def insert_all_data():
    """発行されるSQL
    SQL      : INSERT INTO users (id, name, age) VALUES (?, ?, ?)
    parameter: ((1, 'name', 11), (2, 'name2', 12))
    """

    session = get_session()

    # 登録するオブジェクトをモデルから作成
    u = [
        User(id=1, name="name", age=11),
        User(id=2, name="name2", age=12),
    ]

    # リスト内のUserオブジェクトを一括で追加する
    session.add_all(u)

    # セッションへ変更内容を反映する(コミットはしていないため、rollbackすることが可能)
    # flushをしたところでSQLが発行される
    session.flush()

    # セッションの内容をrollbackして取り消す
    session.rollback()

    # セッションnを閉じる
    session.close()


def insert_bulk_save():
    """発行されるSQL
    SQL      : INSERT INTO users (id, name, age) VALUES (?, ?, ?)
    parameter: ((1, 'name', 11), (2, 'name2', 12))
    """
    session = get_session()

    # 登録するオブジェクトをList[Dict]の形式で作成
    u = [
        User(id=1, name="name", age=11),
        User(id=2, name="name2", age=12),
    ]

    # リスト内のUserオブジェクトを一括で追加する
    # bulk_save_objectsをしたところでSQLが発行される
    session.bulk_save_objects(u)

    # bulk_save_ojbectsでSQLが発行されているので、flushの効果はない
    session.flush()

    # セッションの内容をrollbackして取り消す
    session.rollback()

    # セッションnを閉じる
    session.close()

SQLAlchemy1.4

def insert_core():
    """発行されるSQL
    SQL      : INSERT INTO users (id, name, age) VALUES (?, ?, ?)
    parameter: ((1, 'name', 11), (2, 'name2', 12))
    """
    session = get_session()

    # 登録するデータをList[Dict]の形式で作成
    u = [
        dict(id=1, name="name", age=11),
        dict(id=2, name="name2", age=12),
    ]

    # リスト内のdict形式のオブジェクトを一括で追加する
    session.execute(User.__table__.insert(), u)

    # session.executeでSQLが発行されているので、flushの効果はない
    session.flush()

    # セッションの内容をrollbackして取り消す
    session.rollback()

    # セッションnを閉じる
    session.close()

SQLAlchemy2.0

def insert_core():
    """発行されるSQL
    SQL      : INSERT INTO users (id, name, age) VALUES (?, ?, ?)
    parameter: ((1, 'name', 11), (2, 'name2', 12))
    """
    session = get_session()

    # 登録するデータをList[Dict]の形式で作成
    u = [
        dict(id=1, name="name", age=11),
        dict(id=2, name="name2", age=12),
    ]

    # リスト内のdict形式のオブジェクトを一括で追加する
    session.execute(insert(User), u)

    # session.executeでSQLが発行されているので、flushの効果はない
    session.flush()

    # セッションの内容をrollbackして取り消す
    session.rollback()

    # セッションnを閉じる
    session.close()