diff --git a/django/core/management/commands/syncdb.py b/django/core/management/commands/syncdb.py index 3c11a263d6..a5228a1ab7 100644 --- a/django/core/management/commands/syncdb.py +++ b/django/core/management/commands/syncdb.py @@ -26,6 +26,10 @@ class Command(NoArgsCommand): interactive = options.get('interactive') show_traceback = options.get('traceback', False) + # Stealth option -- 'load_initial_data' is used by the testing setup + # process to disable initial fixture loading. + load_initial_data = options.get('load_initial_data', True) + self.style = no_style() # Import the 'management' module within each installed app, to register @@ -154,5 +158,7 @@ class Command(NoArgsCommand): else: transaction.commit_unless_managed(using=db) - from django.core.management import call_command - call_command('loaddata', 'initial_data', verbosity=verbosity, database=db) + # Load initial_data fixtures (unless that has been disabled) + if load_initial_data: + from django.core.management import call_command + call_command('loaddata', 'initial_data', verbosity=verbosity, database=db) diff --git a/django/db/backends/creation.py b/django/db/backends/creation.py index 7b426cb11f..b92412abe4 100644 --- a/django/db/backends/creation.py +++ b/django/db/backends/creation.py @@ -359,7 +359,21 @@ class BaseDatabaseCreation(object): # Report syncdb messages at one level lower than that requested. # This ensures we don't get flooded with messages during testing # (unless you really ask to be flooded) - call_command('syncdb', verbosity=max(verbosity - 1, 0), interactive=False, database=self.connection.alias) + call_command('syncdb', + verbosity=max(verbosity - 1, 0), + interactive=False, + database=self.connection.alias, + load_initial_data=False) + + # We need to then do a flush to ensure that any data installed by + # custom SQL has been removed. The only test data should come from + # test fixtures, or autogenerated from post_syncdb triggers. + # This has the side effect of loading initial data (which was + # intentionally skipped in the syncdb). + call_command('flush', + verbosity=max(verbosity - 1, 0), + interactive=False, + database=self.connection.alias) from django.core.cache import get_cache from django.core.cache.backends.db import BaseDatabaseCache diff --git a/docs/howto/initial-data.txt b/docs/howto/initial-data.txt index cf3f65d299..dd6a099b9d 100644 --- a/docs/howto/initial-data.txt +++ b/docs/howto/initial-data.txt @@ -121,6 +121,17 @@ the order in which they're executed. The only thing you can assume is that, by the time your custom data files are executed, all the database tables already will have been created. +.. admonition:: Initial SQL data and testing + + This technique *cannot* be used to provide initial data for + testing purposes. Django's test framework flushes the contents of + the test database after each test; as a result, any data added + using the custom SQL hook will be lost. + + If you require data for a test case, you should add it using + either a :ref:`test fixture `, or + programatically add it during the ``setUp()`` of your test case. + Database-backend-specific SQL data ---------------------------------- diff --git a/docs/releases/1.3.txt b/docs/releases/1.3.txt index d41dc8630c..1904e9526d 100644 --- a/docs/releases/1.3.txt +++ b/docs/releases/1.3.txt @@ -410,6 +410,36 @@ Bloggs'``. Although the previous behaviour was not useful for a template languag designed for web designers, and was never deliberately supported, it is possible that some templates may be broken by this change. +Use of custom SQL to load initial data in tests +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Django provides a custom SQL hooks as a way to inject hand-crafted SQL +into the database synchronization process. One of the possible uses +for this custom SQL is to insert data into your database. If your +custom SQL contains ``INSERT`` statements, those insertions will be +performed every time your database is synchronized. This includes the +synchronization of any test databases that are created when you run a +test suite. + +However, in the process of testing the Django 1.3, it was discovered +that this feature has never completely worked as advertised. When +using database backends that don't support transactions, or when using +a TransactionTestCase, data that has been inserted using custom SQL +will not be visible during the testing process. + +Unfortunately, there was no way to rectify this problem without +introducing a backwards incompatibility. Rather than leave +SQL-inserted initial data in an uncertain state, Django now enforces +the policy that data inserted by custom SQL will *not* be visible +during testing. + +This change only affects the testing process. You can still use custom +SQL to load data into your production database as part of the syncdb +process. If you require data to exist during test conditions, you +should either insert it using :ref:`test fixtures +`, or using the ``setUp()`` method of your +test case. + .. _deprecated-features-1.3: Features deprecated in 1.3 diff --git a/docs/topics/testing.txt b/docs/topics/testing.txt index 78590ddb2b..795f041fb9 100644 --- a/docs/topics/testing.txt +++ b/docs/topics/testing.txt @@ -1237,6 +1237,15 @@ documentation` for more details. Fixtures with other names can always be installed manually using the :djadmin:`manage.py loaddata` command. +.. admonition:: Initial SQL data and testing + + Django provides a second way to insert initial data into models -- + the :ref:`custom SQL hook `. However, this technique + *cannot* be used to provide initial data for testing purposes. + Django's test framework flushes the contents of the test database + after each test; as a result, any data added using the custom SQL + hook will be lost. + Once you've created a fixture and placed it in a ``fixtures`` directory in one of your :setting:`INSTALLED_APPS`, you can use it in your unit tests by specifying a ``fixtures`` class attribute on your :class:`django.test.TestCase` diff --git a/tests/regressiontests/initial_sql_regress/models.py b/tests/regressiontests/initial_sql_regress/models.py index 9f91802ec7..6c7db2f362 100644 --- a/tests/regressiontests/initial_sql_regress/models.py +++ b/tests/regressiontests/initial_sql_regress/models.py @@ -7,5 +7,3 @@ from django.db import models class Simple(models.Model): name = models.CharField(max_length = 50) -# NOTE: The format of the included SQL file for this test suite is important. -# It must end with a trailing newline in order to test the fix for #2161. diff --git a/tests/regressiontests/initial_sql_regress/tests.py b/tests/regressiontests/initial_sql_regress/tests.py index 2b3ca91f91..1e6710be9e 100644 --- a/tests/regressiontests/initial_sql_regress/tests.py +++ b/tests/regressiontests/initial_sql_regress/tests.py @@ -5,4 +5,11 @@ from models import Simple class InitialSQLTests(TestCase): def test_initial_sql(self): - self.assertEqual(Simple.objects.count(), 7) + # The format of the included SQL file for this test suite is important. + # It must end with a trailing newline in order to test the fix for #2161. + + # However, as pointed out by #14661, test data loaded by custom SQL + # can't be relied upon; as a result, the test framework flushes the + # data contents before every test. This test validates that this has + # occurred. + self.assertEqual(Simple.objects.count(), 0)