質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.51%
Django

DjangoはPythonで書かれた、オープンソースウェブアプリケーションのフレームワークです。複雑なデータベースを扱うウェブサイトを開発する際に必要な労力を減らす為にデザインされました。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

1回答

19721閲覧

Djangoでのトランザクションの実装方法を教えてください。

uni8inu

総合スコア127

Django

DjangoはPythonで書かれた、オープンソースウェブアプリケーションのフレームワークです。複雑なデータベースを扱うウェブサイトを開発する際に必要な労力を減らす為にデザインされました。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

1グッド

2クリップ

投稿2016/11/20 13:28

編集2022/01/12 10:55

###前提・実現したいこと
Djangoを利用したWebアプリを作成しております。
ユーザー同時アクセス時のDB不整合を避けるため、トランザクション処理を行おうと思っております。

実装方法を色々調べたのですが、日本語の情報が少なかったため調べた実装方法が正しいか非常に不安です。
下記の方針で実装しようと考えています。
お詳しい方に間違っていないか、ぜんぜん違うよ!など指摘をいただきたく思います。

  1. Djangoではsettings.pyのATOMIC_REQUESTSをTrueにすれば全てのviewがトランザクションに対応する
  2. ↑の状態で、view関数内のModel操作(save()とかcreate()とか)部分でトランザクションエラーが発生した場合は

例外がraiseされる。
0. Model操作前後でtry-exception句で例外をcatchし、何か処理を行う。
例えば、ユーザーに再入力を促すtemplateを表示、またはview関数を成功するまでリトライし続ける。

###Viewの例(例外発生時は例外ページを表示する)

python

1#views.py / settings.pyのATOMIC_REQUESTSはTrueを設定 2def foo_page(request): 3 try: 4 model = MyModel.objects.create(name) 5 except TransactionManagementError as e: 6 return render(request, 'myproject/error_page.html', {'exception':e}) 7 8 return render(request, 'myproject/result_page.html', {})

###試したこと
公式のDocumentではATOMIC_REQUESTSでいけると読み取りました。
しかしエラーハンドリング方法の例がいまいち書いておらず、実装方法が不安です。

Database transactions
Retryの実装:Stack Overflow

###ものすごい混乱している部分
そもそもDjangoは自動コミットだからあまりトランザクションを意識しなくても良い?のでしょうか。
トランザクションの話と自動コミットはあんまり関係ないような気がするんですが、理解不足で混乱しております。

###補足情報(言語/FW/ツール等のバージョンなど)
Django 1.10.3.
Python 3.5.2

shimizukawa👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

https://docs.djangoproject.com/en/1.10/topics/db/transactions/#tying-transactions-to-http-requests によると、以下のように動作します。

It works like this. Before calling a view function, Django starts a transaction. If the response is produced without problems, Django commits the transaction. If the view produces an exception, Django rolls back the transaction.

view関数が呼ばれる前にトランザクションを開始します。もし、レスポンス時点で問題がなければ、Djangoはトランザクションをコミットします。もし内部で例外が発生したら、トランザクションをロールバックします。

modelのsave等でIntegrityError例外がおきる可能性はあります。
しかし、ドキュメントに書いてあるのはそれに限らず、view関数がなんらかの例外を発生させて呼出元に伝搬した場合に何が起きるかを説明しています。

// 2017/3/3 訂正: ここから間違い(末尾に追記あり)
また、ATOMIC_REQUESTS設定の場合、全てのviewが atomic となるため、各viewが複数のリクエストで同時に実行されないことを保障できます。
// 2017/3/3 訂正: ここまで

そもそもDjangoは自動コミットだからあまりトランザクションを意識しなくても良い?のでしょうか。
トランザクションの話と自動コミットはあんまり関係ないような気がするんですが、理解不足で混乱しております。

DBではかならずSQL発行時にトランザクションが作られますが、自動コミット設定では、各SQL発行時に自動的にトランザクションを開始して、そのSQL実行後に自動的にコミット or ロールバックが行われます。参考: autocommit

モデルのsave時のエラーで画面表示を切り替えたいのであれば、view関数内で with atomic(): ブロックを用いて行うのがよさそうです。参考: atomic


2017/3/3 追記

ATOMIC_REQUESTS で各viewが複数のリクエストで同時に実行されないことを保障、というのは間違いです(別件の記憶を元に間違えて回答していました)

