50
53
from the settings, if there is no surrounding block (dirty is always false
51
54
when no current block is running).
57
using = DEFAULT_DB_ALIAS
58
connection = connections[using]
53
59
thread_ident = thread.get_ident()
54
if thread_ident in state and state[thread_ident]:
55
state[thread_ident].append(state[thread_ident][-1])
60
if thread_ident in state and state[thread_ident].get(using):
61
state[thread_ident][using].append(state[thread_ident][using][-1])
57
state[thread_ident] = []
58
state[thread_ident].append(settings.TRANSACTIONS_MANAGED)
59
if thread_ident not in dirty:
60
dirty[thread_ident] = False
63
state.setdefault(thread_ident, {})
64
state[thread_ident][using] = [settings.TRANSACTIONS_MANAGED]
65
if thread_ident not in dirty or using not in dirty[thread_ident]:
66
dirty.setdefault(thread_ident, {})
67
dirty[thread_ident][using] = False
61
68
connection._enter_transaction_management(managed)
63
def leave_transaction_management():
70
def leave_transaction_management(using=None):
65
72
Leaves transaction management for a running thread. A dirty flag is carried
66
73
over to the surrounding block, as a commit will commit all changes, even
67
74
those from outside. (Commits are on connection level.)
69
connection._leave_transaction_management(is_managed())
77
using = DEFAULT_DB_ALIAS
78
connection = connections[using]
79
connection._leave_transaction_management(is_managed(using=using))
70
80
thread_ident = thread.get_ident()
71
if thread_ident in state and state[thread_ident]:
72
del state[thread_ident][-1]
81
if thread_ident in state and state[thread_ident].get(using):
82
del state[thread_ident][using][-1]
74
84
raise TransactionManagementError("This code isn't under transaction management")
75
if dirty.get(thread_ident, False):
85
if dirty.get(thread_ident, {}).get(using, False):
77
87
raise TransactionManagementError("Transaction managed block ended with pending COMMIT/ROLLBACK")
78
dirty[thread_ident] = False
88
dirty[thread_ident][using] = False
90
def is_dirty(using=None):
82
92
Returns True if the current transaction requires a commit for changes to
85
return dirty.get(thread.get_ident(), False)
96
using = DEFAULT_DB_ALIAS
97
return dirty.get(thread.get_ident(), {}).get(using, False)
99
def set_dirty(using=None):
89
101
Sets a dirty flag for the current thread and code streak. This can be used
90
102
to decide in a managed block of code to decide whether there are open
91
103
changes waiting for commit.
106
using = DEFAULT_DB_ALIAS
93
107
thread_ident = thread.get_ident()
94
if thread_ident in dirty:
95
dirty[thread_ident] = True
108
if thread_ident in dirty and using in dirty[thread_ident]:
109
dirty[thread_ident][using] = True
97
111
raise TransactionManagementError("This code isn't under transaction management")
113
def set_clean(using=None):
101
115
Resets a dirty flag for the current thread and code streak. This can be used
102
116
to decide in a managed block of code to decide whether a commit or rollback
120
using = DEFAULT_DB_ALIAS
105
121
thread_ident = thread.get_ident()
106
if thread_ident in dirty:
107
dirty[thread_ident] = False
122
if thread_ident in dirty and using in dirty[thread_ident]:
123
dirty[thread_ident][using] = False
109
125
raise TransactionManagementError("This code isn't under transaction management")
126
clean_savepoints(using=using)
112
def clean_savepoints():
128
def clean_savepoints(using=None):
130
using = DEFAULT_DB_ALIAS
113
131
thread_ident = thread.get_ident()
114
if thread_ident in savepoint_state:
115
del savepoint_state[thread_ident]
132
if thread_ident in savepoint_state and using in savepoint_state[thread_ident]:
133
del savepoint_state[thread_ident][using]
135
def is_managed(using=None):
119
137
Checks whether the transaction manager is in manual or in auto state.
140
using = DEFAULT_DB_ALIAS
121
141
thread_ident = thread.get_ident()
122
if thread_ident in state:
123
if state[thread_ident]:
124
return state[thread_ident][-1]
142
if thread_ident in state and using in state[thread_ident]:
143
if state[thread_ident][using]:
144
return state[thread_ident][using][-1]
125
145
return settings.TRANSACTIONS_MANAGED
127
def managed(flag=True):
147
def managed(flag=True, using=None):
129
149
Puts the transaction manager into a manual state: managed transactions have
130
150
to be committed explicitly by the user. If you switch off transaction
131
151
management and there is a pending commit/rollback, the data will be
155
using = DEFAULT_DB_ALIAS
156
connection = connections[using]
134
157
thread_ident = thread.get_ident()
135
top = state.get(thread_ident, None)
158
top = state.get(thread_ident, {}).get(using, None)
138
if not flag and is_dirty():
161
if not flag and is_dirty(using=using):
139
162
connection._commit()
163
set_clean(using=using)
142
165
raise TransactionManagementError("This code isn't under transaction management")
144
def commit_unless_managed():
167
def commit_unless_managed(using=None):
146
169
Commits changes if the system is not in managed transaction mode.
172
using = DEFAULT_DB_ALIAS
173
connection = connections[using]
174
if not is_managed(using=using):
149
175
connection._commit()
176
clean_savepoints(using=using)
178
set_dirty(using=using)
154
def rollback_unless_managed():
180
def rollback_unless_managed(using=None):
156
182
Rolls back changes if the system is not in managed transaction mode.
185
using = DEFAULT_DB_ALIAS
186
connection = connections[using]
187
if not is_managed(using=using):
159
188
connection._rollback()
190
set_dirty(using=using)
192
def commit(using=None):
165
194
Does the commit itself and resets the dirty flag.
197
using = DEFAULT_DB_ALIAS
198
connection = connections[using]
167
199
connection._commit()
200
set_clean(using=using)
202
def rollback(using=None):
172
204
This function does the rollback itself and resets the dirty flag.
207
using = DEFAULT_DB_ALIAS
208
connection = connections[using]
174
209
connection._rollback()
210
set_clean(using=using)
212
def savepoint(using=None):
179
214
Creates a savepoint (if supported and required by the backend) inside the
180
215
current transaction. Returns an identifier for the savepoint that will be
181
216
used for the subsequent rollback or commit.
219
using = DEFAULT_DB_ALIAS
220
connection = connections[using]
183
221
thread_ident = thread.get_ident()
184
if thread_ident in savepoint_state:
185
savepoint_state[thread_ident].append(None)
222
if thread_ident in savepoint_state and using in savepoint_state[thread_ident]:
223
savepoint_state[thread_ident][using].append(None)
187
savepoint_state[thread_ident] = [None]
225
savepoint_state.setdefault(thread_ident, {})
226
savepoint_state[thread_ident][using] = [None]
188
227
tid = str(thread_ident).replace('-', '')
189
sid = "s%s_x%d" % (tid, len(savepoint_state[thread_ident]))
228
sid = "s%s_x%d" % (tid, len(savepoint_state[thread_ident][using]))
190
229
connection._savepoint(sid)
193
def savepoint_rollback(sid):
232
def savepoint_rollback(sid, using=None):
195
234
Rolls back the most recent savepoint (if one exists). Does nothing if
196
235
savepoints are not supported.
198
if thread.get_ident() in savepoint_state:
238
using = DEFAULT_DB_ALIAS
239
connection = connections[using]
240
thread_ident = thread.get_ident()
241
if thread_ident in savepoint_state and using in savepoint_state[thread_ident]:
199
242
connection._savepoint_rollback(sid)
201
def savepoint_commit(sid):
244
def savepoint_commit(sid, using=None):
203
246
Commits the most recent savepoint (if one exists). Does nothing if
204
247
savepoints are not supported.
206
if thread.get_ident() in savepoint_state:
250
using = DEFAULT_DB_ALIAS
251
connection = connections[using]
252
thread_ident = thread.get_ident()
253
if thread_ident in savepoint_state and using in savepoint_state[thread_ident]:
207
254
connection._savepoint_commit(sid)
213
def autocommit(func):
260
def autocommit(using=None):
215
262
Decorator that activates commit on save. This is Django's default behavior;
216
263
this decorator is useful if you globally activated transaction management in
217
264
your settings file and want the default behavior in some view functions.
219
def _autocommit(*args, **kw):
221
enter_transaction_management(managed=False)
223
return func(*args, **kw)
225
leave_transaction_management()
226
return wraps(func)(_autocommit)
228
def commit_on_success(func):
266
def inner_autocommit(func, db=None):
267
def _autocommit(*args, **kw):
269
enter_transaction_management(managed=False, using=db)
270
managed(False, using=db)
271
return func(*args, **kw)
273
leave_transaction_management(using=db)
274
return wraps(func)(_autocommit)
276
# Note that although the first argument is *called* `using`, it
277
# may actually be a function; @autocommit and @autocommit('foo')
278
# are both allowed forms.
280
using = DEFAULT_DB_ALIAS
282
return inner_autocommit(using, DEFAULT_DB_ALIAS)
283
return lambda func: inner_autocommit(func, using)
286
def commit_on_success(using=None):
230
288
This decorator activates commit on response. This way, if the view function
231
289
runs successfully, a commit is made; if the viewfunc produces an exception,
232
290
a rollback is made. This is one of the most common ways to do transaction
233
291
control in web apps.
235
def _commit_on_success(*args, **kw):
237
enter_transaction_management()
293
def inner_commit_on_success(func, db=None):
294
def _commit_on_success(*args, **kw):
240
res = func(*args, **kw)
242
# All exceptions must be handled here (even string ones).
251
leave_transaction_management()
252
return wraps(func)(_commit_on_success)
254
def commit_manually(func):
296
enter_transaction_management(using=db)
297
managed(True, using=db)
299
res = func(*args, **kw)
301
# All exceptions must be handled here (even string ones).
302
if is_dirty(using=db):
306
if is_dirty(using=db):
314
leave_transaction_management(using=db)
315
return wraps(func)(_commit_on_success)
317
# Note that although the first argument is *called* `using`, it
318
# may actually be a function; @autocommit and @autocommit('foo')
319
# are both allowed forms.
321
using = DEFAULT_DB_ALIAS
323
return inner_commit_on_success(using, DEFAULT_DB_ALIAS)
324
return lambda func: inner_commit_on_success(func, using)
326
def commit_manually(using=None):
256
328
Decorator that activates manual transaction control. It just disables
257
329
automatic transaction control and doesn't do any commit/rollback of its
258
330
own -- it's up to the user to call the commit and rollback functions
261
def _commit_manually(*args, **kw):
263
enter_transaction_management()
265
return func(*args, **kw)
267
leave_transaction_management()
269
return wraps(func)(_commit_manually)
333
def inner_commit_manually(func, db=None):
334
def _commit_manually(*args, **kw):
336
enter_transaction_management(using=db)
337
managed(True, using=db)
338
return func(*args, **kw)
340
leave_transaction_management(using=db)
342
return wraps(func)(_commit_manually)
344
# Note that although the first argument is *called* `using`, it
345
# may actually be a function; @autocommit and @autocommit('foo')
346
# are both allowed forms.
348
using = DEFAULT_DB_ALIAS
350
return inner_commit_manually(using, DEFAULT_DB_ALIAS)
351
return lambda func: inner_commit_manually(func, using)