回答編集履歴

1

間違った回答の訂正と追加情報

2017/03/03 02:48

投稿

shimizukawa
shimizukawa

スコア1847

test CHANGED
@@ -16,7 +16,11 @@
16
16
 
17
17
 
18
18
 
19
+ // 2017/3/3 訂正: ここから間違い(末尾に追記あり)
20
+
19
21
  また、ATOMIC_REQUESTS設定の場合、全てのviewが [atomic](https://docs.djangoproject.com/en/1.10/topics/db/transactions/#django.db.transaction.atomic) となるため、各viewが複数のリクエストで同時に実行されないことを保障できます。
22
+
23
+ // 2017/3/3 訂正: ここまで
20
24
 
21
25
 
22
26
 
@@ -33,3 +37,127 @@
33
37
 
34
38
 
35
39
  モデルのsave時のエラーで画面表示を切り替えたいのであれば、view関数内で `with atomic():` ブロックを用いて行うのがよさそうです。参考: [atomic](https://docs.djangoproject.com/en/1.10/topics/db/transactions/#django.db.transaction.atomic)
40
+
41
+
42
+
43
+ -------------
44
+
45
+
46
+
47
+ 2017/3/3 追記
48
+
49
+
50
+
51
+ ATOMIC_REQUESTS で各viewが複数のリクエストで同時に実行されないことを保障、というのは間違いです(別件の記憶を元に間違えて回答していました)
52
+
53
+
54
+
55
+ > ATOMIC_REQUESTSがTrueならトランザクション異常発生した場合、Djangoが自動(!)でトランザクションエラー発生してないように振る舞うんでしょうか?
56
+
57
+ > 同時アクセスしたユーザーはトランザクションエラーが発生したとは気づかずに穏便に結果を受け取る、という振る舞いになる?(DBのCRUDも正しく処理され終わる?)
58
+
59
+
60
+
61
+ いいえ。
62
+
63
+ トランザクションエラーが発生した場合はそのまま例外としてユーザーまで結果がもどります。
64
+
65
+ 間違った説明をしてしまい、すみませんでした。
66
+
67
+
68
+
69
+ `ATOMIC_REQUESTS=True` としている場合のview関数内(または、 `with transaction.atomic()` のwithブロック中)に例外が発生すると、その時点でsave済みのモデルも破棄(rollback)されます
70
+
71
+
72
+
73
+ ```Python
74
+
75
+ def view(request):
76
+
77
+ m = SomeModel.objects.create() # ここで保存される
78
+
79
+ do_some_process() # raise some exception!
80
+
81
+ return render(...)
82
+
83
+ ```
84
+
85
+
86
+
87
+ このコードの場合、 `ATOMIC_REQUESTS=True` を **設定していない** 場合は、do_some_process()が例外を発生させたとしても「ここで保存される」で本当にDBに保存されているので、DBは更新されます。
88
+
89
+ これに対して `ATOMIC_REQUESTS=True` と *設定している* 場合は、「ここで保存される」のはトランザクション上の話になるので、do_some_process()で例外が起きれば、rollbackされ、DBは更新されません。
90
+
91
+
92
+
93
+ もしtry/exceptで例外をview関数の外にもれないようにすれば、 `ATOMIC_REQUESTS=True` であってもDBは更新されます。
94
+
95
+
96
+
97
+ ```Python
98
+
99
+ # ATOMIC_REQUESTS=True
100
+
101
+ def view(request):
102
+
103
+ m = SomeModel.objects.create() # ここで保存される
104
+
105
+ try:
106
+
107
+ do_some_process() # raise some exception!
108
+
109
+ except:
110
+
111
+ pass # 例外にぎりつぶし
112
+
113
+ return render(...)
114
+
115
+ # viewk関数は正常に完了し、「ここで保存される」のデータはview関数を出たところでcommitされる
116
+
117
+ ```
118
+
119
+
120
+
121
+ まとめ
122
+
123
+
124
+
125
+ > Model操作前後でtry-exception句で例外をcatchし、何か処理を行う。
126
+
127
+ > 例えば、ユーザーに再入力を促すtemplateを表示、またはview関数を成功するまでリトライし続ける。
128
+
129
+
130
+
131
+ ユーザーに再入力を促すtemplateを表示するのは以下のように実装できます
132
+
133
+
134
+
135
+ ```Python
136
+
137
+ # ATOMIC_REQUESTS=True
138
+
139
+ def view(request):
140
+
141
+ try:
142
+
143
+ m1 = RelModel.objects.create(...) # 一旦save
144
+
145
+ m2 = SomeModel(pk=1, m1=m1) # id重複
146
+
147
+ m2.save() # raise
148
+
149
+ except:
150
+
151
+ render(...) # エラーなので再入力してねというテンプレート. m1はrollbackされる
152
+
153
+ return render(...) # 更新できましたというテンプレート
154
+
155
+ # viewk関数は正常に完了し、「ここで保存される」のデータはview関数を出たところでcommitされる
156
+
157
+ ```
158
+
159
+
160
+
161
+ view関数を成功するまでリトライし続ける実装は、同一トランザクション中だと(DBのISOLATION LEVELにもよるかもしれませんが)できないかもしれません(未確認です)。
162
+
163
+