Commit d3fdaf90 authored by Tommy Beadle's avatar Tommy Beadle Committed by Tim Graham
Browse files

Fixed #23727 -- Inhibited the post_migrate signal when using serialized_rollback.

When using a TransactionTestCase with serialized_rollback=True,
after creating the database and running its migrations (along with
emitting the post_migrate signal), the contents of the database
are serialized to _test_serialized_contents.

After the first test case, _fixture_teardown() would flush the
tables but then the post_migrate signal would be emitted and new
rows (with new PKs) would be created in the django_content_type
table. Then in any subsequent test cases in a suite,
_fixture_setup() attempts to deserialize the content of
 _test_serialized_contents, but these rows are identical to the
rows already in the database except for their PKs.  This causes an
IntegrityError due to the unique constraint in the
django_content_type table.

This change made it so that in the above scenario the post_migrate
signal is not emitted after flushing the tables, since it will be
repopulated during fixture_setup().
parent 45ed19de
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -698,6 +698,7 @@ answer newbie questions, and generally made Django that much better:
    Tome Cvitan <tome@cvitan.com>
    Tomek Paczkowski <tomek@hauru.eu>
    Tom Insam
    Tommy Beadle <tbeadle@gmail.com>
    Tom Tobin
    torne-django@wolfpuppy.org.uk
    Travis Cline <travis.cline@gmail.com>
+10 −1
Original line number Diff line number Diff line
@@ -940,10 +940,19 @@ class TransactionTestCase(SimpleTestCase):
        # when flushing only a subset of the apps
        for db_name in self._databases_names(include_mirrors=False):
            # Flush the database
            inhibit_post_migrate = (
                self.available_apps is not None
                or (
                    # Inhibit the post_migrate signal when using serialized
                    # rollback to avoid trying to recreate the serialized data.
                    self.serialized_rollback and
                    hasattr(connections[db_name], '_test_serialized_contents')
                )
            )
            call_command('flush', verbosity=0, interactive=False,
                         database=db_name, reset_sequences=False,
                         allow_cascade=self.available_apps is not None,
                         inhibit_post_migrate=self.available_apps is not None)
                         inhibit_post_migrate=inhibit_post_migrate)

    def assertQuerysetEqual(self, qs, values, transform=repr, ordered=True, msg=None):
        items = six.moves.map(transform, qs)
+7 −0
Original line number Diff line number Diff line
@@ -250,6 +250,13 @@ The initial serialization is usually very quick, but if you wish to exclude
some apps from this process (and speed up test runs slightly), you may add
those apps to :setting:`TEST_NON_SERIALIZED_APPS`.

.. versionchanged:: 1.9

To prevent serialized data from being loaded twice, setting
``serialized_rollback=True`` disables the
:data:`~django.db.models.signals.post_migrate` signal when flushing the test
database.

Other test conditions
---------------------

+28 −0
Original line number Diff line number Diff line
from django.test import TransactionTestCase, mock


class TestSerializedRollbackInhibitsPostMigrate(TransactionTestCase):
    """
    TransactionTestCase._fixture_teardown() inhibits the post_migrate signal
    for test classes with serialized_rollback=True.
    """
    available_apps = ['test_utils']
    serialized_rollback = True

    def setUp(self):
        # self.available_apps must be None to test the serialized_rollback
        # condition.
        self.available_apps = None

    def tearDown(self):
        self.available_apps = ['test_utils']

    @mock.patch('django.test.testcases.call_command')
    def test(self, call_command):
        # with a mocked call_command(), this doesn't have any effect.
        self._fixture_teardown()
        call_command.assert_called_with(
            'flush', interactive=False, allow_cascade=False,
            reset_sequences=False, inhibit_post_migrate=True,
            database='default', verbosity=0,
        )