Removed support for syncing apps without migrations per deprecation timeline.

Kept support for creating models without migrations when running tests
(especially for Django's test suite).
This commit is contained in:
Tim Graham 2014-12-26 13:56:08 -05:00
parent 9704b0a82e
commit 7e8cf74dc7
31 changed files with 29 additions and 626 deletions

View File

@ -232,8 +232,7 @@ class ManagementUtility(object):
elif cwords[0] in subcommands and cwords[0] != 'help': elif cwords[0] in subcommands and cwords[0] != 'help':
subcommand_cls = self.fetch_command(cwords[0]) subcommand_cls = self.fetch_command(cwords[0])
# special case: add the names of installed apps to options # special case: add the names of installed apps to options
if cwords[0] in ('dumpdata', 'sql', 'sqlall', 'sqlclear', if cwords[0] in ('dumpdata', 'sqlmigrate', 'sqlsequencereset', 'test'):
'sqlindexes', 'sqlmigrate', 'sqlsequencereset', 'test'):
try: try:
app_configs = apps.get_app_configs() app_configs = apps.get_app_configs()
# Get the last part of the dotted path as the app name. # Get the last part of the dotted path as the app name.

View File

@ -93,14 +93,12 @@ class Command(BaseCommand):
) )
# If they supplied command line arguments, work out what they mean. # If they supplied command line arguments, work out what they mean.
run_syncdb = False
target_app_labels_only = True target_app_labels_only = True
if options['app_label'] and options['migration_name']: if options['app_label'] and options['migration_name']:
app_label, migration_name = options['app_label'], options['migration_name'] app_label, migration_name = options['app_label'], options['migration_name']
if app_label not in executor.loader.migrated_apps: if app_label not in executor.loader.migrated_apps:
raise CommandError( raise CommandError(
"App '%s' does not have migrations (you cannot selectively " "App '%s' does not have migrations." % app_label
"sync unmigrated apps)" % app_label
) )
if migration_name == "zero": if migration_name == "zero":
targets = [(app_label, None)] targets = [(app_label, None)]
@ -122,20 +120,19 @@ class Command(BaseCommand):
app_label = options['app_label'] app_label = options['app_label']
if app_label not in executor.loader.migrated_apps: if app_label not in executor.loader.migrated_apps:
raise CommandError( raise CommandError(
"App '%s' does not have migrations (you cannot selectively " "App '%s' does not have migrations." % app_label
"sync unmigrated apps)" % app_label
) )
targets = [key for key in executor.loader.graph.leaf_nodes() if key[0] == app_label] targets = [key for key in executor.loader.graph.leaf_nodes() if key[0] == app_label]
else: else:
targets = executor.loader.graph.leaf_nodes() targets = executor.loader.graph.leaf_nodes()
run_syncdb = True
plan = executor.migration_plan(targets) plan = executor.migration_plan(targets)
run_syncdb = options.get('run_syncdb') and executor.loader.unmigrated_apps
# Print some useful info # Print some useful info
if self.verbosity >= 1: if self.verbosity >= 1:
self.stdout.write(self.style.MIGRATE_HEADING("Operations to perform:")) self.stdout.write(self.style.MIGRATE_HEADING("Operations to perform:"))
if run_syncdb and executor.loader.unmigrated_apps: if run_syncdb:
self.stdout.write( self.stdout.write(
self.style.MIGRATE_LABEL(" Synchronize unmigrated apps: ") + self.style.MIGRATE_LABEL(" Synchronize unmigrated apps: ") +
(", ".join(executor.loader.unmigrated_apps)) (", ".join(executor.loader.unmigrated_apps))
@ -159,7 +156,7 @@ class Command(BaseCommand):
emit_pre_migrate_signal(self.verbosity, self.interactive, connection.alias) emit_pre_migrate_signal(self.verbosity, self.interactive, connection.alias)
# Run the syncdb phase. # Run the syncdb phase.
if run_syncdb and executor.loader.unmigrated_apps: if run_syncdb:
if self.verbosity >= 1: if self.verbosity >= 1:
self.stdout.write(self.style.MIGRATE_HEADING("Synchronizing apps without migrations:")) self.stdout.write(self.style.MIGRATE_HEADING("Synchronizing apps without migrations:"))
self.sync_apps(connection, executor.loader.unmigrated_apps) self.sync_apps(connection, executor.loader.unmigrated_apps)

View File

@ -1,24 +0,0 @@
from __future__ import unicode_literals
from django.core.management.base import AppCommand
from django.core.management.sql import sql_create
from django.db import connections, DEFAULT_DB_ALIAS
class Command(AppCommand):
help = "Prints the CREATE TABLE SQL statements for the given app name(s)."
output_transaction = True
def add_arguments(self, parser):
super(Command, self).add_arguments(parser)
parser.add_argument('--database', default=DEFAULT_DB_ALIAS,
help='Nominates a database to print the SQL for. Defaults to the '
'"default" database.')
def handle_app_config(self, app_config, **options):
if app_config.models_module is None:
return
connection = connections[options['database']]
statements = sql_create(app_config, self.style, connection)
return '\n'.join(statements)

View File

@ -1,24 +0,0 @@
from __future__ import unicode_literals
from django.core.management.base import AppCommand
from django.core.management.sql import sql_all
from django.db import connections, DEFAULT_DB_ALIAS
class Command(AppCommand):
help = "Prints the CREATE TABLE and CREATE INDEX SQL statements for the given model module name(s)."
output_transaction = True
def add_arguments(self, parser):
super(Command, self).add_arguments(parser)
parser.add_argument('--database', default=DEFAULT_DB_ALIAS,
help='Nominates a database to print the SQL for. Defaults to the '
'"default" database.')
def handle_app_config(self, app_config, **options):
if app_config.models_module is None:
return
connection = connections[options['database']]
statements = sql_all(app_config, self.style, connection)
return '\n'.join(statements)

View File

@ -1,24 +0,0 @@
from __future__ import unicode_literals
from django.core.management.base import AppCommand
from django.core.management.sql import sql_delete
from django.db import connections, DEFAULT_DB_ALIAS
class Command(AppCommand):
help = "Prints the DROP TABLE SQL statements for the given app name(s)."
output_transaction = True
def add_arguments(self, parser):
super(Command, self).add_arguments(parser)
parser.add_argument('--database', default=DEFAULT_DB_ALIAS,
help='Nominates a database to print the SQL for. Defaults to the '
'"default" database.')
def handle_app_config(self, app_config, **options):
if app_config.models_module is None:
return
connection = connections[options['database']]
statements = sql_delete(app_config, self.style, connection)
return '\n'.join(statements)

View File

@ -1,24 +0,0 @@
from __future__ import unicode_literals
from django.core.management.base import AppCommand
from django.core.management.sql import sql_destroy_indexes
from django.db import connections, DEFAULT_DB_ALIAS
class Command(AppCommand):
help = "Prints the DROP INDEX SQL statements for the given model module name(s)."
output_transaction = True
def add_arguments(self, parser):
super(Command, self).add_arguments(parser)
parser.add_argument('--database', default=DEFAULT_DB_ALIAS,
help='Nominates a database to print the SQL for. Defaults to the '
'"default" database.')
def handle_app_config(self, app_config, **options):
if app_config.models_module is None:
return
connection = connections[options['database']]
statements = sql_destroy_indexes(app_config, self.style, connection)
return '\n'.join(statements)

View File

@ -1,24 +0,0 @@
from __future__ import unicode_literals
from django.core.management.base import AppCommand
from django.core.management.sql import sql_indexes
from django.db import connections, DEFAULT_DB_ALIAS
class Command(AppCommand):
help = "Prints the CREATE INDEX SQL statements for the given model module name(s)."
output_transaction = True
def add_arguments(self, parser):
super(Command, self).add_arguments(parser)
parser.add_argument('--database', default=DEFAULT_DB_ALIAS,
help='Nominates a database to print the SQL for. Defaults to the '
'"default" database.')
def handle_app_config(self, app_config, **options):
if app_config.models_module is None:
return
connection = connections[options['database']]
statements = sql_indexes(app_config, self.style, connection)
return '\n'.join(statements)

View File

@ -1,120 +1,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.apps import apps from django.apps import apps
from django.core.management.base import CommandError from django.db import models
from django.db import models, router
from django.utils.version import get_docs_version
def check_for_migrations(app_config, connection):
# Inner import, else tests imports it too early as it needs settings
from django.db.migrations.loader import MigrationLoader
loader = MigrationLoader(connection)
if app_config.label in loader.migrated_apps:
raise CommandError(
"App '%s' has migrations. Only the sqlmigrate and sqlflush commands "
"can be used when an app has migrations." % app_config.label
)
def sql_create(app_config, style, connection):
"Returns a list of the CREATE TABLE SQL statements for the given app."
check_for_migrations(app_config, connection)
if connection.settings_dict['ENGINE'] == 'django.db.backends.dummy':
# This must be the "dummy" database backend, which means the user
# hasn't set ENGINE for the database.
raise CommandError(
"Django doesn't know which syntax to use for your SQL statements,\n"
"because you haven't properly specified the ENGINE setting for the database.\n"
"see: https://docs.djangoproject.com/en/%s/ref/settings/#databases" % get_docs_version()
)
# Get installed models, so we generate REFERENCES right.
# We trim models from the current app so that the sqlreset command does not
# generate invalid SQL (leaving models out of known_models is harmless, so
# we can be conservative).
app_models = list(app_config.get_models(include_auto_created=True))
final_output = []
tables = connection.introspection.table_names()
known_models = set(model for model in connection.introspection.installed_models(tables) if model not in app_models)
pending_references = {}
for model in router.get_migratable_models(app_config, connection.alias, include_auto_created=True):
output, references = connection.creation.sql_create_model(model, style, known_models)
final_output.extend(output)
for refto, refs in references.items():
pending_references.setdefault(refto, []).extend(refs)
if refto in known_models:
final_output.extend(connection.creation.sql_for_pending_references(refto, style, pending_references))
final_output.extend(connection.creation.sql_for_pending_references(model, style, pending_references))
# Keep track of the fact that we've created the table for this model.
known_models.add(model)
# Handle references to tables that are from other apps
# but don't exist physically.
not_installed_models = set(pending_references.keys())
if not_installed_models:
alter_sql = []
for model in not_installed_models:
alter_sql.extend('-- ' + sql for sql in
connection.creation.sql_for_pending_references(model, style, pending_references))
if alter_sql:
final_output.append('-- The following references should be added but depend on non-existent tables:')
final_output.extend(alter_sql)
return final_output
def sql_delete(app_config, style, connection, close_connection=True):
"Returns a list of the DROP TABLE SQL statements for the given app."
check_for_migrations(app_config, connection)
# This should work even if a connection isn't available
try:
cursor = connection.cursor()
except Exception:
cursor = None
try:
# Figure out which tables already exist
if cursor:
table_names = connection.introspection.table_names(cursor)
else:
table_names = []
output = []
# Output DROP TABLE statements for standard application tables.
to_delete = set()
references_to_delete = {}
app_models = router.get_migratable_models(app_config, connection.alias, include_auto_created=True)
for model in app_models:
if cursor and connection.introspection.table_name_converter(model._meta.db_table) in table_names:
# The table exists, so it needs to be dropped
opts = model._meta
for f in opts.local_fields:
if f.rel and f.rel.to not in to_delete:
references_to_delete.setdefault(f.rel.to, []).append((model, f))
to_delete.add(model)
for model in app_models:
if connection.introspection.table_name_converter(model._meta.db_table) in table_names:
output.extend(connection.creation.sql_destroy_model(model, references_to_delete, style))
finally:
# Close database connection explicitly, in case this output is being piped
# directly into a database client, to avoid locking issues.
if cursor and close_connection:
cursor.close()
connection.close()
if not output:
output.append('-- App creates no tables in the database. Nothing to do.')
return output[::-1] # Reverse it, to deal with table dependencies.
def sql_flush(style, connection, only_django=False, reset_sequences=True, allow_cascade=False): def sql_flush(style, connection, only_django=False, reset_sequences=True, allow_cascade=False):
@ -133,39 +20,6 @@ def sql_flush(style, connection, only_django=False, reset_sequences=True, allow_
return statements return statements
def sql_indexes(app_config, style, connection):
"Returns a list of the CREATE INDEX SQL statements for all models in the given app."
check_for_migrations(app_config, connection)
output = []
for model in router.get_migratable_models(app_config, connection.alias, include_auto_created=True):
output.extend(connection.creation.sql_indexes_for_model(model, style))
return output
def sql_destroy_indexes(app_config, style, connection):
"Returns a list of the DROP INDEX SQL statements for all models in the given app."
check_for_migrations(app_config, connection)
output = []
for model in router.get_migratable_models(app_config, connection.alias, include_auto_created=True):
output.extend(connection.creation.sql_destroy_indexes_for_model(model, style))
return output
def sql_all(app_config, style, connection):
check_for_migrations(app_config, connection)
"Returns a list of CREATE TABLE SQL, initial-data inserts, and CREATE INDEX SQL for the given module."
return (
sql_create(app_config, style, connection) +
sql_indexes(app_config, style, connection)
)
def emit_pre_migrate_signal(verbosity, interactive, db): def emit_pre_migrate_signal(verbosity, interactive, db):
# Emit the pre_migrate signal for every application. # Emit the pre_migrate signal for every application.
for app_config in apps.get_app_configs(): for app_config in apps.get_app_configs():

View File

@ -366,6 +366,7 @@ class BaseDatabaseCreation(object):
verbosity=max(verbosity - 1, 0), verbosity=max(verbosity - 1, 0),
interactive=False, interactive=False,
database=self.connection.alias, database=self.connection.alias,
run_syncdb=True,
) )
# We then serialize the current state of the database into a string # We then serialize the current state of the database into a string

View File

@ -537,16 +537,13 @@ Now, run :djadmin:`migrate` again to create those model tables in your database:
.. code-block:: bash .. code-block:: bash
$ python manage.py migrate $ python manage.py migrate
Operations to perform: Operations to perform:
Synchronize unmigrated apps: sessions, admin, messages, auth, staticfiles, contenttypes Apply all migrations: admin, contenttypes, polls, auth, sessions
Apply all migrations: polls
Synchronizing apps without migrations:
Creating tables...
Installing indexes...
Running migrations: Running migrations:
Rendering model states... DONE
...
Applying polls.0001_initial... OK Applying polls.0001_initial... OK
...
The :djadmin:`migrate` command takes all the migrations that haven't been The :djadmin:`migrate` command takes all the migrations that haven't been
applied (Django tracks which ones are applied using a special table in your applied (Django tracks which ones are applied using a special table in your

View File

@ -74,22 +74,9 @@ The
option forces the use of the standard Python interpreter even when IPython is option forces the use of the standard Python interpreter even when IPython is
installed. installed.
.TP .TP
.BI "sql [" "app_label ..." "]"
Prints the CREATE TABLE SQL statements for the given app name(s).
.TP
.BI "sqlall [" "app_label ..." "]"
Prints the CREATE TABLE, initial\-data and CREATE INDEX SQL statements for the
given model module name(s).
.TP
.BI "sqlclear [" "app_label ..." "]"
Prints the DROP TABLE SQL statements for the given app name(s).
.TP
.BI "sqlflush [" "app_label ..." "]" .BI "sqlflush [" "app_label ..." "]"
Prints the SQL statements that would be executed for the "flush" command. Prints the SQL statements that would be executed for the "flush" command.
.TP .TP
.BI "sqlindexes [" "app_label ..." "]"
Prints the CREATE INDEX SQL statements for the given model module name(s).
.TP
.BI "sqlsequencereset [" "app_label ..." "]" .BI "sqlsequencereset [" "app_label ..." "]"
Prints the SQL statements for resetting PostgreSQL sequences for the Prints the SQL statements for resetting PostgreSQL sequences for the
given app name(s). given app name(s).
@ -103,8 +90,7 @@ Creates a Django project directory structure for the given project name
in the current directory or the optional destination. in the current directory or the optional destination.
.TP .TP
.BI migrate .BI migrate
Runs migrations for apps containing migrations, and just creates missing tables Runs migrations for all apps.
for apps without migrations.
.TP .TP
.BI "test [" "\-\-verbosity" "] [" "\-\-failfast" "] [" "app_label ..." "]" .BI "test [" "\-\-verbosity" "] [" "\-\-failfast" "] [" "app_label ..." "]"
Runs the test suite for the specified applications, or the entire project if Runs the test suite for the specified applications, or the entire project if

View File

@ -963,53 +963,6 @@ beyond those apps. Same as ``--list``, applied migrations are marked by an
``[X]``. For a verbosity of 2 and above, all dependencies of a migration will ``[X]``. For a verbosity of 2 and above, all dependencies of a migration will
also be shown. also be shown.
sql <app_label app_label ...>
-----------------------------
.. django-admin:: sql
Prints the CREATE TABLE SQL statements for the given app name(s).
The :djadminopt:`--database` option can be used to specify the database for
which to print the SQL.
sqlall <app_label app_label ...>
--------------------------------
.. django-admin:: sqlall
Prints the CREATE TABLE and initial-data SQL statements for the given app name(s).
The :djadminopt:`--database` option can be used to specify the database for
which to print the SQL.
.. versionchanged:: 1.7
The ``sql*`` management commands now respect the ``allow_migrate()`` method
of :setting:`DATABASE_ROUTERS`. If you have models synced to non-default
databases, use the :djadminopt:`--database` flag to get SQL for those
models (previously they would always be included in the output).
sqlclear <app_label app_label ...>
----------------------------------
.. django-admin:: sqlclear
Prints the DROP TABLE SQL statements for the given app name(s).
The :djadminopt:`--database` option can be used to specify the database for
which to print the SQL.
sqldropindexes <app_label app_label ...>
----------------------------------------
.. django-admin:: sqldropindexes
Prints the DROP INDEX SQL statements for the given app name(s).
The :djadminopt:`--database` option can be used to specify the database for
which to print the SQL.
sqlflush sqlflush
-------- --------
@ -1021,16 +974,6 @@ command.
The :djadminopt:`--database` option can be used to specify the database for The :djadminopt:`--database` option can be used to specify the database for
which to print the SQL. which to print the SQL.
sqlindexes <app_label app_label ...>
------------------------------------
.. django-admin:: sqlindexes
Prints the CREATE INDEX SQL statements for the given app name(s).
The :djadminopt:`--database` option can be used to specify the database for
which to print the SQL.
sqlmigrate <app_label> <migrationname> sqlmigrate <app_label> <migrationname>
-------------------------------------- --------------------------------------

View File

@ -183,8 +183,7 @@ scenes.
.. attribute:: Field.db_index .. attribute:: Field.db_index
If ``True``, :djadmin:`django-admin sqlindexes <sqlindexes>` will output a If ``True``, a database index will be created for this field.
``CREATE INDEX`` statement for this field.
``db_tablespace`` ``db_tablespace``
----------------- -----------------
@ -1120,12 +1119,6 @@ avoid the overhead of an index if you are creating a foreign key for
consistency rather than joins, or if you will be creating an alternative index consistency rather than joins, or if you will be creating an alternative index
like a partial or multiple column index. like a partial or multiple column index.
.. warning::
It is not recommended to have a ``ForeignKey`` from an app without migrations
to an app with migrations. See the :ref:`dependencies documentation
<unmigrated-dependencies>` for more details.
Database Representation Database Representation
~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~
@ -1335,12 +1328,6 @@ which the model is related, which works exactly the same as it does for
Related objects can be added, removed, or created with the field's Related objects can be added, removed, or created with the field's
:class:`~django.db.models.fields.related.RelatedManager`. :class:`~django.db.models.fields.related.RelatedManager`.
.. warning::
It is not recommended to have a ``ManyToManyField`` from an app without migrations
to an app with migrations. See the :ref:`dependencies documentation
<unmigrated-dependencies>` for more details.
Database Representation Database Representation
~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -2397,9 +2397,9 @@ Default: ``[]``
In order to restore the database state between tests for In order to restore the database state between tests for
``TransactionTestCase``\s and database backends without transactions, Django ``TransactionTestCase``\s and database backends without transactions, Django
will :ref:`serialize the contents of all apps with migrations will :ref:`serialize the contents of all apps <test-case-serialized-rollback>`
<test-case-serialized-rollback>` when it starts the test run so it can then when it starts the test run so it can then reload from that copy before tests
reload from that copy before tests that need it. that need it.
This slows down the startup time of the test runner; if you have apps that This slows down the startup time of the test runner; if you have apps that
you know don't need this feature, you can add their full names in here (e.g. you know don't need this feature, you can add their full names in here (e.g.

View File

@ -432,8 +432,7 @@ If you have an existing project that is using the database session
backend, you don't have to do anything to accommodate this change. backend, you don't have to do anything to accommodate this change.
However, you may get a significant performance boost if you manually However, you may get a significant performance boost if you manually
add the new index to the session table. The SQL that will add the add the new index to the session table. The SQL that will add the
index can be found by running the :djadmin:`sqlindexes` admin index can be found by running the ``sqlindexes`` admin command::
command::
python manage.py sqlindexes sessions python manage.py sqlindexes sessions

View File

@ -74,9 +74,8 @@ but a few of the key features are:
<test-case-serialized-rollback>`. <test-case-serialized-rollback>`.
* It is not advised to have apps without migrations depend on (have a * It is not advised to have apps without migrations depend on (have a
:ref:`ForeignKey <ref-foreignkey>` or :ref:`ManyToManyField <ref-manytomany>` to) apps with migrations. Read the :ref:`ForeignKey <ref-foreignkey>` or :ref:`ManyToManyField <ref-manytomany>`
:ref:`dependencies documentation <unmigrated-dependencies>` for more. to) apps with migrations.
* If you are upgrading from South, see our :ref:`upgrading-from-south` * If you are upgrading from South, see our :ref:`upgrading-from-south`
documentation, and third-party app authors should read the documentation, and third-party app authors should read the

View File

@ -582,12 +582,7 @@ spam
spammers spammers
spatialite spatialite
Springmeyer Springmeyer
sql
sqlall
sqlclear
sqldropindexes
sqlflush sqlflush
sqlindexes
sqlmigrate sqlmigrate
sqlsequencereset sqlsequencereset
squashmigrations squashmigrations

View File

@ -136,12 +136,9 @@ database to make sure they work as expected::
$ python manage.py migrate $ python manage.py migrate
Operations to perform: Operations to perform:
Synchronize unmigrated apps: sessions, admin, messages, auth, staticfiles, contenttypes
Apply all migrations: books Apply all migrations: books
Synchronizing apps without migrations:
Creating tables...
Installing indexes...
Running migrations: Running migrations:
Rendering model states... DONE
Applying books.0003_auto... OK Applying books.0003_auto... OK
The command runs in two stages; first, it synchronizes unmigrated apps, and The command runs in two stages; first, it synchronizes unmigrated apps, and
@ -195,26 +192,6 @@ restrict to a single app. Restricting to a single app (either in
a guarantee; any other apps that need to be used to get dependencies correct a guarantee; any other apps that need to be used to get dependencies correct
will be. will be.
.. _unmigrated-dependencies:
Be aware, however, that unmigrated apps cannot depend on migrated apps, by the
very nature of not having migrations. This means that it is not generally
possible to have an unmigrated app have a ``ForeignKey`` or ``ManyToManyField``
to a migrated app; some cases may work, but it will eventually fail.
.. warning::
Even if things appear to work with unmigrated apps depending on migrated
apps, Django may not generate all the necessary foreign key constraints!
This is particularly apparent if you use swappable models (e.g.
``AUTH_USER_MODEL``), as every app that uses swappable models will need
to have migrations if you're unlucky. As time goes on, more and more
third-party apps will get migrations, but in the meantime you can either
give them migrations yourself (using :setting:`MIGRATION_MODULES` to
store those modules outside of the app's own module if you wish), or
keep the app with your user model unmigrated.
.. _migration-files: .. _migration-files:
Migration files Migration files

View File

@ -670,12 +670,12 @@ to test the effects of commit and rollback:
.. warning:: .. warning::
``TestCase`` running on a database that does not support rollback (e.g. MySQL with the ``TestCase`` running on a database that does not support rollback (e.g. MySQL
MyISAM storage engine), and all instances of ``TransactionTestCase``, will with the MyISAM storage engine), and all instances of ``TransactionTestCase``,
roll back at the end of the test by deleting all data from the test database will roll back at the end of the test by deleting all data from the test
and reloading initial data for apps without migrations. database.
Apps with migrations :ref:`will not see their data reloaded <test-case-serialized-rollback>`; Apps :ref:`will not see their data reloaded <test-case-serialized-rollback>`;
if you need this functionality (for example, third-party apps should enable if you need this functionality (for example, third-party apps should enable
this) you can set ``serialized_rollback = True`` inside the this) you can set ``serialized_rollback = True`` inside the
``TestCase`` body. ``TestCase`` body.

View File

@ -319,13 +319,6 @@ class DjangoAdminFullPathDefaultSettings(AdminScriptTestCase):
self.assertNoOutput(err) self.assertNoOutput(err)
self.assertOutput(out, SYSTEM_CHECK_MSG) self.assertOutput(out, SYSTEM_CHECK_MSG)
def test_sqlclear_builtin_with_settings(self):
"fulldefault: django-admin builtin commands succeed if a setting file is provided"
args = ['sqlclear', '--settings=test_project.settings', 'complex_app']
out, err = self.run_django_admin(args)
self.assertNoOutput(err)
self.assertOutput(out, '-- App creates no tables in the database. Nothing to do.')
def test_builtin_with_environment(self): def test_builtin_with_environment(self):
"fulldefault: django-admin builtin commands succeed if the environment contains settings" "fulldefault: django-admin builtin commands succeed if the environment contains settings"
args = ['check', 'admin_scripts'] args = ['check', 'admin_scripts']

View File

@ -78,7 +78,7 @@ class BashCompletionTests(unittest.TestCase):
"Subcommands can be autocompleted" "Subcommands can be autocompleted"
self._user_input('django-admin sql') self._user_input('django-admin sql')
output = self._run_autocomplete() output = self._run_autocomplete()
self.assertEqual(output, ['sql sqlall sqlclear sqldropindexes sqlflush sqlindexes sqlmigrate sqlsequencereset']) self.assertEqual(output, ['sqlflush sqlmigrate sqlsequencereset'])
def test_completed_subcommand(self): def test_completed_subcommand(self):
"Show option flags in case a subcommand is completed" "Show option flags in case a subcommand is completed"

View File

@ -1,11 +0,0 @@
from django.db import models
class Comment(models.Model):
pass
class Book(models.Model):
title = models.CharField(max_length=100, db_index=True)
comments = models.ManyToManyField(Comment)
counter = models.PositiveIntegerField()

View File

@ -1,111 +0,0 @@
from __future__ import unicode_literals
import re
import unittest
from django.apps import apps
from django.core.management.color import no_style
from django.core.management.sql import (sql_create, sql_delete, sql_indexes,
sql_destroy_indexes, sql_all)
from django.db import connections, DEFAULT_DB_ALIAS
from django.test import TestCase, ignore_warnings, override_settings
from django.utils import six
from django.utils.deprecation import RemovedInDjango20Warning
# See also initial_sql_regress for 'custom_sql_for_model' tests
class SQLCommandsTestCase(TestCase):
"""Tests for several functions in django/core/management/sql.py"""
def count_ddl(self, output, cmd):
return len([o for o in output if o.startswith(cmd)])
def test_sql_create(self):
app_config = apps.get_app_config('commands_sql')
output = sql_create(app_config, no_style(), connections[DEFAULT_DB_ALIAS])
tables = set()
create_table_re = re.compile(r'^create table .(?P<table>[\w_]+).*', re.IGNORECASE)
reference_re = re.compile(r'.* references .(?P<table>[\w_]+).*', re.IGNORECASE)
for statement in output:
create_table = create_table_re.match(statement)
if create_table:
# Lower since Oracle's table names are upper cased.
tables.add(create_table.group('table').lower())
continue
reference = reference_re.match(statement)
if reference:
# Lower since Oracle's table names are upper cased.
table = reference.group('table').lower()
self.assertIn(
table, tables, "The table %s is referenced before its creation." % table
)
self.assertEqual(tables, {
'commands_sql_comment', 'commands_sql_book', 'commands_sql_book_comments'
})
@unittest.skipUnless('PositiveIntegerField' in connections[DEFAULT_DB_ALIAS].data_type_check_constraints, 'Backend does not have checks.')
def test_sql_create_check(self):
"""Regression test for #23416 -- Check that db_params['check'] is respected."""
app_config = apps.get_app_config('commands_sql')
output = sql_create(app_config, no_style(), connections[DEFAULT_DB_ALIAS])
success = False
for statement in output:
if 'CHECK' in statement:
success = True
if not success:
self.fail("'CHECK' not found in output %s" % output)
def test_sql_delete(self):
app_config = apps.get_app_config('commands_sql')
output = sql_delete(app_config, no_style(), connections[DEFAULT_DB_ALIAS], close_connection=False)
drop_tables = [o for o in output if o.startswith('DROP TABLE')]
self.assertEqual(len(drop_tables), 3)
# Lower so that Oracle's upper case tbl names wont break
sql = drop_tables[-1].lower()
six.assertRegex(self, sql, r'^drop table .commands_sql_comment.*')
@ignore_warnings(category=RemovedInDjango20Warning)
def test_sql_indexes(self):
app_config = apps.get_app_config('commands_sql')
output = sql_indexes(app_config, no_style(), connections[DEFAULT_DB_ALIAS])
# Number of indexes is backend-dependent
self.assertTrue(1 <= self.count_ddl(output, 'CREATE INDEX') <= 4)
def test_sql_destroy_indexes(self):
app_config = apps.get_app_config('commands_sql')
output = sql_destroy_indexes(app_config, no_style(), connections[DEFAULT_DB_ALIAS])
# Number of indexes is backend-dependent
self.assertTrue(1 <= self.count_ddl(output, 'DROP INDEX') <= 4)
@ignore_warnings(category=RemovedInDjango20Warning)
def test_sql_all(self):
app_config = apps.get_app_config('commands_sql')
output = sql_all(app_config, no_style(), connections[DEFAULT_DB_ALIAS])
self.assertEqual(self.count_ddl(output, 'CREATE TABLE'), 3)
# Number of indexes is backend-dependent
self.assertTrue(1 <= self.count_ddl(output, 'CREATE INDEX') <= 4)
class TestRouter(object):
def allow_migrate(self, db, model):
return False
@override_settings(DATABASE_ROUTERS=[TestRouter()])
class SQLCommandsRouterTestCase(TestCase):
def test_router_honored(self):
app_config = apps.get_app_config('commands_sql')
for sql_command in (sql_all, sql_create, sql_delete, sql_indexes, sql_destroy_indexes):
if sql_command is sql_delete:
output = sql_command(app_config, no_style(), connections[DEFAULT_DB_ALIAS], close_connection=False)
# "App creates no tables in the database. Nothing to do."
expected_output = 1
else:
output = sql_command(app_config, no_style(), connections[DEFAULT_DB_ALIAS])
expected_output = 0
self.assertEqual(len(output), expected_output,
"%s command is not honoring routers" % sql_command.__name__)

View File

@ -1,33 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
]
operations = [
migrations.CreateModel(
name='Comment',
fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)),
],
options={
},
bases=(models.Model,),
),
migrations.CreateModel(
name='Book',
fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)),
('title', models.CharField(db_index=True, max_length=100)),
('comments', models.ManyToManyField(to='commands_sql_migrations.Comment')),
],
options={
},
bases=(models.Model,),
),
]

