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:
parent
9704b0a82e
commit
7e8cf74dc7
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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():
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
--------------------------------------
|
||||
|
||||
|
|
|
@ -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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -582,12 +582,7 @@ spam
|
|||
spammers
|
||||
spatialite
|
||||
Springmeyer
|
||||
sql
|
||||
sqlall
|
||||
sqlclear
|
||||
sqldropindexes
|
||||
sqlflush
|
||||
sqlindexes
|
||||
sqlmigrate
|
||||
sqlsequencereset
|
||||
squashmigrations
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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']
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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()
|
|
@ -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__)
|
|
@ -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,),
|
||||
),
|
||||
]
|
|
@ -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)
|
|
@ -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])
|
|
@ -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.',
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue