Djangoにおけるトランザクションの応用

こんにちは、グローバルウェイの林です。

今回は前回に続きデータベースアクセスの際に必須となる機能であるトランザクションについて、少し応用的な技術を紹介します。

目次

この記事は以下の方を対象としています。

★4 Python開発経験が3年以上。
★3 Python開発経験が1年以上。
★2 Python 初級者。簡単なプログラムコードが書けます。
★1 プログラミング未経験。

前回の復習と課題について

設定ファイル上に自動トランザクションを有効に設定する記載をすることで、トランザクションが有効になることを説明しました。

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "NAME": "mydatabase",
        "USER": "mydatabaseuser",
        "PASSWORD": "mypassword",
        "HOST": "127.0.0.1",
        "PORT": "5432",
        'ATOMIC_REQUESTS': True,  # 自動トランザクション有効
    }
}

そしてその中で、以下のように例外をキャッチした場合にロールバックが行われない旨を説明しました。

def withdrawMoney(account, ammount)
    account. balance -= ammount   # 口座の残高を減算
    account.save()          # ①データベースを変更した値で更新
    try:
        lib.withdraaw(ammount)    # ②実際の集金処理を呼び出し、異常時に例外発生
    except Exception as e:
         return e.message         # ③例外を消化するためロールバックされない

入力値に対するチェックなどは事前に行い、その後にエラーを発生させないというのが原則となりますが、どうしてもデータ加工後でないと検出できないケースもあります。今回はこういった場合にどうすればよいかを説明いたします。

自動トランザクションの解除と手動トランザクション

自動トランザクションの解除

自動トランザクションを解除するのは簡単で、以下サンプルの通りアノテーションを付与することで解除可能です。アノテーションのつけ方の注意点としては、このアノテーションはurls.pyで定義した関数でのみ定義可能であり、サブルーチンとなるような関数などには指定不可となります。

当たり前ですが、自動トランザクションを解除しているため、このままの状態でDBを更新するのは危険です。

@transaction.non_atomic_requests   # 自動トランザクションの無効化
def withdrawMoney(account, ammount)
    account. balance -= ammount
    account.save()
    try:
        lib.withdraaw(ammount)
    except Exception as e:         return e.message

手動トランザクション

手動トランザクションの方法は、下記サンプルのようにトランザクション用のブロックを定義することで実現できます。基本的な考えは自動トランザクションと同じで、該当区間内で例外を発生させなければデータの保存が確定します。

注意しなければならないのは、他の上位のトランザクション(より上位のトランザクション、自動トランザクション等)が有効になっている場合、最終的に例外が発生しなければ、変更が確定してしまいます。

def withdrawMoney(account, ammount)
    account. balance -= ammount
    with transaction.atomic():       # 手動トランザクションの設定
        account.save()
        lib.withdraaw(ammount)
    except Exception as e:
        # 例外が発行された場合はロールバックされるはずだが
        # 自動トランザクションが有効な場合はデータの復旧が行われない
        return e.message

任意のトランザクション

もうお分かりかと思いますが、任意のトランザクションを用いるには上記記載の技術を両方利用します。自動トランザクションを解除し、任意で設定したい区間でトランザクションを設定します。その区間でエラー条件を満たした場合のみ例外を発生させれば、データのロールバックが可能です。

@transaction.non_atomic_requests     # 自動トランザクションを解除
def withdrawMoney(account, ammount)
    account. balance -= ammount
    with transaction.atomic():       # 手動トランザクションの設定
        account.save()
        lib.withdraaw(ammount)
    except Exception as e:
        # このケースであれば手動トランザクションが最上位となるため
        # データ加工後でもロールバックが可能
        return e.message

まとめ

トランザクション制御はデータベースを用いるアプリでは重要な機能です。前回の繰り返しになりますが、曖昧な理解でアプリ開発を進めてしまうと、大きなバグにつながるリスクとなります。

本記事ではトランザクション機能に関する理解の導入を目的としていたため、内容を極力シンプルにしてみました。

今回の記事をよんで、多少でも理解の一端になったのであれば、是所とも参考文献に記載したdjango公式ドキュメントを確認し、理解を深めていってください。

参考文献

データベースのトランザクション:django doc

この記事が気に入ったら
いいね または フォローしてね!

  • URLをコピーしました!
  • URLをコピーしました!
目次