View File

@ -1,10 +0,0 @@
from django.db import models
class Comment(models.Model):
pass
class Book(models.Model):
title = models.CharField(max_length=100, db_index=True)
comments = models.ManyToManyField(Comment)

View File

@ -1,39 +0,0 @@
from __future__ import unicode_literals
from django.apps import apps
from django.core.management import CommandError
from django.core.management.color import no_style
from django.core.management.sql import (sql_create, sql_delete, sql_indexes,
sql_destroy_indexes, sql_all)
from django.db import connections, DEFAULT_DB_ALIAS
from django.test import TestCase
class SQLCommandsMigrationsTestCase(TestCase):
"""Tests that apps with migrations can not use sql commands."""
def test_sql_create(self):
app_config = apps.get_app_config('commands_sql_migrations')
with self.assertRaises(CommandError):
sql_create(app_config, no_style(), connections[DEFAULT_DB_ALIAS])
def test_sql_delete(self):
app_config = apps.get_app_config('commands_sql_migrations')
with self.assertRaises(CommandError):
sql_delete(app_config, no_style(), connections[DEFAULT_DB_ALIAS], close_connection=False)
def test_sql_indexes(self):
app_config = apps.get_app_config('commands_sql_migrations')
with self.assertRaises(CommandError):
sql_indexes(app_config, no_style(), connections[DEFAULT_DB_ALIAS])
def test_sql_destroy_indexes(self):
app_config = apps.get_app_config('commands_sql_migrations')
with self.assertRaises(CommandError):
sql_destroy_indexes(app_config, no_style(),
connections[DEFAULT_DB_ALIAS])
def test_sql_all(self):
app_config = apps.get_app_config('commands_sql_migrations')
with self.assertRaises(CommandError):
sql_all(app_config, no_style(), connections[DEFAULT_DB_ALIAS])

