mirror of https://github.com/django/django.git
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