ATOMIC_REQUESTSがTrueならトランザクション異常発生した場合、Djangoが自動(!)でトランザクションエラー発生してないように振る舞うんでしょうか?
同時アクセスしたユーザーはトランザクションエラーが発生したとは気づかずに穏便に結果を受け取る、という振る舞いになる?(DBのCRUDも正しく処理され終わる?)

いいえ。
トランザクションエラーが発生した場合はそのまま例外としてユーザーまで結果がもどります。
間違った説明をしてしまい、すみませんでした。

ATOMIC_REQUESTS=True としている場合のview関数内(または、 with transaction.atomic() のwithブロック中)に例外が発生すると、その時点でsave済みのモデルも破棄(rollback)されます

Python

1def view(request): 2 m = SomeModel.objects.create() # ここで保存される 3 do_some_process() # raise some exception! 4 return render(...)

このコードの場合、 ATOMIC_REQUESTS=True設定していない 場合は、do_some_process()が例外を発生させたとしても「ここで保存される」で本当にDBに保存されているので、DBは更新されます。
これに対して ATOMIC_REQUESTS=True設定している 場合は、「ここで保存される」のはトランザクション上の話になるので、do_some_process()で例外が起きれば、rollbackされ、DBは更新されません。

もしtry/exceptで例外をview関数の外にもれないようにすれば、 ATOMIC_REQUESTS=True であってもDBは更新されます。

Python

1# ATOMIC_REQUESTS=True 2def view(request): 3 m = SomeModel.objects.create() # ここで保存される 4 try: 5 do_some_process() # raise some exception! 6 except: 7 pass # 例外にぎりつぶし 8 return render(...) 9 # viewk関数は正常に完了し、「ここで保存される」のデータはview関数を出たところでcommitされる

まとめ

Model操作前後でtry-exception句で例外をcatchし、何か処理を行う。
例えば、ユーザーに再入力を促すtemplateを表示、またはview関数を成功するまでリトライし続ける。

ユーザーに再入力を促すtemplateを表示するのは以下のように実装できます

Python

1# ATOMIC_REQUESTS=True 2def view(request): 3 try: 4 m1 = RelModel.objects.create(...) # 一旦save 5 m2 = SomeModel(pk=1, m1=m1) # id重複 6 m2.save() # raise 7 except: 8 render(...) # エラーなので再入力してねというテンプレート. m1はrollbackされる 9 return render(...) # 更新できましたというテンプレート 10 # viewk関数は正常に完了し、「ここで保存される」のデータはview関数を出たところでcommitされる

view関数を成功するまでリトライし続ける実装は、同一トランザクション中だと(DBのISOLATION LEVELにもよるかもしれませんが)できないかもしれません(未確認です)。

投稿2016/11/21 12:41

編集2017/03/03 02:48
shimizukawa

総合スコア1847

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

uni8inu

2016/11/22 15:27

ご回答ありがとうございます。直接の課題解決方法として、トランザクションと異常発生時対応の実装方法として、C(R)UD箇所にwith atomic()埋め込みで対応できそうです。 しかしなんとか理解しようとしていますが、まだ理解が足りない点を追加で質問させて下さい。 【よくわからない点】 1.「また、ATOMIC_REQUESTS設定の場合、全てのviewが atomic となるため、各viewが複数のリクエストで同時に実行されないことを保障できます。」←ということはATOMIC_REQUESTSをTrueにしておけばそもそもトランザクション異常が発生しないので、ユーザーへの影響は考慮しない=異常時処理は書かなくていい?(なんか違う感じがしますが・・・) 2.↑で、いや異常処理は必要な場合、with atomic()でなく、わざわざATOMIC_REQUESTSがTrueを使う理由があまりわからなくなりました。 ATOMIC_REQUESTSがTrueならトランザクション異常発生した場合、Djangoが自動(!)でトランザクションエラー発生してないように振る舞うんでしょうか?同時アクセスしたユーザーはトランザクションエラーが発生したとは気づかずに穏便に結果を受け取る、という振る舞いになる?(DBのCRUDも正しく処理され終わる?)
shimizukawa

2017/03/03 02:48

時間がだいぶ経ってしまいましたが、手元で検証して回答を修正しました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.51%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問