View File

@ -404,7 +404,7 @@ class InheritanceSameModelNameTests(TransactionTestCase):
def test_inheritance_with_same_model_name(self): def test_inheritance_with_same_model_name(self):
with self.modify_settings( with self.modify_settings(
INSTALLED_APPS={'append': ['model_inheritance.same_model_name']}): INSTALLED_APPS={'append': ['model_inheritance.same_model_name']}):
call_command('migrate', verbosity=0) call_command('migrate', verbosity=0, run_syncdb=True)
from .same_model_name.models import Copy from .same_model_name.models import Copy
copy = self.title.attached_same_model_name_copy_set.create( copy = self.title.attached_same_model_name_copy_set.create(
content='The Web framework for perfectionists with deadlines.', content='The Web framework for perfectionists with deadlines.',

View File

@ -22,7 +22,7 @@ class ProxyModelInheritanceTests(TransactionTestCase):
def test_table_exists(self): def test_table_exists(self):
with extend_sys_path(os.path.dirname(os.path.abspath(upath(__file__)))): with extend_sys_path(os.path.dirname(os.path.abspath(upath(__file__)))):
with self.modify_settings(INSTALLED_APPS={'append': ['app1', 'app2']}): with self.modify_settings(INSTALLED_APPS={'append': ['app1', 'app2']}):
call_command('migrate', verbosity=0) call_command('migrate', verbosity=0, run_syncdb=True)
from app1.models import ProxyModel from app1.models import ProxyModel
from app2.models import NiceModel from app2.models import NiceModel
self.assertEqual(NiceModel.objects.all().count(), 0) self.assertEqual(NiceModel.objects.all().count(), 0)