Skip to content

Commit 685ddbb

Browse files
committed
[1.2.X] Migrated transactions doctests. Thanks to Eric Florenzano.
Backport of r13826 from trunk. git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.2.X@13835 bcc190cf-cafb-0310-a4f2-bffc1f526a37
1 parent 06211b0 commit 685ddbb

File tree

2 files changed

+156
-135
lines changed

2 files changed

+156
-135
lines changed

tests/modeltests/transactions/models.py

Lines changed: 1 addition & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -18,138 +18,4 @@ class Meta:
1818
ordering = ('first_name', 'last_name')
1919

2020
def __unicode__(self):
21-
return u"%s %s" % (self.first_name, self.last_name)
22-
23-
__test__ = {'API_TESTS':"""
24-
>>> from django.db import connection, transaction
25-
"""}
26-
27-
from django.conf import settings
28-
29-
building_docs = getattr(settings, 'BUILDING_DOCS', False)
30-
31-
if building_docs or settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.mysql':
32-
__test__['API_TESTS'] += """
33-
# the default behavior is to autocommit after each save() action
34-
>>> def create_a_reporter_then_fail(first, last):
35-
... a = Reporter(first_name=first, last_name=last)
36-
... a.save()
37-
... raise Exception("I meant to do that")
38-
...
39-
>>> create_a_reporter_then_fail("Alice", "Smith")
40-
Traceback (most recent call last):
41-
...
42-
Exception: I meant to do that
43-
44-
# The object created before the exception still exists
45-
>>> Reporter.objects.all()
46-
[<Reporter: Alice Smith>]
47-
48-
# the autocommit decorator works exactly the same as the default behavior
49-
>>> autocomitted_create_then_fail = transaction.autocommit(create_a_reporter_then_fail)
50-
>>> autocomitted_create_then_fail("Ben", "Jones")
51-
Traceback (most recent call last):
52-
...
53-
Exception: I meant to do that
54-
55-
# Same behavior as before
56-
>>> Reporter.objects.all()
57-
[<Reporter: Alice Smith>, <Reporter: Ben Jones>]
58-
59-
# the autocommit decorator also works with a using argument
60-
>>> using_autocomitted_create_then_fail = transaction.autocommit(using='default')(create_a_reporter_then_fail)
61-
>>> using_autocomitted_create_then_fail("Carol", "Doe")
62-
Traceback (most recent call last):
63-
...
64-
Exception: I meant to do that
65-
66-
# Same behavior as before
67-
>>> Reporter.objects.all()
68-
[<Reporter: Alice Smith>, <Reporter: Ben Jones>, <Reporter: Carol Doe>]
69-
70-
# With the commit_on_success decorator, the transaction is only committed if the
71-
# function doesn't throw an exception
72-
>>> committed_on_success = transaction.commit_on_success(create_a_reporter_then_fail)
73-
>>> committed_on_success("Dirk", "Gently")
74-
Traceback (most recent call last):
75-
...
76-
Exception: I meant to do that
77-
78-
# This time the object never got saved
79-
>>> Reporter.objects.all()
80-
[<Reporter: Alice Smith>, <Reporter: Ben Jones>, <Reporter: Carol Doe>]
81-
82-
# commit_on_success decorator also works with a using argument
83-
>>> using_committed_on_success = transaction.commit_on_success(using='default')(create_a_reporter_then_fail)
84-
>>> using_committed_on_success("Dirk", "Gently")
85-
Traceback (most recent call last):
86-
...
87-
Exception: I meant to do that
88-
89-
# This time the object never got saved
90-
>>> Reporter.objects.all()
91-
[<Reporter: Alice Smith>, <Reporter: Ben Jones>, <Reporter: Carol Doe>]
92-
93-
# If there aren't any exceptions, the data will get saved
94-
>>> def remove_a_reporter():
95-
... r = Reporter.objects.get(first_name="Alice")
96-
... r.delete()
97-
...
98-
>>> remove_comitted_on_success = transaction.commit_on_success(remove_a_reporter)
99-
>>> remove_comitted_on_success()
100-
>>> Reporter.objects.all()
101-
[<Reporter: Ben Jones>, <Reporter: Carol Doe>]
102-
103-
# You can manually manage transactions if you really want to, but you
104-
# have to remember to commit/rollback
105-
>>> def manually_managed():
106-
... r = Reporter(first_name="Dirk", last_name="Gently")
107-
... r.save()
108-
... transaction.commit()
109-
>>> manually_managed = transaction.commit_manually(manually_managed)
110-
>>> manually_managed()
111-
>>> Reporter.objects.all()
112-
[<Reporter: Ben Jones>, <Reporter: Carol Doe>, <Reporter: Dirk Gently>]
113-
114-
# If you forget, you'll get bad errors
115-
>>> def manually_managed_mistake():
116-
... r = Reporter(first_name="Edward", last_name="Woodward")
117-
... r.save()
118-
... # oops, I forgot to commit/rollback!
119-
>>> manually_managed_mistake = transaction.commit_manually(manually_managed_mistake)
120-
>>> manually_managed_mistake()
121-
Traceback (most recent call last):
122-
...
123-
TransactionManagementError: Transaction managed block ended with pending COMMIT/ROLLBACK
124-
125-
# commit_manually also works with a using argument
126-
>>> using_manually_managed_mistake = transaction.commit_manually(using='default')(manually_managed_mistake)
127-
>>> using_manually_managed_mistake()
128-
Traceback (most recent call last):
129-
...
130-
TransactionManagementError: Transaction managed block ended with pending COMMIT/ROLLBACK
131-
132-
"""
133-
134-
# Regression for #11900: If a function wrapped by commit_on_success writes a
135-
# transaction that can't be committed, that transaction should be rolled back.
136-
# The bug is only visible using the psycopg2 backend, though
137-
# the fix is generally a good idea.
138-
pgsql_backends = ('django.db.backends.postgresql_psycopg2', 'postgresql_psycopg2',)
139-
if building_docs or settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] in pgsql_backends:
140-
__test__['API_TESTS'] += """
141-
>>> def execute_bad_sql():
142-
... cursor = connection.cursor()
143-
... cursor.execute("INSERT INTO transactions_reporter (first_name, last_name) VALUES ('Douglas', 'Adams');")
144-
... transaction.set_dirty()
145-
...
146-
>>> execute_bad_sql = transaction.commit_on_success(execute_bad_sql)
147-
>>> execute_bad_sql()
148-
Traceback (most recent call last):
149-
...
150-
IntegrityError: null value in column "email" violates not-null constraint
151-
<BLANKLINE>
152-
153-
>>> transaction.rollback()
154-
155-
"""
21+
return u"%s %s" % (self.first_name, self.last_name)
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
from django.test import TransactionTestCase
2+
from django.db import connection, transaction, IntegrityError, DEFAULT_DB_ALIAS
3+
from django.conf import settings
4+
5+
from models import Reporter
6+
7+
PGSQL = 'psycopg2' in settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE']
8+
MYSQL = 'mysql' in settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE']
9+
10+
class TransactionTests(TransactionTestCase):
11+
12+
if not MYSQL:
13+
14+
def create_a_reporter_then_fail(self, first, last):
15+
a = Reporter(first_name=first, last_name=last)
16+
a.save()
17+
raise Exception("I meant to do that")
18+
19+
def remove_a_reporter(self, first_name):
20+
r = Reporter.objects.get(first_name="Alice")
21+
r.delete()
22+
23+
def manually_managed(self):
24+
r = Reporter(first_name="Dirk", last_name="Gently")
25+
r.save()
26+
transaction.commit()
27+
28+
def manually_managed_mistake(self):
29+
r = Reporter(first_name="Edward", last_name="Woodward")
30+
r.save()
31+
# Oops, I forgot to commit/rollback!
32+
33+
def execute_bad_sql(self):
34+
cursor = connection.cursor()
35+
cursor.execute("INSERT INTO transactions_reporter (first_name, last_name) VALUES ('Douglas', 'Adams');")
36+
transaction.set_dirty()
37+
38+
def test_autocommit(self):
39+
"""
40+
The default behavior is to autocommit after each save() action.
41+
"""
42+
self.assertRaises(Exception,
43+
self.create_a_reporter_then_fail,
44+
"Alice", "Smith"
45+
)
46+
47+
# The object created before the exception still exists
48+
self.assertEqual(Reporter.objects.count(), 1)
49+
50+
def test_autocommit_decorator(self):
51+
"""
52+
The autocommit decorator works exactly the same as the default behavior.
53+
"""
54+
autocomitted_create_then_fail = transaction.autocommit(
55+
self.create_a_reporter_then_fail
56+
)
57+
self.assertRaises(Exception,
58+
autocomitted_create_then_fail,
59+
"Alice", "Smith"
60+
)
61+
# Again, the object created before the exception still exists
62+
self.assertEqual(Reporter.objects.count(), 1)
63+
64+
def test_autocommit_decorator_with_using(self):
65+
"""
66+
The autocommit decorator also works with a using argument.
67+
"""
68+
autocomitted_create_then_fail = transaction.autocommit(using='default')(
69+
self.create_a_reporter_then_fail
70+
)
71+
self.assertRaises(Exception,
72+
autocomitted_create_then_fail,
73+
"Alice", "Smith"
74+
)
75+
# Again, the object created before the exception still exists
76+
self.assertEqual(Reporter.objects.count(), 1)
77+
78+
def test_commit_on_success(self):
79+
"""
80+
With the commit_on_success decorator, the transaction is only committed
81+
if the function doesn't throw an exception.
82+
"""
83+
committed_on_success = transaction.commit_on_success(
84+
self.create_a_reporter_then_fail)
85+
self.assertRaises(Exception, committed_on_success, "Dirk", "Gently")
86+
# This time the object never got saved
87+
self.assertEqual(Reporter.objects.count(), 0)
88+
89+
def test_commit_on_success_with_using(self):
90+
"""
91+
The commit_on_success decorator also works with a using argument.
92+
"""
93+
using_committed_on_success = transaction.commit_on_success(using='default')(
94+
self.create_a_reporter_then_fail
95+
)
96+
self.assertRaises(Exception,
97+
using_committed_on_success,
98+
"Dirk", "Gently"
99+
)
100+
# This time the object never got saved
101+
self.assertEqual(Reporter.objects.count(), 0)
102+
103+
def test_commit_on_success_succeed(self):
104+
"""
105+
If there aren't any exceptions, the data will get saved.
106+
"""
107+
Reporter.objects.create(first_name="Alice", last_name="Smith")
108+
remove_comitted_on_success = transaction.commit_on_success(
109+
self.remove_a_reporter
110+
)
111+
remove_comitted_on_success("Alice")
112+
self.assertEqual(list(Reporter.objects.all()), [])
113+
114+
def test_manually_managed(self):
115+
"""
116+
You can manually manage transactions if you really want to, but you
117+
have to remember to commit/rollback.
118+
"""
119+
manually_managed = transaction.commit_manually(self.manually_managed)
120+
manually_managed()
121+
self.assertEqual(Reporter.objects.count(), 1)
122+
123+
def test_manually_managed_mistake(self):
124+
"""
125+
If you forget, you'll get bad errors.
126+
"""
127+
manually_managed_mistake = transaction.commit_manually(
128+
self.manually_managed_mistake
129+
)
130+
self.assertRaises(transaction.TransactionManagementError,
131+
manually_managed_mistake)
132+
133+
def test_manually_managed_with_using(self):
134+
"""
135+
The commit_manually function also works with a using argument.
136+
"""
137+
using_manually_managed_mistake = transaction.commit_manually(using='default')(
138+
self.manually_managed_mistake
139+
)
140+
self.assertRaises(transaction.TransactionManagementError,
141+
using_manually_managed_mistake
142+
)
143+
144+
if PGSQL:
145+
146+
def test_bad_sql(self):
147+
"""
148+
Regression for #11900: If a function wrapped by commit_on_success
149+
writes a transaction that can't be committed, that transaction should
150+
be rolled back. The bug is only visible using the psycopg2 backend,
151+
though the fix is generally a good idea.
152+
"""
153+
execute_bad_sql = transaction.commit_on_success(self.execute_bad_sql)
154+
self.assertRaises(IntegrityError, execute_bad_sql)
155+
transaction.rollback()

0 commit comments

Comments
 (0)