mirror of
https://github.com/django/django.git
synced 2025-06-08 13:09:13 +00:00
magic-removal: added transaction support to Django! see transactions.txt (in magic-removal) for the details.
git-svn-id: http://code.djangoproject.com/svn/django/branches/magic-removal@2457 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
7901c93328
commit
2a65b00381
@ -1,6 +1,6 @@
|
|||||||
"Daily cleanup file"
|
"Daily cleanup file"
|
||||||
|
|
||||||
from django.db import backend, connection
|
from django.db import backend, connection, transaction
|
||||||
|
|
||||||
DOCUMENTATION_DIRECTORY = '/home/html/documentation/'
|
DOCUMENTATION_DIRECTORY = '/home/html/documentation/'
|
||||||
|
|
||||||
@ -11,7 +11,7 @@ def clean_up():
|
|||||||
(backend.quote_name('core_sessions'), backend.quote_name('expire_date')))
|
(backend.quote_name('core_sessions'), backend.quote_name('expire_date')))
|
||||||
cursor.execute("DELETE FROM %s WHERE %s < NOW() - INTERVAL '1 week'" % \
|
cursor.execute("DELETE FROM %s WHERE %s < NOW() - INTERVAL '1 week'" % \
|
||||||
(backend.quote_name('registration_challenges'), backend.quote_name('request_date')))
|
(backend.quote_name('registration_challenges'), backend.quote_name('request_date')))
|
||||||
connection.commit()
|
transaction.commit_unless_managed()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
clean_up()
|
clean_up()
|
||||||
|
@ -194,6 +194,10 @@ TIME_FORMAT = 'P'
|
|||||||
# http://psyco.sourceforge.net/
|
# http://psyco.sourceforge.net/
|
||||||
ENABLE_PSYCO = False
|
ENABLE_PSYCO = False
|
||||||
|
|
||||||
|
# Do you want to manage transactions manually?
|
||||||
|
# Hint: you really don't!
|
||||||
|
TRANSACTIONS_MANAGED = False
|
||||||
|
|
||||||
##############
|
##############
|
||||||
# MIDDLEWARE #
|
# MIDDLEWARE #
|
||||||
##############
|
##############
|
||||||
|
8
django/core/cache/backends/db.py
vendored
8
django/core/cache/backends/db.py
vendored
@ -1,7 +1,7 @@
|
|||||||
"Database cache backend."
|
"Database cache backend."
|
||||||
|
|
||||||
from django.core.cache.backends.base import BaseCache
|
from django.core.cache.backends.base import BaseCache
|
||||||
from django.db import connection
|
from django.db import connection, transaction
|
||||||
import base64, time
|
import base64, time
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
try:
|
try:
|
||||||
@ -33,7 +33,7 @@ class CacheClass(BaseCache):
|
|||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
if row[2] < now:
|
if row[2] < now:
|
||||||
cursor.execute("DELETE FROM %s WHERE cache_key = %%s" % self._table, [key])
|
cursor.execute("DELETE FROM %s WHERE cache_key = %%s" % self._table, [key])
|
||||||
connection.commit()
|
transaction.commit_unless_managed()
|
||||||
return default
|
return default
|
||||||
return pickle.loads(base64.decodestring(row[1]))
|
return pickle.loads(base64.decodestring(row[1]))
|
||||||
|
|
||||||
@ -58,12 +58,12 @@ class CacheClass(BaseCache):
|
|||||||
# To be threadsafe, updates/inserts are allowed to fail silently
|
# To be threadsafe, updates/inserts are allowed to fail silently
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
connection.commit()
|
transaction.commit_unless_managed()
|
||||||
|
|
||||||
def delete(self, key):
|
def delete(self, key):
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
cursor.execute("DELETE FROM %s WHERE cache_key = %%s" % self._table, [key])
|
cursor.execute("DELETE FROM %s WHERE cache_key = %%s" % self._table, [key])
|
||||||
connection.commit()
|
transaction.commit_unless_managed()
|
||||||
|
|
||||||
def has_key(self, key):
|
def has_key(self, key):
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
|
@ -218,7 +218,7 @@ get_sql_create.args = APP_ARGS
|
|||||||
|
|
||||||
def get_sql_delete(app):
|
def get_sql_delete(app):
|
||||||
"Returns a list of the DROP TABLE SQL statements for the given app."
|
"Returns a list of the DROP TABLE SQL statements for the given app."
|
||||||
from django.db import backend, connection, models
|
from django.db import backend, connection, models, transaction
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
@ -233,7 +233,7 @@ def get_sql_delete(app):
|
|||||||
cursor.execute("SELECT 1 FROM %s LIMIT 1" % backend.quote_name('django_admin_log'))
|
cursor.execute("SELECT 1 FROM %s LIMIT 1" % backend.quote_name('django_admin_log'))
|
||||||
except:
|
except:
|
||||||
# The table doesn't exist, so it doesn't need to be dropped.
|
# The table doesn't exist, so it doesn't need to be dropped.
|
||||||
connection.rollback()
|
transaction.rollback_unless_managed()
|
||||||
admin_log_exists = False
|
admin_log_exists = False
|
||||||
else:
|
else:
|
||||||
admin_log_exists = True
|
admin_log_exists = True
|
||||||
@ -252,7 +252,7 @@ def get_sql_delete(app):
|
|||||||
cursor.execute("SELECT 1 FROM %s LIMIT 1" % backend.quote_name(klass._meta.db_table))
|
cursor.execute("SELECT 1 FROM %s LIMIT 1" % backend.quote_name(klass._meta.db_table))
|
||||||
except:
|
except:
|
||||||
# The table doesn't exist, so it doesn't need to be dropped.
|
# The table doesn't exist, so it doesn't need to be dropped.
|
||||||
connection.rollback()
|
transaction.rollback_unless_managed()
|
||||||
else:
|
else:
|
||||||
opts = klass._meta
|
opts = klass._meta
|
||||||
for f in opts.fields:
|
for f in opts.fields:
|
||||||
@ -268,7 +268,7 @@ def get_sql_delete(app):
|
|||||||
cursor.execute("SELECT 1 FROM %s LIMIT 1" % backend.quote_name(klass._meta.db_table))
|
cursor.execute("SELECT 1 FROM %s LIMIT 1" % backend.quote_name(klass._meta.db_table))
|
||||||
except:
|
except:
|
||||||
# The table doesn't exist, so it doesn't need to be dropped.
|
# The table doesn't exist, so it doesn't need to be dropped.
|
||||||
connection.rollback()
|
transaction.rollback_unless_managed()
|
||||||
else:
|
else:
|
||||||
output.append("DROP TABLE %s;" % backend.quote_name(klass._meta.db_table))
|
output.append("DROP TABLE %s;" % backend.quote_name(klass._meta.db_table))
|
||||||
if backend.supports_constraints and references_to_delete.has_key(klass):
|
if backend.supports_constraints and references_to_delete.has_key(klass):
|
||||||
@ -290,7 +290,7 @@ def get_sql_delete(app):
|
|||||||
if cursor is not None:
|
if cursor is not None:
|
||||||
cursor.execute("SELECT 1 FROM %s LIMIT 1" % backend.quote_name(f.m2m_db_table()))
|
cursor.execute("SELECT 1 FROM %s LIMIT 1" % backend.quote_name(f.m2m_db_table()))
|
||||||
except:
|
except:
|
||||||
connection.rollback()
|
transaction.rollback_unless_managed()
|
||||||
else:
|
else:
|
||||||
output.append("DROP TABLE %s;" % backend.quote_name(f.m2m_db_table()))
|
output.append("DROP TABLE %s;" % backend.quote_name(f.m2m_db_table()))
|
||||||
|
|
||||||
@ -403,7 +403,7 @@ get_sql_all.args = APP_ARGS
|
|||||||
# TODO: Check for model validation errors before executing SQL
|
# TODO: Check for model validation errors before executing SQL
|
||||||
def syncdb():
|
def syncdb():
|
||||||
"Creates the database tables for all apps in INSTALLED_APPS whose tables haven't already been created."
|
"Creates the database tables for all apps in INSTALLED_APPS whose tables haven't already been created."
|
||||||
from django.db import backend, connection, models, get_creation_module, get_introspection_module
|
from django.db import backend, connection, transaction, models, get_creation_module, get_introspection_module
|
||||||
introspection_module = get_introspection_module()
|
introspection_module = get_introspection_module()
|
||||||
data_types = get_creation_module().DATA_TYPES
|
data_types = get_creation_module().DATA_TYPES
|
||||||
|
|
||||||
@ -470,7 +470,7 @@ def syncdb():
|
|||||||
backend.quote_name(r_col), backend.quote_name(table), backend.quote_name(col))
|
backend.quote_name(r_col), backend.quote_name(table), backend.quote_name(col))
|
||||||
cursor.execute(sql)
|
cursor.execute(sql)
|
||||||
|
|
||||||
connection.commit()
|
transaction.commit_unless_managed()
|
||||||
syncdb.args = ''
|
syncdb.args = ''
|
||||||
|
|
||||||
def get_admin_index(app):
|
def get_admin_index(app):
|
||||||
@ -499,7 +499,7 @@ get_admin_index.args = APP_ARGS
|
|||||||
|
|
||||||
def install(app):
|
def install(app):
|
||||||
"Executes the equivalent of 'get_sql_all' in the current database."
|
"Executes the equivalent of 'get_sql_all' in the current database."
|
||||||
from django.db import connection
|
from django.db import connection, transaction
|
||||||
from cStringIO import StringIO
|
from cStringIO import StringIO
|
||||||
app_name = app.__name__[app.__name__.rindex('.')+1:]
|
app_name = app.__name__[app.__name__.rindex('.')+1:]
|
||||||
app_label = app_name.split('.')[-1]
|
app_label = app_name.split('.')[-1]
|
||||||
@ -526,15 +526,15 @@ def install(app):
|
|||||||
Hint: Look at the output of 'django-admin.py sqlall %s'. That's the SQL this command wasn't able to run.
|
Hint: Look at the output of 'django-admin.py sqlall %s'. That's the SQL this command wasn't able to run.
|
||||||
The full error: %s\n""" % \
|
The full error: %s\n""" % \
|
||||||
(app_name, app_label, e))
|
(app_name, app_label, e))
|
||||||
connection.rollback()
|
transaction.rollback_unless_managed()
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
connection.commit()
|
transaction.commit_unless_managed()
|
||||||
install.help_doc = "Executes ``sqlall`` for the given app(s) in the current database."
|
install.help_doc = "Executes ``sqlall`` for the given app(s) in the current database."
|
||||||
install.args = APP_ARGS
|
install.args = APP_ARGS
|
||||||
|
|
||||||
def reset(app):
|
def reset(app):
|
||||||
"Executes the equivalent of 'get_sql_reset' in the current database."
|
"Executes the equivalent of 'get_sql_reset' in the current database."
|
||||||
from django.db import connection
|
from django.db import connection, transaction
|
||||||
from cStringIO import StringIO
|
from cStringIO import StringIO
|
||||||
app_name = app.__name__[app.__name__.rindex('.')+1:]
|
app_name = app.__name__[app.__name__.rindex('.')+1:]
|
||||||
app_label = app_name.split('.')[-1]
|
app_label = app_name.split('.')[-1]
|
||||||
@ -568,9 +568,9 @@ Type 'yes' to continue, or 'no' to cancel: """)
|
|||||||
Hint: Look at the output of 'django-admin.py sqlreset %s'. That's the SQL this command wasn't able to run.
|
Hint: Look at the output of 'django-admin.py sqlreset %s'. That's the SQL this command wasn't able to run.
|
||||||
The full error: %s\n""" % \
|
The full error: %s\n""" % \
|
||||||
(app_name, app_label, e))
|
(app_name, app_label, e))
|
||||||
connection.rollback()
|
transaction.rollback_unless_managed()
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
connection.commit()
|
transaction.commit_unless_managed()
|
||||||
else:
|
else:
|
||||||
print "Reset cancelled."
|
print "Reset cancelled."
|
||||||
reset.help_doc = "Executes ``sqlreset`` for the given app(s) in the current database."
|
reset.help_doc = "Executes ``sqlreset`` for the given app(s) in the current database."
|
||||||
@ -1035,7 +1035,7 @@ runserver.args = '[optional port number, or ipaddr:port]'
|
|||||||
|
|
||||||
def createcachetable(tablename):
|
def createcachetable(tablename):
|
||||||
"Creates the table needed to use the SQL cache backend"
|
"Creates the table needed to use the SQL cache backend"
|
||||||
from django.db import backend, get_creation_module, models
|
from django.db import backend, connection, transaction, get_creation_module, models
|
||||||
data_types = get_creation_module().DATA_TYPES
|
data_types = get_creation_module().DATA_TYPES
|
||||||
fields = (
|
fields = (
|
||||||
# "key" is a reserved word in MySQL, so use "cache_key" instead.
|
# "key" is a reserved word in MySQL, so use "cache_key" instead.
|
||||||
@ -1066,7 +1066,7 @@ def createcachetable(tablename):
|
|||||||
curs.execute("\n".join(full_statement))
|
curs.execute("\n".join(full_statement))
|
||||||
for statement in index_output:
|
for statement in index_output:
|
||||||
curs.execute(statement)
|
curs.execute(statement)
|
||||||
connection.commit()
|
transaction.commit_unless_managed()
|
||||||
createcachetable.args = "[tablename]"
|
createcachetable.args = "[tablename]"
|
||||||
|
|
||||||
def run_shell(use_plain=False):
|
def run_shell(use_plain=False):
|
||||||
|
@ -38,4 +38,7 @@ dispatcher.connect(reset_queries, signal=signals.request_started)
|
|||||||
|
|
||||||
# Register an event that rolls back the connection
|
# Register an event that rolls back the connection
|
||||||
# when a Django request has an exception.
|
# when a Django request has an exception.
|
||||||
dispatcher.connect(lambda: connection.rollback(), signal=signals.got_request_exception)
|
def _rollback_on_exception():
|
||||||
|
from django.db import transaction
|
||||||
|
transaction.rollback_unless_managed()
|
||||||
|
dispatcher.connect(_rollback_on_exception, signal=signals.got_request_exception)
|
||||||
|
@ -64,10 +64,10 @@ class DatabaseWrapper:
|
|||||||
return base.CursorDebugWrapper(cursor, self)
|
return base.CursorDebugWrapper(cursor, self)
|
||||||
return cursor
|
return cursor
|
||||||
|
|
||||||
def commit(self):
|
def _commit(self):
|
||||||
return self.connection.commit()
|
return self.connection.commit()
|
||||||
|
|
||||||
def rollback(self):
|
def _rollback(self):
|
||||||
if self.connection:
|
if self.connection:
|
||||||
return self.connection.rollback()
|
return self.connection.rollback()
|
||||||
|
|
||||||
|
@ -17,8 +17,8 @@ class DatabaseError(Exception):
|
|||||||
|
|
||||||
class DatabaseWrapper:
|
class DatabaseWrapper:
|
||||||
cursor = complain
|
cursor = complain
|
||||||
commit = complain
|
_commit = complain
|
||||||
rollback = complain
|
_rollback = complain
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
pass # close()
|
pass # close()
|
||||||
|
@ -71,10 +71,10 @@ class DatabaseWrapper:
|
|||||||
return util.CursorDebugWrapper(MysqlDebugWrapper(cursor), self)
|
return util.CursorDebugWrapper(MysqlDebugWrapper(cursor), self)
|
||||||
return cursor
|
return cursor
|
||||||
|
|
||||||
def commit(self):
|
def _commit(self):
|
||||||
self.connection.commit()
|
self.connection.commit()
|
||||||
|
|
||||||
def rollback(self):
|
def _rollback(self):
|
||||||
if self.connection:
|
if self.connection:
|
||||||
try:
|
try:
|
||||||
self.connection.rollback()
|
self.connection.rollback()
|
||||||
|
@ -37,10 +37,10 @@ class DatabaseWrapper:
|
|||||||
return util.CursorDebugWrapper(cursor, self)
|
return util.CursorDebugWrapper(cursor, self)
|
||||||
return cursor
|
return cursor
|
||||||
|
|
||||||
def commit(self):
|
def _commit(self):
|
||||||
return self.connection.commit()
|
return self.connection.commit()
|
||||||
|
|
||||||
def rollback(self):
|
def _rollback(self):
|
||||||
if self.connection:
|
if self.connection:
|
||||||
return self.connection.rollback()
|
return self.connection.rollback()
|
||||||
|
|
||||||
|
@ -39,10 +39,10 @@ class DatabaseWrapper:
|
|||||||
else:
|
else:
|
||||||
return cursor
|
return cursor
|
||||||
|
|
||||||
def commit(self):
|
def _commit(self):
|
||||||
self.connection.commit()
|
self.connection.commit()
|
||||||
|
|
||||||
def rollback(self):
|
def _rollback(self):
|
||||||
if self.connection:
|
if self.connection:
|
||||||
self.connection.rollback()
|
self.connection.rollback()
|
||||||
|
|
||||||
@ -67,7 +67,6 @@ class SQLiteCursorWrapper(Database.Cursor):
|
|||||||
return Database.Cursor.executemany(self, query, params)
|
return Database.Cursor.executemany(self, query, params)
|
||||||
|
|
||||||
def convert_query(self, query, num_params):
|
def convert_query(self, query, num_params):
|
||||||
# XXX this seems too simple to be correct... is this right?
|
|
||||||
return query % tuple("?" * num_params)
|
return query % tuple("?" * num_params)
|
||||||
|
|
||||||
supports_constraints = False
|
supports_constraints = False
|
||||||
|
@ -5,7 +5,7 @@ from django.db.models.fields.related import OneToOne, ManyToOne
|
|||||||
from django.db.models.related import RelatedObject
|
from django.db.models.related import RelatedObject
|
||||||
from django.db.models.query import orderlist2sql, delete_objects
|
from django.db.models.query import orderlist2sql, delete_objects
|
||||||
from django.db.models.options import Options, AdminOptions
|
from django.db.models.options import Options, AdminOptions
|
||||||
from django.db import connection, backend
|
from django.db import connection, backend, transaction
|
||||||
from django.db.models import signals
|
from django.db.models import signals
|
||||||
from django.db.models.loading import register_models
|
from django.db.models.loading import register_models
|
||||||
from django.dispatch import dispatcher
|
from django.dispatch import dispatcher
|
||||||
@ -184,7 +184,7 @@ class Model(object):
|
|||||||
','.join(placeholders)), db_values)
|
','.join(placeholders)), db_values)
|
||||||
if self._meta.has_auto_field and not pk_set:
|
if self._meta.has_auto_field and not pk_set:
|
||||||
setattr(self, self._meta.pk.attname, backend.get_last_insert_id(cursor, self._meta.db_table, self._meta.pk.column))
|
setattr(self, self._meta.pk.attname, backend.get_last_insert_id(cursor, self._meta.db_table, self._meta.pk.column))
|
||||||
connection.commit()
|
transaction.commit_unless_managed()
|
||||||
|
|
||||||
# Run any post-save hooks.
|
# Run any post-save hooks.
|
||||||
dispatcher.send(signal=signals.post_save, sender=self.__class__, instance=self)
|
dispatcher.send(signal=signals.post_save, sender=self.__class__, instance=self)
|
||||||
@ -340,7 +340,7 @@ class Model(object):
|
|||||||
backend.quote_name(rel_field.m2m_column_name()),
|
backend.quote_name(rel_field.m2m_column_name()),
|
||||||
backend.quote_name(rel_field.m2m_reverse_name()))
|
backend.quote_name(rel_field.m2m_reverse_name()))
|
||||||
cursor.executemany(sql, [(this_id, i) for i in id_list])
|
cursor.executemany(sql, [(this_id, i) for i in id_list])
|
||||||
connection.commit()
|
transaction.commit_unless_managed()
|
||||||
|
|
||||||
############################################
|
############################################
|
||||||
# HELPER FUNCTIONS (CURRIED MODEL METHODS) #
|
# HELPER FUNCTIONS (CURRIED MODEL METHODS) #
|
||||||
@ -357,7 +357,7 @@ def method_set_order(ordered_obj, self, id_list):
|
|||||||
backend.quote_name(ordered_obj.pk.column))
|
backend.quote_name(ordered_obj.pk.column))
|
||||||
rel_val = getattr(self, ordered_obj.order_with_respect_to.rel.field_name)
|
rel_val = getattr(self, ordered_obj.order_with_respect_to.rel.field_name)
|
||||||
cursor.executemany(sql, [(i, rel_val, j) for i, j in enumerate(id_list)])
|
cursor.executemany(sql, [(i, rel_val, j) for i, j in enumerate(id_list)])
|
||||||
connection.commit()
|
transaction.commit_unless_managed()
|
||||||
|
|
||||||
def method_get_order(ordered_obj, self):
|
def method_get_order(ordered_obj, self):
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from django.db import backend, connection
|
from django.db import backend, connection, transaction
|
||||||
from django.db.models import signals
|
from django.db.models import signals
|
||||||
from django.db.models.fields import AutoField, Field, IntegerField
|
from django.db.models.fields import AutoField, Field, IntegerField
|
||||||
from django.db.models.related import RelatedObject
|
from django.db.models.related import RelatedObject
|
||||||
@ -231,7 +231,7 @@ def _add_m2m_items(rel_manager_inst, managerclass, rel_model, join_table, source
|
|||||||
cursor.execute("INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \
|
cursor.execute("INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \
|
||||||
(join_table, source_col_name, target_col_name),
|
(join_table, source_col_name, target_col_name),
|
||||||
[source_pk_val, obj_id])
|
[source_pk_val, obj_id])
|
||||||
connection.commit()
|
transaction.commit_unless_managed()
|
||||||
|
|
||||||
def _remove_m2m_items(rel_model, join_table, source_col_name,
|
def _remove_m2m_items(rel_model, join_table, source_col_name,
|
||||||
target_col_name, source_pk_val, *objs):
|
target_col_name, source_pk_val, *objs):
|
||||||
@ -255,7 +255,7 @@ def _remove_m2m_items(rel_model, join_table, source_col_name,
|
|||||||
cursor.execute("DELETE FROM %s WHERE %s = %%s AND %s = %%s" % \
|
cursor.execute("DELETE FROM %s WHERE %s = %%s AND %s = %%s" % \
|
||||||
(join_table, source_col_name, target_col_name),
|
(join_table, source_col_name, target_col_name),
|
||||||
[source_pk_val, obj._get_pk_val()])
|
[source_pk_val, obj._get_pk_val()])
|
||||||
connection.commit()
|
transaction.commit_unless_managed()
|
||||||
|
|
||||||
def _clear_m2m_items(join_table, source_col_name, source_pk_val):
|
def _clear_m2m_items(join_table, source_col_name, source_pk_val):
|
||||||
# Utility function used by the ManyRelatedObjectsDescriptors
|
# Utility function used by the ManyRelatedObjectsDescriptors
|
||||||
@ -268,7 +268,7 @@ def _clear_m2m_items(join_table, source_col_name, source_pk_val):
|
|||||||
cursor.execute("DELETE FROM %s WHERE %s = %%s" % \
|
cursor.execute("DELETE FROM %s WHERE %s = %%s" % \
|
||||||
(join_table, source_col_name),
|
(join_table, source_col_name),
|
||||||
[source_pk_val])
|
[source_pk_val])
|
||||||
connection.commit()
|
transaction.commit_unless_managed()
|
||||||
|
|
||||||
class ManyRelatedObjectsDescriptor(object):
|
class ManyRelatedObjectsDescriptor(object):
|
||||||
# This class provides the functionality that makes the related-object
|
# This class provides the functionality that makes the related-object
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from django.db import backend, connection
|
from django.db import backend, connection, transaction
|
||||||
from django.db.models.fields import DateField, FieldDoesNotExist
|
from django.db.models.fields import DateField, FieldDoesNotExist
|
||||||
from django.db.models import signals
|
from django.db.models import signals
|
||||||
from django.dispatch import dispatcher
|
from django.dispatch import dispatcher
|
||||||
@ -846,4 +846,4 @@ def delete_objects(seen_objs):
|
|||||||
setattr(instance, cls._meta.pk.attname, None)
|
setattr(instance, cls._meta.pk.attname, None)
|
||||||
dispatcher.send(signal=signals.post_delete, sender=cls, instance=instance)
|
dispatcher.send(signal=signals.post_delete, sender=cls, instance=instance)
|
||||||
|
|
||||||
connection.commit()
|
transaction.commit_unless_managed()
|
||||||
|
225
django/db/transaction.py
Normal file
225
django/db/transaction.py
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
"""
|
||||||
|
This module implements a transaction manager that can be used to define
|
||||||
|
transaction handling in a request or view function. It is used by transaction
|
||||||
|
control middleware and decorators.
|
||||||
|
|
||||||
|
The transaction manager can be in managed or in auto state. Auto state means the
|
||||||
|
system is using a commit-on-save strategy (actually it's more like
|
||||||
|
commit-on-change). As soon as the .save() or .delete() (or related) methods are
|
||||||
|
called, a commit is made.
|
||||||
|
|
||||||
|
Managed transactions don't do those commits, but will need some kind of manual
|
||||||
|
or implicit commits or rollbacks.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import thread
|
||||||
|
from django.db import connection
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
class TransactionManagementError(Exception):
|
||||||
|
"""
|
||||||
|
This is the exception that is thrown when
|
||||||
|
something bad happens with transaction management.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
# The state is a dictionary of lists. The key to the dict is the current
|
||||||
|
# thread and the list is handled as a stack of values.
|
||||||
|
state = {}
|
||||||
|
|
||||||
|
# The dirty flag is set by *_unless_managed functions to denote that the
|
||||||
|
# code under transaction management has changed things to require a
|
||||||
|
# database commit.
|
||||||
|
dirty = {}
|
||||||
|
|
||||||
|
def enter_transaction_management():
|
||||||
|
"""
|
||||||
|
Enters transaction management for a running thread. It must be balanced with
|
||||||
|
the appropriate leave_transaction_management call, since the actual state is
|
||||||
|
managed as a stack.
|
||||||
|
|
||||||
|
The state and dirty flag are carried over from the surrounding block or
|
||||||
|
from the settings, if there is no surrounding block (dirty is allways false
|
||||||
|
when no current block is running).
|
||||||
|
"""
|
||||||
|
thread_ident = thread.get_ident()
|
||||||
|
if state.has_key(thread_ident) and state[thread_ident]:
|
||||||
|
state[thread_ident].append(state[thread_ident][-1])
|
||||||
|
else:
|
||||||
|
state[thread_ident] = []
|
||||||
|
state[thread_ident].append(settings.TRANSACTIONS_MANAGED)
|
||||||
|
if not dirty.has_key(thread_ident):
|
||||||
|
dirty[thread_ident] = False
|
||||||
|
|
||||||
|
def leave_transaction_management():
|
||||||
|
"""
|
||||||
|
Leaves transaction management for a running thread. A dirty flag is carried
|
||||||
|
over to the surrounding block, as a commit will commit all changes, even
|
||||||
|
those from outside (commits are on connection level).
|
||||||
|
"""
|
||||||
|
thread_ident = thread.get_ident()
|
||||||
|
if state.has_key(thread_ident) and state[thread_ident]:
|
||||||
|
del state[thread_ident][-1]
|
||||||
|
else:
|
||||||
|
raise TransactionManagementError("This code isn't under transaction management")
|
||||||
|
if dirty.get(thread_ident, False):
|
||||||
|
# I fixed it for you this time, but don't do it again!
|
||||||
|
rollback()
|
||||||
|
raise TransactionManagementError("Transaction managed block ended with pending COMMIT/ROLLBACK")
|
||||||
|
dirty[thread_ident] = False
|
||||||
|
|
||||||
|
def is_dirty():
|
||||||
|
"""
|
||||||
|
Checks if the current transaction requires a commit for changes to happen.
|
||||||
|
"""
|
||||||
|
return dirty.get(thread.get_ident(), False)
|
||||||
|
|
||||||
|
def set_dirty():
|
||||||
|
"""
|
||||||
|
Sets a dirty flag for the current thread and code streak. This can be used
|
||||||
|
to decide in a managed block of code to decide whether there are open
|
||||||
|
changes waiting for commit.
|
||||||
|
"""
|
||||||
|
thread_ident = thread.get_ident()
|
||||||
|
if dirty.has_key(thread_ident):
|
||||||
|
dirty[thread_ident] = True
|
||||||
|
else:
|
||||||
|
raise TransactionManagementError("This code isn't under transaction management")
|
||||||
|
|
||||||
|
def set_clean():
|
||||||
|
"""
|
||||||
|
Resets a dirty flag for the current thread and code streak. This can be used
|
||||||
|
to decide in a managed block of code to decide whether there should happen a
|
||||||
|
commit or rollback.
|
||||||
|
"""
|
||||||
|
thread_ident = thread.get_ident()
|
||||||
|
if dirty.has_key(thread_ident):
|
||||||
|
dirty[thread_ident] = False
|
||||||
|
else:
|
||||||
|
raise TransactionManagementError("This code isn't under transaction management")
|
||||||
|
|
||||||
|
def is_managed():
|
||||||
|
"""
|
||||||
|
Checks whether the transaction manager is in manual or in auto state.
|
||||||
|
"""
|
||||||
|
thread_ident = thread.get_ident()
|
||||||
|
if state.has_key(thread_ident):
|
||||||
|
if state[thread_ident]:
|
||||||
|
return state[thread_ident][-1]
|
||||||
|
return settings.TRANSACTIONS_MANAGED
|
||||||
|
|
||||||
|
def managed(flag=True):
|
||||||
|
"""
|
||||||
|
Puts the transaction manager into a manual state - managed transactions have
|
||||||
|
to be committed explicitely by the user. If you switch off transaction
|
||||||
|
management and there is a pending commit/rollback, the data will be
|
||||||
|
commited.
|
||||||
|
"""
|
||||||
|
thread_ident = thread.get_ident()
|
||||||
|
top = state.get(thread_ident, None)
|
||||||
|
if top:
|
||||||
|
top[-1] = flag
|
||||||
|
if not flag and is_dirty():
|
||||||
|
connection._commit()
|
||||||
|
set_clean()
|
||||||
|
else:
|
||||||
|
raise TransactionManagementError("This code isn't under transaction management")
|
||||||
|
|
||||||
|
def commit_unless_managed():
|
||||||
|
"""
|
||||||
|
Commits changes if the system is not in managed transaction mode.
|
||||||
|
"""
|
||||||
|
if not is_managed():
|
||||||
|
connection._commit()
|
||||||
|
else:
|
||||||
|
set_dirty()
|
||||||
|
|
||||||
|
def rollback_unless_managed():
|
||||||
|
"""
|
||||||
|
Rolls back changes if the system is not in managed transaction mode.
|
||||||
|
"""
|
||||||
|
if not is_managed():
|
||||||
|
connection._rollback()
|
||||||
|
else:
|
||||||
|
set_dirty()
|
||||||
|
|
||||||
|
def commit():
|
||||||
|
"""
|
||||||
|
Does the commit itself and resets the dirty flag.
|
||||||
|
"""
|
||||||
|
connection._commit()
|
||||||
|
set_clean()
|
||||||
|
|
||||||
|
def rollback():
|
||||||
|
"""
|
||||||
|
This function does the rollback itself and resets the dirty flag.
|
||||||
|
"""
|
||||||
|
connection._rollback()
|
||||||
|
set_clean()
|
||||||
|
|
||||||
|
##############
|
||||||
|
# DECORATORS #
|
||||||
|
##############
|
||||||
|
|
||||||
|
def autocommit(func):
|
||||||
|
"""
|
||||||
|
Decorator that activates commit on save. This is Django's default behavour;
|
||||||
|
this decorator is useful if you globally activated transaction management in
|
||||||
|
your settings file and want the default behaviour in some view functions.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def _autocommit(*args, **kw):
|
||||||
|
try:
|
||||||
|
enter_transaction_management()
|
||||||
|
managed(False)
|
||||||
|
return func(*args, **kw)
|
||||||
|
finally:
|
||||||
|
leave_transaction_management()
|
||||||
|
|
||||||
|
return _autocommit
|
||||||
|
|
||||||
|
def commit_on_success(func):
|
||||||
|
"""
|
||||||
|
This decorator activates commit on response. This way if the viewfunction
|
||||||
|
runs successfully, a commit is made, if the viewfunc produces an exception,
|
||||||
|
a rollback is made. This is one of the most common ways to do transaction
|
||||||
|
control in web apps.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def _commit_on_success(*args, **kw):
|
||||||
|
try:
|
||||||
|
enter_transaction_management()
|
||||||
|
managed(True)
|
||||||
|
try:
|
||||||
|
res = func(*args, **kw)
|
||||||
|
except Exception, e:
|
||||||
|
if is_dirty():
|
||||||
|
rollback()
|
||||||
|
raise e
|
||||||
|
else:
|
||||||
|
if is_dirty():
|
||||||
|
commit()
|
||||||
|
return res
|
||||||
|
finally:
|
||||||
|
leave_transaction_management()
|
||||||
|
|
||||||
|
return _commit_on_success
|
||||||
|
|
||||||
|
def commit_manually(func):
|
||||||
|
"""
|
||||||
|
Decorator that activates manual transaction control. It just disables
|
||||||
|
automatic transaction control and doesn't do any commit/rollback of it's own
|
||||||
|
- it's up to the user to call the commit and rollback functions themselves.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def _commit_manually(*args, **kw):
|
||||||
|
try:
|
||||||
|
enter_transaction_management()
|
||||||
|
managed(True)
|
||||||
|
return func(*args, **kw)
|
||||||
|
finally:
|
||||||
|
leave_transaction_management()
|
||||||
|
|
||||||
|
return _commit_manually
|
||||||
|
|
||||||
|
|
29
django/middleware/transaction.py
Normal file
29
django/middleware/transaction.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
from django.conf import settings
|
||||||
|
from django.db import transaction
|
||||||
|
|
||||||
|
class TransactionMiddleware:
|
||||||
|
"""
|
||||||
|
Transaction middleware. If this is enabled, each view function will be run
|
||||||
|
with commit_on_response activated - that way a save() doesn't do a direct
|
||||||
|
commit, the commit is done when a successfull response is created. If an
|
||||||
|
exception happens, the database is rolled back.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def process_request(self, request):
|
||||||
|
"""Enters transaction management"""
|
||||||
|
transaction.enter_transaction_management()
|
||||||
|
transaction.managed(True)
|
||||||
|
|
||||||
|
def process_exception(self, request, exception):
|
||||||
|
"""Rolls back the database and leaves transaction management"""
|
||||||
|
if transaction.is_dirty():
|
||||||
|
transaction.rollback()
|
||||||
|
transaction.leave_transaction_management()
|
||||||
|
|
||||||
|
def process_response(self, request, response):
|
||||||
|
"""Commits and leaves transaction management."""
|
||||||
|
if transaction.is_managed():
|
||||||
|
if transaction.is_dirty():
|
||||||
|
transaction.commit()
|
||||||
|
transaction.leave_transaction_management()
|
||||||
|
return response
|
@ -102,6 +102,23 @@ Enables session support. See the `session documentation`_.
|
|||||||
|
|
||||||
.. _`session documentation`: http://www.djangoproject.com/documentation/sessions/
|
.. _`session documentation`: http://www.djangoproject.com/documentation/sessions/
|
||||||
|
|
||||||
|
django.middleware.transaction.TransactionMiddleware
|
||||||
|
---------------------------------------------------
|
||||||
|
|
||||||
|
Binds commit and rollback to the request/response phase. If a view function runs
|
||||||
|
successfully, a commit is done. If it fails with an exception, a rollback is
|
||||||
|
done.
|
||||||
|
|
||||||
|
The order of this middleware in the stack is important: middleware modules
|
||||||
|
running outside of it run with commit-on-save - the default Django behavior.
|
||||||
|
Middleware modules running inside it (coming later in the stack) will be under
|
||||||
|
the same transaction control as the view functions.
|
||||||
|
|
||||||
|
See the `transaction management documentation`_.
|
||||||
|
|
||||||
|
.. _`transaction management documentation`: http://www.djangoproject.com/documentation/transaction/
|
||||||
|
|
||||||
|
|
||||||
Writing your own middleware
|
Writing your own middleware
|
||||||
===========================
|
===========================
|
||||||
|
|
||||||
|
138
docs/transactions.txt
Normal file
138
docs/transactions.txt
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
==============================
|
||||||
|
Managing database transactions
|
||||||
|
==============================
|
||||||
|
|
||||||
|
Django gives you a few ways to control how database transactions are managed.
|
||||||
|
|
||||||
|
Django's default transaction behavior
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
The default behavior of Django is to commit on special model functions. If you
|
||||||
|
call ``model.save()`` or ``model.delete()``, that change will be committed immediately.
|
||||||
|
|
||||||
|
This is much like the auto-commit setting for most databases: as soon as you
|
||||||
|
perform an action that needs to write to the database, Django produces the
|
||||||
|
insert/update/delete statements and then does the commit. There is no implicit
|
||||||
|
rollback in Django.
|
||||||
|
|
||||||
|
Tying transactions to HTTP requests
|
||||||
|
===================================
|
||||||
|
|
||||||
|
A useful way to handle transactions is to tie them to the request and response
|
||||||
|
phases.
|
||||||
|
|
||||||
|
When a request starts, you start a transaction. If the response is produced
|
||||||
|
without problems, any transactions are committed. If the view function produces
|
||||||
|
and exception, a rollback happens. This is one of the more intuitive ways to
|
||||||
|
handle transactions. To activate this feature, just add the
|
||||||
|
``TransactionMiddleware`` middleware to your stack::
|
||||||
|
|
||||||
|
MIDDLEWARE_CLASSES = (
|
||||||
|
"django.middleware.common.CommonMiddleware",
|
||||||
|
"django.middleware.sessions.SessionMiddleware",
|
||||||
|
"django.middleware.cache.CacheMiddleware",
|
||||||
|
"django.middleware.transaction.TransactionMiddleware",
|
||||||
|
)
|
||||||
|
|
||||||
|
The order is quite important: the transaction middleware will be relevant not
|
||||||
|
only for the view functions called, but for all middleware modules that come
|
||||||
|
after it. So if you use the session middleware after the transaction middleware,
|
||||||
|
session creation will be part of the transaction.
|
||||||
|
|
||||||
|
The cache middleware isn't affected as it uses it's own database cursor (that is
|
||||||
|
mapped to it's own database connection internally) and only the database based
|
||||||
|
cache is affected.
|
||||||
|
|
||||||
|
Controlling transaction management in views
|
||||||
|
===========================================
|
||||||
|
|
||||||
|
For many people, implicit request-based transactions will work wonderfully.
|
||||||
|
However, if you need to control the way that transactions are managed,
|
||||||
|
there are a set of decorators that you can apply to a function to change
|
||||||
|
the way transactions are handled.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Although the examples below use view functions as examples, these
|
||||||
|
decorators can be applied to non-view functions as well.
|
||||||
|
|
||||||
|
``autocommit``
|
||||||
|
--------------
|
||||||
|
|
||||||
|
You can use the ``autocommit`` decorator to switch a view function to the
|
||||||
|
default commit behavior of Django, regardless of the global setting. Just use
|
||||||
|
the decorator like this::
|
||||||
|
|
||||||
|
from django.db.transaction import autocommit
|
||||||
|
|
||||||
|
@transaction.autocommit
|
||||||
|
def viewfunc(request):
|
||||||
|
....
|
||||||
|
|
||||||
|
Within ``viewfunc`` transactions will be comitted as soon as you call
|
||||||
|
``model.save()``, ``model.delete()``, or any similar function that writes to the
|
||||||
|
database.
|
||||||
|
|
||||||
|
``commit_on_success``
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
You can use the ``commit_on_success`` decorator to use a single transaction for
|
||||||
|
all the work done in a function::
|
||||||
|
|
||||||
|
from django.db.transaction import commit_on_success
|
||||||
|
|
||||||
|
@commit_on_success
|
||||||
|
def viewfunc(request):
|
||||||
|
....
|
||||||
|
|
||||||
|
If the function returns successfully then all work done will be committed. If an
|
||||||
|
exception is raised beyond the function, however, the transaction will be rolled
|
||||||
|
back.
|
||||||
|
|
||||||
|
``commit_manually``
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Sometimes you need full control over your transactions. In that case, you can use the
|
||||||
|
``commit_manually`` decorator which will make you run your own transaction management.
|
||||||
|
|
||||||
|
If you don't commit or rollback and did change data (so that the current transaction
|
||||||
|
is marked as dirty), you will get a ``TransactionManagementError`` exception saying so.
|
||||||
|
|
||||||
|
Manual transaction management looks like::
|
||||||
|
|
||||||
|
from django.db import transaction
|
||||||
|
|
||||||
|
@transaction.commit_manually
|
||||||
|
def viewfunc(request):
|
||||||
|
...
|
||||||
|
transaction.commit()
|
||||||
|
...
|
||||||
|
try:
|
||||||
|
...
|
||||||
|
except:
|
||||||
|
transaction.rollback()
|
||||||
|
else:
|
||||||
|
transaction.commit()
|
||||||
|
|
||||||
|
..admonition:: An important note to users of earlier django releases:
|
||||||
|
|
||||||
|
The database ``connection.commit`` and ``connection.rollback`` functions
|
||||||
|
(also called ``db.commit`` and ``db.rollback`` in 0.91 and earlier), no
|
||||||
|
longer exist and have been replaced by the ``transaction.commit`` and
|
||||||
|
``transaction.rollback`` commands.
|
||||||
|
|
||||||
|
How to globally deactivate transaction management
|
||||||
|
=================================================
|
||||||
|
|
||||||
|
Control freaks can totally disable all transaction management by setting
|
||||||
|
``DISABLE_TRANSACTION_MANAGEMENT`` to ``True`` in your settings file.
|
||||||
|
|
||||||
|
If you do this, there will be no management whatsoever. The middleware will no
|
||||||
|
longer implicitly commit transactions, and you'll need to roll management
|
||||||
|
yourself. This even will require you to commit changes done by middleware
|
||||||
|
somewhere else.
|
||||||
|
|
||||||
|
Thus, this is best used in situations where you want to run your own transaction
|
||||||
|
controlling middleware or do something really strange. In almost all situations
|
||||||
|
you'll be better off using the default behavior or the transaction middleware
|
||||||
|
and only modify selected functions as needed.
|
0
tests/modeltests/transactions/__init__.py
Normal file
0
tests/modeltests/transactions/__init__.py
Normal file
91
tests/modeltests/transactions/models.py
Normal file
91
tests/modeltests/transactions/models.py
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
"""
|
||||||
|
XXX. Transactions
|
||||||
|
|
||||||
|
Django handles transactions in three different ways. The default is to commit
|
||||||
|
each transaction upon a write, but you can decorate a function to get
|
||||||
|
commit-on-sucess behavior, or else you can manage the transaction manually.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class Reporter(models.Model):
|
||||||
|
first_name = models.CharField(maxlength=30)
|
||||||
|
last_name = models.CharField(maxlength=30)
|
||||||
|
email = models.EmailField()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "%s %s" % (self.first_name, self.last_name)
|
||||||
|
|
||||||
|
API_TESTS = """
|
||||||
|
>>> from django.db import connection, transaction
|
||||||
|
|
||||||
|
# the default behavior is to autocommit after each save() action
|
||||||
|
>>> def create_a_reporter_then_fail(first, last):
|
||||||
|
... a = Reporter(first_name=first, last_name=last)
|
||||||
|
... a.save()
|
||||||
|
... raise Exception("I meant to do that")
|
||||||
|
...
|
||||||
|
>>> create_a_reporter_then_fail("Alice", "Smith")
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
Exception: I meant to do that
|
||||||
|
|
||||||
|
# The object created before the exception still exists
|
||||||
|
>>> Reporter.objects.all()
|
||||||
|
[Alice Smith]
|
||||||
|
|
||||||
|
# the autocommit decorator works exactly the same as the default behavior
|
||||||
|
>>> autocomitted_create_then_fail = transaction.autocommit(create_a_reporter_then_fail)
|
||||||
|
>>> autocomitted_create_then_fail("Ben", "Jones")
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
Exception: I meant to do that
|
||||||
|
|
||||||
|
# Same behavior as before
|
||||||
|
>>> Reporter.objects.all()
|
||||||
|
[Alice Smith, Ben Jones]
|
||||||
|
|
||||||
|
# With the commit_on_success decorator, the transaction is only comitted if the
|
||||||
|
# function doesn't throw an exception
|
||||||
|
>>> committed_on_success = transaction.commit_on_success(create_a_reporter_then_fail)
|
||||||
|
>>> committed_on_success("Carol", "Doe")
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
Exception: I meant to do that
|
||||||
|
|
||||||
|
# This time the object never got saved
|
||||||
|
>>> Reporter.objects.all()
|
||||||
|
[Alice Smith, Ben Jones]
|
||||||
|
|
||||||
|
# If there aren't any exceptions, the data will get saved
|
||||||
|
>>> def remove_a_reporter():
|
||||||
|
... r = Reporter.objects.get(first_name="Alice")
|
||||||
|
... r.delete()
|
||||||
|
...
|
||||||
|
>>> remove_comitted_on_success = transaction.commit_on_success(remove_a_reporter)
|
||||||
|
>>> remove_comitted_on_success()
|
||||||
|
>>> Reporter.objects.all()
|
||||||
|
[Ben Jones]
|
||||||
|
|
||||||
|
# You can manually manage transactions if you really want to, but you
|
||||||
|
# have to remember to commit/rollback
|
||||||
|
>>> def manually_managed():
|
||||||
|
... r = Reporter(first_name="Carol", last_name="Doe")
|
||||||
|
... r.save()
|
||||||
|
... transaction.commit()
|
||||||
|
>>> manually_managed = transaction.commit_manually(manually_managed)
|
||||||
|
>>> manually_managed()
|
||||||
|
>>> Reporter.objects.all()
|
||||||
|
[Ben Jones, Carol Doe]
|
||||||
|
|
||||||
|
# If you forget, you'll get bad errors
|
||||||
|
>>> def manually_managed_mistake():
|
||||||
|
... r = Reporter(first_name="David", last_name="Davidson")
|
||||||
|
... r.save()
|
||||||
|
... # oops, I forgot to commit/rollback!
|
||||||
|
>>> manually_managed_mistake = transaction.commit_manually(manually_managed_mistake)
|
||||||
|
>>> manually_managed_mistake()
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
TransactionManagementError: Transaction managed block ended with pending COMMIT/ROLLBACK
|
||||||
|
"""
|
@ -39,13 +39,13 @@ class DjangoDoctestRunner(doctest.DocTestRunner):
|
|||||||
"Code: %r\nLine: %s\nExpected: %r\nGot: %r" % (example.source.strip(), example.lineno, example.want, got))
|
"Code: %r\nLine: %s\nExpected: %r\nGot: %r" % (example.source.strip(), example.lineno, example.want, got))
|
||||||
|
|
||||||
def report_unexpected_exception(self, out, test, example, exc_info):
|
def report_unexpected_exception(self, out, test, example, exc_info):
|
||||||
from django.db import connection
|
from django.db import transaction
|
||||||
tb = ''.join(traceback.format_exception(*exc_info)[1:])
|
tb = ''.join(traceback.format_exception(*exc_info)[1:])
|
||||||
log_error(test.name, "API test raised an exception",
|
log_error(test.name, "API test raised an exception",
|
||||||
"Code: %r\nLine: %s\nException: %s" % (example.source.strip(), example.lineno, tb))
|
"Code: %r\nLine: %s\nException: %s" % (example.source.strip(), example.lineno, tb))
|
||||||
# Rollback, in case of database errors. Otherwise they'd have
|
# Rollback, in case of database errors. Otherwise they'd have
|
||||||
# side effects on other tests.
|
# side effects on other tests.
|
||||||
connection.rollback()
|
transaction.rollback_unless_managed()
|
||||||
|
|
||||||
normalize_long_ints = lambda s: re.sub(r'(?<![\w])(\d+)L(?![\w])', '\\1', s)
|
normalize_long_ints = lambda s: re.sub(r'(?<![\w])(\d+)L(?![\w])', '\\1', s)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user