diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py
a
|
b
|
|
255 | 255 | def make_debug_cursor(self, cursor): |
256 | 256 | return util.CursorDebugWrapper(cursor, self) |
257 | 257 | |
| 258 | def check_constraints(self): |
| 259 | sql = self.ops.check_constraints_sql() |
| 260 | for s in sql: |
| 261 | self.cursor().execute(s) |
| 262 | |
258 | 263 | class BaseDatabaseFeatures(object): |
259 | 264 | allows_group_by_pk = False |
260 | 265 | # True if django.db.backend.utils.typecast_timestamp is used on values |
… |
… |
|
440 | 445 | """ |
441 | 446 | return '' |
442 | 447 | |
| 448 | def check_constraints_sql(self): |
| 449 | """ |
| 450 | Returns the SQL necessary to force deferred constraints to be checked. |
| 451 | """ |
| 452 | return [] |
| 453 | |
443 | 454 | def drop_foreignkey_sql(self): |
444 | 455 | """ |
445 | 456 | Returns the SQL command that drops a foreign key. |
diff --git a/django/db/backends/postgresql_psycopg2/operations.py b/django/db/backends/postgresql_psycopg2/operations.py
a
|
b
|
|
39 | 39 | def deferrable_sql(self): |
40 | 40 | return " DEFERRABLE INITIALLY DEFERRED" |
41 | 41 | |
| 42 | def check_constraints_sql(self): |
| 43 | return ["SET CONSTRAINTS ALL IMMEDIATE"] |
| 44 | |
42 | 45 | def lookup_cast(self, lookup_type): |
43 | 46 | lookup = '%s' |
44 | 47 | |
diff --git a/django/test/testcases.py b/django/test/testcases.py
a
|
b
|
|
13 | 13 | from django.core.urlresolvers import clear_url_caches |
14 | 14 | from django.db import (transaction, connection, connections, DEFAULT_DB_ALIAS, |
15 | 15 | reset_queries) |
| 16 | from django.db.utils import IntegrityError |
16 | 17 | from django.http import QueryDict |
17 | 18 | from django.test import _doctest as doctest |
18 | 19 | from django.test.client import Client |
… |
… |
|
298 | 299 | return |
299 | 300 | super(TransactionTestCase, self).__call__(result) |
300 | 301 | try: |
301 | | self._post_teardown() |
| 302 | self._post_teardown(result) |
302 | 303 | except (KeyboardInterrupt, SystemExit): |
303 | 304 | raise |
304 | 305 | except Exception: |
… |
… |
|
306 | 307 | result.addError(self, sys.exc_info()) |
307 | 308 | return |
308 | 309 | |
309 | | def _post_teardown(self): |
| 310 | def _post_teardown(self, result): |
310 | 311 | """ Performs any post-test things. This includes: |
311 | 312 | |
| 313 | * Tearing down database fixtures, if any. |
312 | 314 | * Putting back the original ROOT_URLCONF if it was changed. |
313 | 315 | * Force closing the connection, so that the next test gets |
314 | 316 | a clean cursor. |
315 | 317 | """ |
316 | | self._fixture_teardown() |
| 318 | self._fixture_teardown(result) |
317 | 319 | self._urlconf_teardown() |
318 | 320 | # Some DB cursors include SQL statements as part of cursor |
319 | 321 | # creation. If you have a test that does rollback, the effect |
… |
… |
|
325 | 327 | for connection in connections.all(): |
326 | 328 | connection.close() |
327 | 329 | |
328 | | def _fixture_teardown(self): |
| 330 | def _fixture_teardown(self, result): |
329 | 331 | pass |
330 | 332 | |
331 | 333 | def _urlconf_teardown(self): |
… |
… |
|
589 | 591 | 'database': db |
590 | 592 | }) |
591 | 593 | |
592 | | def _fixture_teardown(self): |
| 594 | def _fixture_teardown(self, result): |
593 | 595 | if not connections_support_transactions(): |
594 | 596 | return super(TestCase, self)._fixture_teardown() |
595 | 597 | |
… |
… |
|
602 | 604 | |
603 | 605 | restore_transaction_methods() |
604 | 606 | for db in databases: |
605 | | transaction.rollback(using=db) |
606 | | transaction.leave_transaction_management(using=db) |
| 607 | # Force constraints check execution (on backends that support |
| 608 | # deferring them) before rolling back the transaction. |
| 609 | try: |
| 610 | connections[db].check_constraints() |
| 611 | except IntegrityError: |
| 612 | result.addError(self, sys.exc_info()) |
| 613 | finally: |
| 614 | transaction.rollback(using=db) |
| 615 | transaction.leave_transaction_management(using=db) |
607 | 616 | |
608 | 617 | def _deferredSkip(condition, reason): |
609 | 618 | def decorator(test_func): |
diff --git a/tests/regressiontests/multiple_database/tests.py b/tests/regressiontests/multiple_database/tests.py
a
|
b
|
|
1465 | 1465 | published=datetime.date(2008, 12, 16)) |
1466 | 1466 | |
1467 | 1467 | marty = Person.objects.using('other').create(pk=1, name="Marty Alchin") |
1468 | | pro.authors = [marty] |
| 1468 | # Per the router in effect, writes go to the 'default' DB and so the |
| 1469 | # manager will create m2m relationships on that DB. Derive a new manager |
| 1470 | # associated with the 'other' DB so we don't create cross-DB FKs |
| 1471 | author_m2m_manager_in_other_db = pro.authors.db_manager('other') |
| 1472 | author_m2m_manager_in_other_db = [marty] |
1469 | 1473 | |
1470 | 1474 | self.assertEqual(pro.authors.db, 'other') |
1471 | 1475 | self.assertEqual(pro.authors.db_manager('default').db, 'default') |