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':
subcommand_cls = self.fetch_command(cwords[0])
# special case: add the names of installed apps to options
if cwords[0] in ('dumpdata', 'sql', 'sqlall', 'sqlclear',
'sqlindexes', 'sqlmigrate', 'sqlsequencereset', 'test'):
if cwords[0] in ('dumpdata', 'sqlmigrate', 'sqlsequencereset', 'test'):
try:
app_configs = apps.get_app_configs()
# 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.
run_syncdb = False
target_app_labels_only = True
if options['app_label'] and options['migration_name']:
app_label, migration_name = options['app_label'], options['migration_name']
if app_label not in executor.loader.migrated_apps:
raise CommandError(
"App '%s' does not have migrations (you cannot selectively "
"sync unmigrated apps)" % app_label
"App '%s' does not have migrations." % app_label
)
if migration_name == "zero":
targets = [(app_label, None)]
@ -122,20 +120,19 @@ class Command(BaseCommand):
app_label = options['app_label']
if app_label not in executor.loader.migrated_apps:
raise CommandError(
"App '%s' does not have migrations (you cannot selectively "
"sync unmigrated apps)" % app_label
"App '%s' does not have migrations." % app_label
)
targets = [key for key in executor.loader.graph.leaf_nodes() if key[0] == app_label]
else:
targets = executor.loader.graph.leaf_nodes()
run_syncdb = True
plan = executor.migration_plan(targets)
run_syncdb = options.get('run_syncdb') and executor.loader.unmigrated_apps
# Print some useful info
if self.verbosity >= 1:
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.style.MIGRATE_LABEL(" Synchronize unmigrated apps: ") +
(", ".join(executor.loader.unmigrated_apps))
@ -159,7 +156,7 @@ class Command(BaseCommand):
emit_pre_migrate_signal(self.verbosity, self.interactive, connection.alias)
# Run the syncdb phase.
if run_syncdb and executor.loader.unmigrated_apps:
if run_syncdb:
if self.verbosity >= 1:
self.stdout.write(self.style.MIGRATE_HEADING("Synchronizing apps without migrations:"))
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 django.apps import apps
from django.core.management.base import CommandError
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.
from django.db import models
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
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):
# Emit the pre_migrate signal for every application.
for app_config in apps.get_app_configs():

View File

@ -366,6 +366,7 @@ class BaseDatabaseCreation(object):
verbosity=max(verbosity - 1, 0),
interactive=False,
database=self.connection.alias,
run_syncdb=True,
)
# 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
$ python manage.py migrate
Operations to perform:
Synchronize unmigrated apps: sessions, admin, messages, auth, staticfiles, contenttypes
Apply all migrations: polls
Synchronizing apps without migrations:
Creating tables...
Installing indexes...
Apply all migrations: admin, contenttypes, polls, auth, sessions
Running migrations:
Rendering model states... DONE
...
Applying polls.0001_initial... OK
...
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

View File

@ -74,22 +74,9 @@ The
option forces the use of the standard Python interpreter even when IPython is
installed.
.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 ..." "]"
Prints the SQL statements that would be executed for the "flush" command.
.TP
.BI "sqlindexes [" "app_label ..." "]"
Prints the CREATE INDEX SQL statements for the given model module name(s).
.TP
.BI "sqlsequencereset [" "app_label ..." "]"
Prints the SQL statements for resetting PostgreSQL sequences for the
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.
.TP
.BI migrate
Runs migrations for apps containing migrations, and just creates missing tables
for apps without migrations.
Runs migrations for all apps.
.TP
.BI "test [" "\-\-verbosity" "] [" "\-\-failfast" "] [" "app_label ..." "]"
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
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
--------
@ -1021,16 +974,6 @@ command.
The :djadminopt:`--database` option can be used to specify the database for
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>
--------------------------------------

View File

@ -183,8 +183,7 @@ scenes.
.. attribute:: Field.db_index
If ``True``, :djadmin:`django-admin sqlindexes <sqlindexes>` will output a
``CREATE INDEX`` statement for this field.
If ``True``, a database index will be created for this field.
``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
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
~~~~~~~~~~~~~~~~~~~~~~~
@ -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
: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
~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -2397,9 +2397,9 @@ Default: ``[]``
In order to restore the database state between tests for
``TransactionTestCase``\s and database backends without transactions, Django
will :ref:`serialize the contents of all apps with migrations
<test-case-serialized-rollback>` when it starts the test run so it can then
reload from that copy before tests that need it.
will :ref:`serialize the contents of all apps <test-case-serialized-rollback>`
when it starts the test run so it can then reload from that copy before tests
that need it.
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.

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.
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
index can be found by running the :djadmin:`sqlindexes` admin
command::
index can be found by running the ``sqlindexes`` admin command::
python manage.py sqlindexes sessions

View File

@ -74,9 +74,8 @@ but a few of the key features are:
<test-case-serialized-rollback>`.
* 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:`dependencies documentation <unmigrated-dependencies>` for more.
:ref:`ForeignKey <ref-foreignkey>` or :ref:`ManyToManyField <ref-manytomany>`
to) apps with migrations.
* If you are upgrading from South, see our :ref:`upgrading-from-south`
documentation, and third-party app authors should read the

View File

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

View File

@ -136,12 +136,9 @@ database to make sure they work as expected::
$ python manage.py migrate
Operations to perform:
Synchronize unmigrated apps: sessions, admin, messages, auth, staticfiles, contenttypes
Apply all migrations: books
Synchronizing apps without migrations:
Creating tables...
Installing indexes...
Running migrations:
Rendering model states... DONE
Applying books.0003_auto... OK
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
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

View File

@ -670,12 +670,12 @@ to test the effects of commit and rollback:
.. warning::
``TestCase`` running on a database that does not support rollback (e.g. MySQL with the
MyISAM storage engine), and all instances of ``TransactionTestCase``, will
roll back at the end of the test by deleting all data from the test database
and reloading initial data for apps without migrations.
``TestCase`` running on a database that does not support rollback (e.g. MySQL
with the MyISAM storage engine), and all instances of ``TransactionTestCase``,
will roll back at the end of the test by deleting all data from the test
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
this) you can set ``serialized_rollback = True`` inside the
``TestCase`` body.

View File

@ -319,13 +319,6 @@ class DjangoAdminFullPathDefaultSettings(AdminScriptTestCase):
self.assertNoOutput(err)
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):
"fulldefault: django-admin builtin commands succeed if the environment contains settings"
args = ['check', 'admin_scripts']

View File

@ -78,7 +78,7 @@ class BashCompletionTests(unittest.TestCase):
"Subcommands can be autocompleted"
self._user_input('django-admin sql')
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):
"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):
with self.modify_settings(
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
copy = self.title.attached_same_model_name_copy_set.create(
content='The Web framework for perfectionists with deadlines.',

View File

@ -22,7 +22,7 @@ class ProxyModelInheritanceTests(TransactionTestCase):
def test_table_exists(self):
with extend_sys_path(os.path.dirname(os.path.abspath(upath(__file__)))):
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 app2.models import NiceModel
self.assertEqual(NiceModel.objects.all().count(), 0)